Restful javax.ws.rs. en tomcat


 La creación de un servidor Restful en tomcat suele ser algo sencillo ya que en general se limita a seguir unos pasos bien definidos.
Una vez implementado el weservice es una idea probar si lo que se hizo quedó bien elaborado el sitio en linea : https://reqbin.com/

Como todo en Tomcat debe ser una estructura bien conocida por las personas que usan esta herramienta
tecnológica y que que para el caso se mantiene igual
Una Carpeta donde va residir el despliegue de la aplicación de Restful con sus correspondientes capetas de WEB+INF donde debe ir la estructura de clases bien cualificada de donde va residir el servicio que se pretende exponer.
Es de notar que en este tipo de aplicación no se requiere un archivo del tipo index, porque el despliegue de la aplicación se hace mediante la definición del archivo web.xml que veremos mas adelante.

A partir de la versión de la 3.0 de la librería que vamos a usar para crear el servidor denominada jersey, tampoco se va requiere el archivo web.xml porque todo se hace mediante anotaciones del mismo archivo del servicio.
Sin embargo aquí vamos a mantener la estructura más clásica o sea exponer el servicio mediante web.xml.

Creamos la estructura que va contener el servicio que va estar en este ejercicio en la carpeta api bajo /tomcat/webapps

$ sudo mkdir api
$ sudo mkdir api/WEB-INF
$ sudo mkdir ap/WEB-INF/lib
$ sudo mkdir api/WEB-INF/classes

Solamente por mantener una estructura que me son conocidas voy a crear unas sub-carpetas  pero que pueden ser cambiadas a voluntad, eso teniendo claro que esa estructura que va quedar bajo classes debe ser tenida en cuenta a la hora de desplegar el servicio tanto en la definición del archivo web.xml que definiremos mas adelante con package de las clases que contendrá el servicio.

$ sudo mkdir /api/WEB-INF/classes/com
$ sudo mkdir /api/WEB-INF/classes/com/app
$ sudo mkdir /api/WEB-INF/classes/com/app/ws

la jerarquia  de carpetas queda entonces similar a esto

/tomvat
             webapps
                           api

                              web.xml
                               WEB-INF
                                               lib
                                                   javax.ws.rs.jar

                                                 classes
                                                              com
                                                                  app
                                                                        ws
                                                                             WebService.class

En la carpeta /lib, vamos a colocar los archivos que hacen parte del despliegue web service RestFul en tomcat que en este caso es: jaxrs-ri.jar

Es pertinente anotar que aunque algunos autores indican que es simplemente descargar la versión de interés de esta librería y dejarla allí, en mi caso no pude que me funcionara y coincidí con algunos otros autores que no es simplemente descomprimir el archivo zip que descarga , sino que tuve la necesidad de buscar en las diferentes que librería despliega al desempaquetar y colocarlas directamente en WEB-INF/lib

Una ves descargado el archivo jaxrs-ri.zip del sitio: https://jar-download.com/artifact-search/jaxrs-ri

Descomprimirlo en /lib

$ sudo unzip jaxrs-ri.zip

Copiar los archivos desde la carpeta jaxrs-ri a /tomcat/api/WEB-INF/lib

Nota: se podría colocar en la carpeta general de librerías del tomcat /tomcat/lib, pero a mi me presentó algunos inconvenientes que tenia que ver otros archivos *.jar para el manejo de seguridad y SSL, y se presentaban muchos mensajes de dependencias.

Al dejarlas en /tomcat/WEB-INF/lib
Todos esos problemas conexos desaparecieron., opte por dejar todo lo que tiene que ver con el Restful independiente y me funciono sin problemas al la hora del despliegue de tomcat.

Vamos a copiar los arhivos que indicaba están en jaxrs-ri a /lib

$ sudo cp -rf jaxrs-ri/api/*.jar .
$ sudo cp -rf jaxrs-ri/lib/*.jar .
$ sudo cp -rf jaxrs-ri/ext/*.jar .

Si todo funciono bien se debe ver algo similar a lo siguiente dependiendo desde luego de la versión que se está procesado: algunos archivos pueden ser no necesarios pero prefiero mantener tal cual se despliega para evitar problemas a futuro.

-rw-r--r-- 1 tomcat tomcat 14768 sep 23 14:15 aopalliance-repackaged-2.5.0-b32.jar
-rw-r--r-- 1 tomcat tomcat 185793 sep 23 14:15 hk2-api-2.5.0-b32.jar
-rw-r--r-- 1 tomcat tomcat 187274 sep 23 14:15 hk2-locator-2.5.0-b32.jar
-rw-r--r-- 1 tomcat tomcat 134908 sep 23 14:15 hk2-utils-2.5.0-b32.jar
-rw-r--r-- 1 tomcat tomcat 750581 sep 23 14:15 javassist-3.20.0-GA.jar
-rw-r--r-- 1 tomcat tomcat 26366 sep 23 14:15 javax.annotation-api-1.2.jar
-rw-r--r-- 1 tomcat tomcat 5951 sep 23 14:15 javax.inject-2.5.0-b32.jar
-rw-r--r-- 1 tomcat tomcat 85353 sep 23 14:15 javax.servlet-api-3.0.1.jar
-rw-r--r-- 1 tomcat tomcat 115534 sep 23 14:15 javax.ws.rs-api-2.0.1.jar
-rw-r--r-- 1 tomcat tomcat 100146 sep 23 14:15 jaxb-api-2.2.7.jar
drwxr-xr-x 5 tomcat tomcat 103 ene 19 2017 jaxrs-ri
-rw-r--r-- 1 tomcat tomcat 4964069 sep 23 14:14 jaxrs-ri-2.25.1.zip
-rw-r--r-- 1 tomcat tomcat 168997 sep 23 14:15 jersey-client.jar
-rw-r--r-- 1 tomcat tomcat 715923 sep 23 14:15 jersey-common.jar
-rw-r--r-- 1 tomcat tomcat 66125 sep 23 14:15 jersey-container-servlet-core.jar
-rw-r--r-- 1 tomcat tomcat 18105 sep 23 14:15 jersey-container-servlet.jar
-rw-r--r-- 1 tomcat tomcat 971309 sep 23 14:15 jersey-guava-2.25.1.jar
-rw-r--r-- 1 tomcat tomcat 72757 sep 23 14:15 jersey-media-jaxb.jar
-rw-r--r-- 1 tomcat tomcat 940798 sep 23 14:15 jersey-server.jar
-rw-r--r-- 1 tomcat tomcat 246924 sep 23 14:15 org.osgi.core-4.2.0.jar
-rw-r--r-- 1 tomcat tomcat 20235 sep 23 14:15 osgi-resource-locator-1.0.1.jar
-rw-r--r-- 1 tomcat tomcat 52150 sep 23 14:15 persistence-api-1.0.jar
-rw-r--r-- 1 tomcat tomcat 63777 sep 23 14:15 validation-api-1.1.0.Final.jar

Ahora procedamos a crear el archivo web.xml, es muy importante que quede bien configurado respectando la jerarquía de las classes que se definieron anteriormente y donde va a estar el package del servicio, si esto no queda bien, simplemente cuando se trata de abordar el servicio dice que el recurso no existe, está es con mucho la principal causa de problemas a la hora de usar el servicio Restful


archivo web.xml del ejercicio que estamos desarrollando debe quedar bajo

tomcat/api/WEB-INF

como es normal en tomcat

web.xml
--------------------------------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>

<web-app
xmlns="http://java.sun.com/xml/ns/javaee" version="3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
       <servlet-name>api</servlet-name>
       <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
       <init-param>
              <param-name>jersey.config.server.provider.packages</param-name>
              <param-value>com.app.ws</param-value>
       </init-param>
       <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
     <servlet-name>api</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

</web-app>

En la linea :

<param-name>jersey.config.server.provider.packages</param-name>

Estamos indicando que el despliegue del servicio se va hacer mediante la introspección de los packages que vamos a definir bajo la librería classes , hay otras formas de indicarlos para ejercicios mas exigente o que requieren una definición mas precisa de los servicios como puede se Aplication o Anotaciones pero para este ejercicio relativamente simple es suficiente.
Toda la magia del sistema está en la siguiente linea

<param-value>com.app.ws</param-value>

Donde se indica donde están los programas que van a dar de alta el servicio Restful y que no me canso de enfatizar TIENE que coincidir con el lugar donde están los archivos *.class del servicio que TIENE además que coincidir con el package del dichas class.
Vamos a crear entonces el servicio a desplegar el cual dejaremos

tomvat/webapp/api/WEB-INF/classess/com/api/ws/WebService.java

En este ejercicio vamos ver como usar le recuperación de unos campos de javasBean en Mongo para inyectar el resultado de la consulta en un JsonArray y mostrar como obtener una respuesta Json des-serializada.
Se dejan lo archivo DTO/DAO al final, con objeto de referencia, pero lo que se pretende es mostrar el proceso de des-serializacion de  la estructura JSON generada desde javaBean.

Se hace uso de el modelo DAO/DTO para implementar las consultas  que expone los datos en webservice.

WebService.java
----------------------------------------------------------------------------------------------------------------------

package com.app.ws; //Esta es la estructura de paquete creada que debe coincidier con web.xml

import java.io.* ;
import java.util.* ;
import java.text.* ;
import java.math.* ;

//La libreria gson permite serializar y des-serializar la data tipo JSON
//La libreria Mongodb.client permite el acceso a una base de datos NoSql que se uso en el ejemplo.
//La liberia javax.ws.rs. permite la generación del  Webservice Restful

import com.google.gson.* ;
import com.mongodb.client.* ;

import javax.ws.rs.* ; //Importamos la librería para manejar RESTful
import com.mongodb.client.* ;

@Path("getInfo/{type}") //Especificamos una ruta que se debe usar para invocar este método y un parámetro (tipo)

public class WebService
{
  @GET //Indicamos que este método se ejecutará al recibir una petición por get

  @Produces({"application/json","text/plain", "text/html","text/xml"}) 
  //Indicamos que el tipo de salida es texto plano, XML, HTML o JSON

  // En este caso puede recibir como parámetros "sedes","json" y debe una data Json, si se le
  // le paso cualquier otro parámetro indica servicio no soportado.

  public String mostrarMensaje(@PathParam("type") String tipo,

     //Para el ejercicio recibe como parametros una apikey y apiusuario que valida contra la base
    // Claves en Mongo.
     
     @QueryParam("apiKey")
     @DefaultValue("12345678") String apiKey,
     @QueryParam("apiUsuario")
     @DefaultValue("default") String apiUsuario,
)

// El Método recibe como parámetro el valor de type en la URL a consultar y 
//un parámetro adicional y los campo de acceso
// apiKey y apiUsuario
// para validar credenciales.

{

   Vector<SedesDTO> sedesInfo                 = new Vector<SedesDTO>() ;
   Vector<PersonalesDTO> personalesInfo = new Vector<PersonalesDTO>() ;
   Vector<CitasDTO> citasInfo = new Vector<CitasDTO>() ;
   Vector<ReferenciasDTO>referenciasInfo= new Vector<ReferenciasDTO>() ;

   MongoClient mongoClient = MongoClients.create() ;

   Gson gson = new Gson() ;
   String cadenaJson = new String() ;
   JsonArray jArray = new JsonArray() ;

   String items = new String() ;
   String DB = "zephyr001" ;
   int i = 0 ;

try

{
   ClavesDAO ClavesDAO = new ClavesDAO() ;
   MongoDatabase mgDB = ConexionMongo.Conectar( DB , mongoClient) ;
   ClavesDTO claves = new ClavesDTO() ;

//Autenticacion con apiKey y apiUsuario

  claves.setClaveClave(apiKey) ;
  claves.setClaveUsuario(apiUsuario) ;
  claves.setClaveEntorno(DB) ;

  boolean claveExiste = ClavesDAO.ClaveUsuarioExiste(mgDB, claves) ;
              if ( ! claveExiste )
                {
                  return "{\"status\":\"false\",\"mensaje\":\"error, credenciales invalidas\",\"items\":"+ "\"0\"" + ",\"data\":" + "[{}]"+\
"}";
    }
            }
        catch (Exception e)
            {
                return "{\"status\":\"false\",\"mensaje\":\"error,\""+ e.toString() + "\",\"items\":"+ "\"0\"" + ",\"data\":" + "[{}]"+"}";
            }

if (tipo.equalsIgnoreCase("sedes"))
          {
            //---------------- SEDES ------------------------------                                                                         

            SedesDTO sedes = new SedesDTO()                              ;

            sedes.setSedeBuscar("")                                                  ;
            sedes.setSedeSort("NOMBRE")                                     ;
            sedes.setSedeEntorno(DB)                                              ;

         try
            {
      SedesDAO SedesDAO = new SedesDAO()                  ;
              MongoDatabase mgDB = ConexionMongo.Conectar( DB , mongoClient)         ;

              sedesInfo = SedesDAO.SedesID(mgDB,
                                           sedes
                                          )                                                           ;
              mongoClient.close()                                                       ;
    }
        catch (Exception e)
            {
             return "{\"status\":\"false\",\"mensaje\":\"error,\""+ e.toString() + "\",\"items\":"+ "\"0\"" + ",\"data\":" + "[{}]"+"}";
            }

            for ( i = 0 ; i < sedesInfo.size() ; i++)
                {
                    sedes = new SedesDTO()                                           ;

            sedes.setSedeId    (sedesInfo.elementAt(i).getSedeId().toString())                 ;
                    sedes.setSedeNombre(sedesInfo.elementAt(i).getSedeNombre().toString()) ;
                   
                    cadenaJson = gson.toJson(sedes)                                                                     ;

                    jArray.add(cadenaJson)                                           ;
                }
            items = "" + i                                                           ;
            return "{\"status\":\"ok\",\"mensaje\":\"sedes\",\"items\":\""+ items+"\",\"data\":"+jArray+"}";
        }

      catch (Exception e)
         {
            return "{\"status\":\"false\",\"mensaje\":\"error,\""+ e.toString() + "\",\"items\":"+ "\"0\"" + ",\"data\":" + "[{}]"+"}";
         }
else if(tipo.equalsIgnoreCase("json"))
{
   return "{\"root\":{\"value\":\"Este proceso RESTful no esta disponible\"}}";
}
else
{
return "{\"status\":\"false\",\"mensaje\":\"servicio no soportado\",\"items\":" + "\"0\"" + ",\"data\":" + "[{}]"+"}";
}
}

}

Para invocar el webservice se usa la URL

URL Sedes con respectivos parámetros. curl https://127.0.0.1:8080/api/getInfo/sedes?apiKey=admin&apiUsuario=admin 

ingresando la URL directamente en la barra del navegador  o en la aplicación en linea https://reqbin.com/

La aplicaron usa los siguientes archivos DTO/DAO. Solo son de importancia como referencia para ilustrar la consulta en Mongo mediante JabaBeans y su posterior conversión a JSON des-serializado.


SedesDTO.java
------------------------------------------------------------------------------------------------------------
package com.app.ws; //Esta es la estructura de paquete creada                                                                               
public class SedesDTO
{
  private String sedeId;
  private String sedeNombre;

  public String getSedeId()                       {
     return sedeId                                    ;  }
  public void setSedeId(String sedeId)     {
    this.sedeId = sedeId                          ;  }

  public String getSedeNombre()             {
     return sedeNombre                          ;  }
  public void setSedeNombre(String sedeNombre)     {
    this.sedeNombre = sedeNombre      ;  }
}

ClavesDTO.java
------------------------------------------------------------------------------------------------------------
package com.app.ws; //Esta es la estructura de paquete creada     

public class ClavesDTO
{
  private String claveClave                   ;
  private String claveUsuario                ;

    public String getClaveClave()              {
        return claveClave                          ;   }
    public void setClaveClave(String claveClave)  {
        this.claveClave = claveClave        ;   }
    public String getClaveUsuario()            {
        return claveUsuario                       ;   }
    public void setClaveUsuario(String claveUsuario) {
        this.claveUsuario = claveUsuario  ;    }

}
                   
//Recupera el ID y el nombre de las sedes de una base de datos MongoDB
SedesDAO.java
--------------------------------------------------------------------------------------------------------------
package com.app.ws; //Esta es la estructura de paquete creada                                                                               
import java.io.*                                            ;
import java.util.*                                          ;
import java.text.*                                          ;

import java.util.Arrays.*                               ;
import com.mongodb.client.*                       ;
import com.mongodb.client.result.*             ;
import com.mongodb.client.model.Field      ;
import com.mongodb.client.model.Filters.* ;

import static com.mongodb.client.model.Filters.eq;
import com.mongodb.client.FindIterable;

import static com.mongodb.client.model.Aggregates.*;
import static com.mongodb.client.model.Accumulators.*;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Projections.*;
import static com.mongodb.client.model.Sorts.descending;
import static com.mongodb.client.model.Sorts.ascending;

import org.bson.*;
import org.bson.conversions.Bson    ;//Se usa para crear el array de aggregation Array.asArray().                                     
import java.util.regex.Pattern            ;

public class SedesDAO 
{
   public Vector<SedesDTO> SedesID(MongoDatabase mgDB,
                                 SedesDTO sedes
                                )
   {
     SedesDTO sedesC               = new SedesDTO()                   ;
     Vector< SedesDTO > gDato= new Vector< SedesDTO >()  ;

     String[] busquedas    = null                                                   ;
     String buscar             = sedes.getSedeBuscar().trim()           ;
     String sort                 = sedes.getSedeSort()                          ;
     int    limite                = 50000                                                ;
     int    offSet                = 0                                                        ;
     int    i                        = 0                                                        ;
     int    l                        = 1                                                        ;                     

     Document data          = new Document()                              ;
     Document mgFields  = new Document()                             ;
     Document mgQuery  = new Document()                             ;
     Document mgSort     = new Document()                             ;

     Pattern pattern           = Pattern.compile(buscar)                  ;
     String  mRegex         = pattern.toString()                             ;

     List<Bson> myMatchAY= new ArrayList<Bson>()           ;                         
     List<Bson> myMatchAO= new ArrayList<Bson>()           ;                   
     List<Bson> myList          = new ArrayList<Bson>()           ;                               

     List<Bson> myMatchAF= new ArrayList<Bson>()            ;

     Bson myAddFieldsDate                                                       ;             
     Bson myAddFieldsF                                                            ;                      
     Bson myAddFieldsIdLen                                                    ;                                     
     Bson myAddFieldsInLen                                                    ;
     Bson myAddFieldsST                                                         ;
     Bson myMatchY                                                                 ;
     Bson myMatchO                                                                 ;
     Bson myMatchF                                                                  ;
     Bson mySort                                                                        ;
     Bson myProject                                                                   ;
     Bson mySkip                                                                       ;
     Bson myLimit                                                                      ;


     if ( sedes.getSedeLimit() != null )
       {
         limite = Sos.Val(sedes.getSedeLimit())                               ;
       }

     if ( sedes.getSedeOffSet() != null )
       {
         offSet = Sos.Val(sedes.getSedeOffSet())                              ;
       }

     if ( buscar != null)
       {
         busquedas = buscar.split(" ")                                     //Separa las diferentes palabras  busqueda              
         l  = busquedas.length                                                            ;
       }

     MongoCollection<Document> collection = mgDB.getCollection("SEDES")    ;

     myMatchAY.add(eq("entorno", sedes.getSedeEntorno()))                                  ;

     myAddFieldsF = addFields(new Field<String>("document", "$$ROOT"))        ;

     myProject    = excludeId()                                                       ;
     mySort         = null                                                                   ;
     mySkip        = skip(offSet)                                                      ;
     myLimit      = limit(limite)                                                      ;

     myList.add(myAddFieldsF)                                                    ;

     //Agregue un nuevo campo cuyo nombre es sedeIdL                                      
     myAddFieldsInLen = addFields(new Field<Bson>("sedeInLen",
                                          new Document("$strLenCP" , "$sedeId"))
                                        )                                                             ;
     myList.add(myAddFieldsInLen)                                             ;//Agrega el campo virtual 

    try
        {

         if (myMatchAO.size() != 0)
             {
               myMatchO = match(or(myMatchAO)) ;
               myList.add(myMatchO)                                                   ;
             }
           myMatchY = match(and(myMatchAY))                              ;

           myList.add(myMatchY)                                                       ;

           myList.add(mySort)                                                              ;
           myList.add(mySkip)                                                             ;
           myList.add(myLimit)                                                            ;

           AggregateIterable<Document> result   = collection.aggregate(myList)    ;
           MongoCursor<Document>       iterator = result.iterator()     ;

          while (iterator.hasNext())
            {
              sedesC = new SedesDTO()                                                 ;
              data   = iterator.next()                                                          ;

              sedesC.setSedeId(data.getString("sedeId"))                        ;
              sedesC.setSedeNombre(data.getString("sedeNombre"))     ;
     
              gDato.add(sedesC)                                                               ;

            }

          iterator.close()                                                                           ;
          return(gDato)                                                                            ;
    }
  catch(Exception e)
    {
      System.out.println("Error sedes Consultar : " + e.toString())      ;
      e.printStackTrace(System.out)                                                     ;

      gDato.add(sedesC)                                                                       ;
      return (gDato)                                                                               ;
    }


}

ClavesDAO.java
----------------------------------------------------------------------------------
package com.app.ws; //Esta es la estructura de paquete creada                                                                               
import java.io.*                                              ;
import java.util.*                                           ;
import java.text.*                                          ;

import com.mongodb.client.*                       ;
import com.mongodb.client.result.*             ;
import com.mongodb.client.model.Filters.* ;
import org.bson.*;                                         ;
import java.util.regex.Pattern                        ;

public class ClavesDAO 
{
  public boolean  ClaveUsuarioExiste(MongoDatabase mgDB,
                                      ClavesDTO claves
                                     )
   {

     ClavesDTO clavesC                = new ClavesDTO()                     ;
     Vector< ClavesDTO > gDato  = new Vector< ClavesDTO >()    ;
     boolean existe                         = false                                            ;

     Document data           = new Document()                                     ;
     Document mgFields  = new Document()                                      ;
     Document mgQuery  = new Document()                                      ;

     ArrayList<Document> myWhereO = new ArrayList<Document>()                        ;
     ArrayList<Document> myWhereY = new ArrayList<Document>()                        ;

     MongoCollection<Document> collection = mgDB.getCollection("CLAVES")      ;

     myWhereY.add(new Document("claveClave"     ,Codec.Enc(claves.getClaveClave())))    ;
     myWhereY.add(new Document("claveUsuario" ,Codec.Enc(claves.getClaveUsuario()))) ;
     myWhereY.add(new Document("entorno"          ,claves.getClaveEntorno()))                     ;

     try
        {
          mgQuery.append("$and",myWhereY)                                ;
          mgFields.append("_id",0)                                                   ;

          MongoCursor<Document> cursor = collection.find(mgQuery).projection(mgFields).cursor() ;

          while (cursor.hasNext())
            {
              data   = cursor.next()                                                        ;
              existe = true                                                                     ;

            }
          cursor.close()                                                                       ;
          return(existe)                                                                       ;
    }
  catch(Exception e)
    {
      System.out.println("Error Claves Consultar : " + e.toString()) ;
      e.printStackTrace(System.out)                                                  ;

      return (existe)                                                                            ;
    }
 }

}





Comentarios

Entradas populares