En publicaciones pasadas, he escrito sobre WebServices en diferentes tecnologías. De la misma manera, he realizado lo mismo para definir clientes que consumen dichos servicios. Con esta nueva publicación, añado una nueva tecnología para la definición de WebServices en Java con Jersey.
Las publicaciones pasadas de WebServices son las siguientes:
En la presente entrada, me centraré en cómo definir servicios sencillos con Jersey, clientes que consumen los servicios y la creación de un proyecto de tester con SOAP UI.
Creación del proyecto
La generación de un proyecto para trabajar con Jersey es muy sencillo con Maven; simplemente, tenemos que utilizar los arquetipos predefinidos para Jersey. Los principales arquetipos son dos: proyecto básico, cuyo identificador es jersey-quickstart-grizzly2; y, para un proyecto web, el identificador del arquetipo es jersey-quickstart-webapp.
La generación del arquetipo para la generación de un proyecto Maven con la configuración básica con Jersey es el siguiente:
mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false -DgroupId=mi.paquete -DartifactId=simple-service -Dpackage=mi.paquete -DarchetypeVersion=2.6
La generación del arquetipo para la generación de un proyecto Maven con la configuración para un proyecto web con Jersey es el siguiente:
mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-webapp -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false -DgroupId=mi.paquete -DartifactId=simple-service-webapp -Dpackage=mi.paquete -DarchetypeVersion=2.6
Configuración Maven de Jersey
En esta entrada, me centraré en un proyecto web que soporte Servlet 3.X, y, como servidor embebido, utilizaré Jetty. Así, los elementos importantes del fichero pom son los siguientes:
[...]
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty.version}</version>
</plugin>
[...]
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jettison</artifactId>
<version>${jetty.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
</dependencies>
[...]
Configuración del servidor, fichero web.xml
Para trabajar con Jersey, es necesario definir el Servlet de Jersey para interpretar las peticiones REST. La definición del servlet contiene lo siguiente: definición del ServletContainer, definición de los parámetros del servlet y el mapeo del Servlet. El snippet con el código es el siguiente:
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>es.directoandroid</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.wadl.disableWadl</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/webapi/*</url-pattern>
</servlet-mapping>
Un aspecto a destacar es la definición del parámetro jersey.config.server.wadl.disableWadl para permitir la obtención del fichero wadl con la definición de los servicios.
Servicios
Una vez llegado a este punto, solo nos falta la definición de los servicios. Este proceso es sencillo, simplemente, se define una clase con las anotaciones de Jersey. En el ejemplo, defino dos clases: la primera, ServicioEjemploBasico, define unos servicios básicos; y, ServicioEjemploCabeceras, define unos servicios para la utilización de cabeceras y parámetros. Dichas clases tiene la anotación @Path para definir el path de primer nivel de acceso a los servicios; el de segundo nivel, se encuentra en cada método que define cada servicio.
Servicio con JSON
La definición del servicio que sirva una estructura JSON consiste en lo siguiente: utilización de la anotación @Get para definir que es una petición HTTP GET; utilización de la anotación @Produces para definir el tipo de respuesta, en nuestro caso, MediaType.APPLICATION_JSON; y, por último, la utilización de la anotación @Path para definir el path de segundo nivel.
En nuestro ejemplo, el método del servicio retorna un String con una estructura JSON. El snippet del código es el siguiente:
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/ejem1")
public String echoHolaMundo(){
return "{'mensaje':'Hola Mundo desde Jersey!!!'}";
}
Servicio con XML
La definición del servicio que sirva una estructura XML consiste en lo siguiente: utilización de la anotación @Get para definir que es una petición HTTP GET; utilización de la anotación @Produces para definir el tipo de respuesta, en nuestro caso, MediaType.APPLICATION_XML; y, por último, la utilización de la anotación @Path para definir el path de segundo nivel.
En nuestro ejemplo, el método del servicio retorna un String con una estructura XML. El snippet del código es el siguiente:
@GET
@Produces(MediaType.APPLICATION_XML)
@Path("/ejem2")
public String echoHolaMundoXml(){
return "<nodo><mensaje>Hola Mundo desde Jersey!!!</mensaje></nodo>";
}
Servicio Json con Bean
La definición del servicio que sirva una estructura JSON utilizando un bean consiste en lo siguiente: utilización de la anotación @Get para definir que es una petición HTTP GET; utilización de la anotación @Produces para definir el tipo de respuesta, en nuestro caso, MediaType.APPLICATION_XML; y, por último, la utilización de la anotación @Path para definir el path de segundo nivel.
En nuestro ejemplo, el método del servicio retorna un bean con nombre MyBean con los datos de la estructura JSON. El snippet del código es el siguiente:
@GET
@Path("/ejem4")
@Produces(MediaType.APPLICATION_JSON)
public MyBean getJson() {
return new MyBean("Manolito", 69);
}
Servicio texto
La definición del servicio que sirva una estructura de texto consiste en lo siguiente: utilización de la anotación @Get para definir que es una petición HTTP GET; utilización de la anotación @Produces para definir el tipo de respuesta, en nuestro caso, MediaType.TEXT_PLAIN; y, por último, la utilización de la anotación @Path para definir el path de segundo nivel.
En nuestro ejemplo, el método del servicio retorna un String. El snippet del código es el siguiente:
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("/ejem3")
public String echo(){
return "Haciendo echo...";
}
Servicio con parámetro
La definición del servicio al cual se le pasa un parámetro y, para el ejemplo, que sirva una estructura de texto consiste en lo siguiente: utilización de la anotación @Get para definir que es una petición HTTP GET; utilización de la anotación @Produces para definir el tipo de respuesta, en nuestro caso, MediaType.TEXT_PLAIN; la utilización de la anotación @Path para definir el path de segundo nivel; y, para definir el parámetro, se utiliza la anotación @QueryParam con el identificador del parámetro que se pasa desde el cliente.
En nuestro ejemplo, el método del servicio que retorna un String y se le pasa por parámetro queda representado por el snippet siguiente:
@GET
@Path("/ejem2")
@Produces(MediaType.TEXT_PLAIN)
public String get(@QueryParam("parametro") String param) {
return "parametro pasado=" + param;
}
Servicio con parámetro, valor por defecto y cabecera
En este apartado realizaremos la misma operación que en el anterior; la diferencia, radica en la forma en la cual se asigna el parámetro. En este caso, la asignación del parámetro se realizará por inyección, además, se realizará, de la misma forma, la inyección de un parámetro de la cabecera.
La inyección se realiza sobre atributos de la clase con las siguientes anotaciones: @QueryParam, para el parámetro; @HeaderParam, para el parámetro de la cabecera; y, @DefaultValue, para la asignación del valor por defecto del parámetro en caso de que no exista en la petición. El snippet del ejemplo es el siguiente:
@Path("/header")
public class ServicioEjemploCabeceras {
@DefaultValue("pordefecto")
@QueryParam("param1")
private String param1;
@HeaderParam("header")
private String header;
El método del servicio queda de la siguiente forma:
@GET
@Path("/ejem1")
@Produces(MediaType.TEXT_PLAIN)
public String get() {
return "parametro="+param1+" - [header="+header+"]";
}
Cliente
Los clientes son todas aquellas entidades que realizan peticiones a los servicios. Los clientes que definiremos son los clientes que conectan con los servicios definidos anteriormente. La definición de los clientes tiene prácticamente la misma secuencia: primero, creación del cliente; segundo, definición del path del servicio; tercero, definición de la respuesta (JSON, XML, …); cuarto, definición de los parámetro o cabeceras si procede; y, por último, obtención de la respuesta.
Cliente con JSON
El snippet con la definición del cliente a un servicio JSON es el siguiente:
private void servicioJson() {
System.out.println("\n-*-*- Cliente a servicio con respuesta JSON -*-*-");
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(HTTP_LOCALHOST_8080_WEBAPI);
WebTarget jsonWebTarget = webTarget.path("/servicio1/ejem1");
Builder request = jsonWebTarget.request(MediaType.APPLICATION_JSON);
Response response = request.get();
System.out.println("status=" + response.getStatus());
System.out.println("readEntity=" + response.readEntity(String.class));
}
Cliente con XML
El snippet con la definición del cliente a un servicio XML es el siguiente:
private void servicioXml() {
System.out.println("\n-*-*- Cliente a servicio con respuesta XML -*-*-");
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(HTTP_LOCALHOST_8080_WEBAPI);
WebTarget jsonWebTarget = webTarget.path("/servicio1/ejem2");
Builder request = jsonWebTarget.request(MediaType.APPLICATION_XML);
Response response = request.get();
System.out.println("status=" + response.getStatus());
System.out.println("readEntity=" + response.readEntity(String.class));
}
Cliente Json con Bean
El snippet con la definición del cliente a un servicio JSON con bean es el siguiente:
private void servicioJsonConBean() {
System.out.println("\n-*-*- Cliente a servicio con respuesta JSON -*-*-");
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(HTTP_LOCALHOST_8080_WEBAPI);
WebTarget jsonWebTarget = webTarget.path("/servicio1/ejem4");
Builder request = jsonWebTarget.request(MediaType.APPLICATION_JSON);
Response response = request.get();
System.out.println("status=" + response.getStatus());
System.out.println("readEntity=" + response.readEntity(String.class));
}
Cliente texto
El snippet con la definición del cliente a un servicio de texto es el siguiente:
private void servicioText() {
System.out.println("\n-*-*- Cliente a servicio con respuesta Text -*-*-");
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(HTTP_LOCALHOST_8080_WEBAPI);
WebTarget jsonWebTarget = webTarget.path("/servicio1/ejem3");
Builder request = jsonWebTarget.request(MediaType.TEXT_PLAIN);
Response response = request.get();
System.out.println("status=" + response.getStatus());
System.out.println("readEntity=" + response.readEntity(String.class));
}
Cliente con parámetro
El snippet con la definición del cliente a un servicio de texto definiendo un parámetro es el siguiente:
private void servicioConParametro() {
System.out.println("\n-*-*- Cliente a servicio con parametros y campos de cabecera-*-*-");
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(HTTP_LOCALHOST_8080_WEBAPI);
WebTarget jsonWebTarget = webTarget.path("/header/ejem2");
WebTarget queryParam = jsonWebTarget.queryParam("parametro", 123456789);
Builder request = queryParam.request(MediaType.TEXT_PLAIN);
Response response = request.get();
System.out.println("status=" + response.getStatus());
System.out.println("readEntity=" + response.readEntity(String.class));
}
Cliente con parámetro y cabecera
El snippet con la definición del cliente a un servicio de texto con un parámetro y una cabecera es el siguiente:
private void servicioConParametrosYCabecera() {
System.out.println("\n-*-*- Cliente a servicio con parametros y campos de cabecera-*-*-");
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(HTTP_LOCALHOST_8080_WEBAPI);
WebTarget jsonWebTarget = webTarget.path("/header/ejem1");
WebTarget queryParam = jsonWebTarget.queryParam("param1", 1);
Builder request = queryParam.request(MediaType.TEXT_PLAIN);
request.header("header", "ValorCabecera");
Response response = request.get();
System.out.println("status=" + response.getStatus());
System.out.println("readEntity=" + response.readEntity(String.class));
}
Pruebas con SOAP UI
Para la realización de las pruebas de los servicios podemos utilizar la herramienta SOAP UI. Al ser un servicio REST, primeramente, debemos de obtener el WADL (Web Application Description Language); para ello, en un navegador escribiremos lo siguiente: http://localhost:8080/webapi/application.wadl ; la respuesta, será una estructura xml con la definición de los servicios.
Una vez arrancado SOAP UI, crearemos un proyecto REST al que le referenciaremos la URL con el contenido del wadl; automáticamente, se creará un proyecto con los clientes y las peticiones para probar cada servicio.
Si se desea el código ejemplo se puede acceder aquí.
En la siguiente entrada, Servicios REST con Jersey. Peticiones asíncronos, realizaré la definición de servicios asíncronos, así como, la definición de sus clientes.