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.
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-riDescomprimirlo 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>j
ersey.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
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 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
Publicar un comentario