sábado, 16 de febrero de 2013

Cliente javascript y java de servicio web REST con RESTEasy

RESTEasy
Una vez que hemos desarrollado un servicio web REST tendremos que obtener un cliente para consumirlo ya sea con javascript, Java u otro lenguaje. En cualquier caso los clientes son mucho más sencillos de obtener en RESTeasy que en los servicios web basados en mensajes SOAP y XML haciendo uso de otras librerías a partir de la definición contenida en el WSDL como vamos a ver. Además, de poder ser consumidos desde un navegador a través de javascript.

Para el cliente Java deberemos tener la interfaz de ese web service con los métodos ofrecidos en el servicio, con las anotaciones adecuadas y que deberemos implementar en una clase. La interfaz del ejemplo Ejemplo sencillo de web service con RESTEasy sería:

package es.com.blogspot.elblogdepicodev.resteasy;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/helloworld")
public interface HelloWorldResource {
@GET
@Path("/saluda")
public String getSaluda();
@GET
@Path("/saluda/{nombre}")
public String getSaludaA(@PathParam("nombre") String nombre);
}
Y la implementación:

package es.com.blogspot.elblogdepicodev.resteasy;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
public class HelloWorldResourceImpl implements HelloWorldResource {
@Override
public String getSaluda() {
return "¡Hola mundo!";
}
@Override
public String getSaludaA(String nombre) {
return MessageFormat.format("¡Hola {0}!", nombre);
}
}
Una vez tengamos la interfaz del web service REST y su implementación podemos hacer un programa java que lo use como cliente:

Apartir de esa interfaz podemos obtener el cliente y un programa que lo invoca con el siguiente código. La parte importante está en el uso de las clases RegisterBuiltin y ProxyFactory:

package es.com.blogspot.elblogdepicodev.resteasy;
import org.jboss.resteasy.client.ProxyFactory;
import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
public class HelloWorldResourceClient implements HelloWorldResource {
private HelloWorldResource client;
public HelloWorldResourceClient() {
// Obtener el cliente a partir de la interfaz y de donde está localizado
client = ProxyFactory.create(HelloWorldResource.class, "http://localhost:8080/helloworld-resteasy/rest");
}
@Override
public String getSaluda() {
return client.getSaluda();
}
@Override
public String getSaludaA(String nombre) {
return client.getSaludaA(nombre);
}
public static void main(String[] args) {
// Inicializacion a realizar una vez por máquina virtual
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
HelloWorldResourceClient client = new HelloWorldResourceClient();
System.out.println(client.getSaluda());
System.out.println(client.getSaludaA("picodotdev"));
}
}
Y eso es lo mínimo necesario para consumir los web services desde java ¿te pareció sencillo?, pues para consumirlos desde un navegador con javascript es aún más sencillo, veamos como. RESTEasy proporciona un servlet que genera un archivo js con los clientes javascript de nuestros servicios web REST, lo único que deberemos hacer es incluir ese servlet en el web.xml de nuestra aplicación web y usarlo en la página html en la que queramos hacer uso de él.

<?xml version="1.0" encoding="iso-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>HelloWorldRESTEasy</display-name>
<description>Aplicación Hola Mundo con RESTEasy</description>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/rest</param-value>
</context-param>
<servlet>
<servlet-name>resteasy</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>es.com.blogspot.elblogdepicodev.resteasy.Application</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>resteasy-jsapi</servlet-name>
<servlet-class>org.jboss.resteasy.jsapi.JSAPIServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>resteasy</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>resteasy-jsapi</servlet-name>
<url-pattern>/rest-jsapi</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
view raw web.xml hosted with ❤ by GitHub
El servlet que generará el javscript, resteasy-jsapi, debe ser cargado después del servlet de RESTeasy por eso los valores de los load-on-startup en las etiquetas servlet, si no lo hiciéramos así obtendríamos la siguiente excepción de error:

java.lang.NullPointerException
org.jboss.resteasy.jsapi.ServiceRegistry.scanRegistry(ServiceRegistry.java:69)
org.jboss.resteasy.jsapi.ServiceRegistry.<init>(ServiceRegistry.java:63)
org.jboss.resteasy.jsapi.JSAPIServlet.scanResources(JSAPIServlet.java:75)
org.jboss.resteasy.jsapi.JSAPIServlet.init(JSAPIServlet.java:37)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1002)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
org.apache.tomcat.util.net.AprEndpoint$SocketWithOptionsProcessor.run(AprEndpoint.java:1771)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
java.lang.Thread.run(Thread.java:679)
view raw StackTrace hosted with ❤ by GitHub
En el archivo helloworld-resteasy.jsp está como es la llamada al web service desde el navegador, básicamente consiste en generar un html que incluya la etiqueta script haciendo referencia al javascript a obtener en el navegador, contendrá los clientes javascript de todos nuestros web services REST. Aunque en el uso de los clientes las peticiones que se producen a través de la red estas operaciones son totalmente transparentes para nosotros, en cada correspondiente cliente javascript tendremos disponibles unos métodos que se encargarán de realizar y de la forma adecuada las peticiones teniendo entre otras cosas en cuenta como enviar los parámetros de los métodos. Una cosa a tener en cuenta es que en la interfaz del web service REST no debemos usar sobrecarga, mismo nombre de método con diferentes parámetros, ya que de otra manera el cliente javascript no sabe distinguir a que método del Servicio web llamar.

<html>
<head>
<title>Ejemplo sencillo de web service con RESTEasy</title>
<script type="text/javascript" src="http://localhost:8080/helloworld-resteasy/rest-jsapi"></script>
</head>
<body>
<script type="text/javascript">
alert(HelloWorldResource.getSaluda());
alert(HelloWorldResource.getSaludaA({nombre:'picodotdev'}));
</script>
</body>
</html>

Recursos que pide el cliente Javascript
El último paso que tendremos que aprender es como devolver otros tipos de datos más complejos como json, xml, html u otros.

Referencia:
REST
Ejemplo sencillo de servicio web con RESTEasy
Código fuente completo del ejemplo Hola Mundo con RESTEasy