Usar SerfJ es la forma más fácil de desarrollar aplicaciones web Java basadas en REST. Te ayuda a desarrollar tus aplicaciones sobre una arquitectura MVC elegante, dando más importancia a las convenciones que a la configuración, por ejemplo, no tendrás que tener ficheros de configuración o escribir anotaciones para especificar que vista es servida por un método del controlador. Por otro lado, SerfJ es una librería muy flexible, así que si te quieres saltar las convenciones, puedes configurar el comportamiento de tus aplicaciones como quieras.
El framework intenta cumplir la especificación JSR 311, pero no sigue todos los puntos al pie de la letra porque el propósito es que sea una librería muy intuitiva, y algunos aspectos de la especificación quedan fuera del ámbito de SerfJ.
SerfJ es opensource y está bajo la Licencia Apache, Versión 2.0.
Última versión 0.4.1 fue publicada el 14 de Septiembre de 2012.
Puedes seguir desarrollos del proyecto en nuestro blog: http://serfj.wordpress.com. No es un blog con demasiada actividad, pero ahí puedes seguir que habrá en futuras versiones, cuándo se lanzará la siguiente versión, etc.
Si quieres saber cómo usar SerfJ, tienes que leer esta pequeña guía. Lo primero es configurar el fichero web.xml para mapear las URLs REST contra el servlet net.sf.serfj.RestServlet. En el siguiente ejemplo mapearemos dos recursos banks (bancos) y accounts (cuentas).
RestServlet net.sf.serfj.RestServlet 5 RestServlet /banks/* RestServlet /accounts/*
El siguiente paso es añadir al classpath el fichero de configuración /config/serfj.properties, con al menos una línea:
# Paquete principal en el que buscar las clases (controladores, serializadores) main.package=net.sf.serfj.test
Ahora nuestra aplicación es capaz de atender peticiones como estas:
Date cuenta que some-action es cualquier método que los desarrolladores quieran escribir en los controladores. Pero por ahora nadie está atendiendo las peticiones, así que tenemos que escribir algunas clases para que todo funcione como esperamos. Necesitamos un controlador que atienda las peticiones sobre /banks/* (bancos), y otro que atienda las peticiones sobre /accounts/* (cuentas).
Controlador para el recurso Banco:
public class Bank extends RestController { @GET public void index() { // Por defecto, esta acción redirige a index.jsp (o index.html, o index.htm) } @GET public void show() { // Obtiene el ID de la URL /banks/1 String id = this.getId("bank"); // Obtiene el ID de la URL /banks/1 String theSameId = this.getId(); // Por defecto, esta acción redirige a show.jsp (o show.html, o show.htm) } @GET public void newResource() { // Por defecto, esta acción redirige a new.jsp (o new.html, o new.htm) } @GET public void edit() { // Por defecto, esta acción redirige a edit.jsp (o edit.html, o edit.htm) } @POST public void create() { // Por defecto, esta acción redirige a create.jsp (o create.html, o create.htm) } @PUT public void update() { // Obtiene el ID del banco String id = this.getId("bank"); Bank bank = // Código que obtiene el objeto banco // Obtiene un nuevo nombre para el banco String name = this.getStringParam("name"); // Actualiza el banco // ... Código que actualiza la información del banco // Por defecto, esta acción redirige a update.jsp (o update.html, o update.htm) } @DELETE public void delete() { // Por defecto, esta acción redirige a delete.jsp (o delete.html, o delete.htm) } @GET public void someAction() { // Por defecto, esta acción redirige a someAction.jsp (o someAction.html, o someAction.htm) } }
Controlador para el recurso Account:
public class Account extends RestController { @GET public void index() { // Por defecto, esta acción redirige a index.jsp (o index.html, o index.htm) } @GET public void show() { // Obtiene el ID de la cuenta de la URL /banks/1/accounts/2 String accountId = this.getId("account"); // Otra forma de obtener el id de la cuenta de la URL /banks/1/accounts/2 String theSameAccountId = this.getId(); // Identificador del banco String bankId = this.getId("bank"); // Obtiene la cuenta Account account = // Código que obtiene la cuenta 2 del banco 1 // Guarda la cuenta en la 'request' para que la página JSP pueda usarla this.addObject2Request("account", account); // Por defecto, esta acción redirige a show.jsp (o show.html, o show.htm) } @GET public void newResource() { // Por defecto, esta acción redirige a new.jsp (o new.html, o new.htm) } @GET public void edit() { // Por defecto, esta acción redirige a edit.jsp (o edit.html, o edit.htm) } @POST public void create() { // Por defecto, esta acción redirige a create.jsp (o create.html, o create.htm) // ¡Pero yo quiero redirigir a otra página!... es fácil this.renderPage("mypage.jsp"); } @PUT public void update() { // Por defecto, esta acción redirige a update.jsp (o update.html, o update.htm) // ¡Pero quiero redirigir a otra página... de otro controlador!... también es fácil. this.renderPage("bank", "another_page.jsp"); } @DELETE public void delete() { // Por defecto, esta acción redirige a delete.jsp (o delete.html, o delete.htm) // Bien, si pasa algo, quiero redirigir a una página JSO en concreto (mypage.jsp) if (somethingHappens) { this.renderPage("mypage.jsp"); } else { // Default page this.renderPage(); } } /** * Si este método se llama con /accounts/1/accountBalance.xml, entonces el objeto Balance (saldo) será * serializado como XML, mientras que si es llamado con /accounts/1/accountBalance.json, el objeto será * serializado como JSON. */ @POST public Balance accountBalance() { // Obtiene el ID de la cuenta String id = this.getId("account"); // Calcula el saldo de la cuenta BalanceManager manager = new BalanceManager(); Balance balance = manager.getBalance(id); this.serialize(balance); } }
Si te estás preguntando dónde tienes que poner esas clases. Teniendo en cuenta que especificamos el paquete principal, main.package (recuerda serfj.properties), a net.sf.serfj.test, tienes tres posibilidades diferentes, y las puedes combinar como quieras para cada controlador.
Si no quieres, no tienes que configurar nada, SerfJ encontrará tus controladores si siguen esas reglas. Es bueno que todos los controladores sigan la misma regla, de esta forma puedes configurar el framework para que los encuentre todos de la misma manera (es una opción dentro del fichero de configuración). También puedes poner prefijos y sufijos a los controladores, pero esto también es una opción de la configuración. Por ahora no está toda la documentación preparada, pero lo estará pronto.
Por defecto, el framework responderá tus peticiones presentando una página web. Primero, buscará por una página con extensión .jsp, luego por una .html, y finalmente por una página .htm. Si no encuentra ninguna de esas páginas se lanzará una excepción. Pero... ¿dónde deben estar esas páginas?. Por convención, SerfJ las buscará en el direcotrio views. De esta forma tenemos que tener la siguiente estructura de directorios:
Dentro de esos directorios tenemos que tener una página index.jsp, new.jsp (para el método newResource), edit.html, etc.
Siguiendo la especificación JSR 311, SerfJ proporciona algunas anotaciones:
Pero hay una quinta anotación DoNotRenderPage.
Con esta anotación de tipo runtime, un programador puede marcar un método del controlador para que
no renderice ninguna página después de su ejecución. Esto sólo es válido para métodos que devuelvan void,
porque los métodos que devuelvan un objeto siempre serializarán el objeto en la respuesta.
Los métodos que no devuelven nada tienen que estar anotados también, así el framework es capaz de saber qué
hacer después de la ejecución del método. Si se anota el método con DoNotRenderPage, el framework devolverá
un estado HTTP 204 (No content), si no está anotado, el framework buscará una página para presentar.
Nuestra aplicación es ahora capaz de responder peticiones con bonitas páginas, pero, ¿qué pasa si quiero responder con algún objeto serializado?, ¿o con algún objeto serializado como JSON, XML, PDF, en Base 64, etc.? ¡No hay problema!, lo primero es declarar el método como que devuelve una clase serializable:
public class Account extends RestController { @POST public MyObject some-action() { // Algunas líneas de código muy inteligente :) MyObject myObject = // método fabuloso que devuelve un objeto de la clase MyObject return myObject; } }
Ahora ya puedes llamar a este método de diferentes formas:
¿Magia?... No, SerfJ proorciona algunos serializadores que te ayudan a poder hacer todo esto. Hay serializadores para XML, JSON y Base 64, pero puedes hacer os tuyos propios para esos formatos, o incluso implementar serializadores para los formatos que tú quieras como PDF, CSV, o lo que te dé la gana. Sólo necesitas escribir una implementación para la interfaz net.sf.serfj.serializers.Serializer:
package net.sf.serfj.serializers; /** * Interfaz parar serializadores. */ public interface Serializer { /** * Serialize an object in the format that the implementation requires. * * @param object * Object to serialize. * @return a String with the object serialized. */ public String serialize(Object object); /** * Deserialize an object from the format that the implementation requires to * Java Object. * * @param string * String representation of the object to deserialize. * @return an Object. */ public Object deserialize(String string); /** * Content type that will be used in the response. */ public String getContentType(); /** * Returns the extension which came in the URL. With that extension the * framework knows which serializer must use for serialization. * * @return a String with an extension without the dot. */ public String getExtension(); }
Pero, ¿dónde tienes que poner tus serializadores?. Bien, se sigue la lógica de los controladores:
Cuando estés seguro acerca de la arquitectura de tu aplicación, puedes configurar SerfJ para que busque tus serializadores en el sitio correcto, pero por ahora, los buscará usando varios algoritmos.
Sí, lo sé, en un navegador sólo puedes hacer peticiones GET o POST, pero hay un pequeño truco para convencer a SerfJ de que la petición sigue el protocolo HTTP. Tienes que hacer una petición POST, enviando el parámetro http_method cuyo valor será el método HTTP que necesita la petición (GET, POST, PUT or DELETE). De esta forma, si quieres llamar al método Bank.delete() para borrar el Banco con ID = 1, tienes que hacer una petición POST como esta:
POST /banks/1?http_method=DELETE
Desde luego, por eso SerfJ proporciona uno: net.sf.serfj.client.Client. Con este cliente puedes hacer peticiones REST a cualquier método de los controladores SerfJ que implementes. Si el método del controlador devuelve valores, el cliente sabrá cómo devolverte el objeto correcto. Para estar más cerca de la especificación JSR 311, si el método del controlador falla, se lanzará la excepción WebServiceException. También, si el método no devuelve nada (un método void), se responderá con un código HTTP 204.
No hay problema, SerfJ está en el repositorio de Maven Central, aquí tienes el POM para añadir a tus dependencias:
net.sf.serfj ;serfj 0.4.1
Tampoco hay problema :), estas son las dependencias:
Y para los tests:
De todas formas, puedes descargar un fichero .zip con todas las dependencias necesarias en tiempo de ejecución en nuestra sección de Descargas.
Copyright © 2010-2012 Eduardo Yáñez Parareda, Licensed under the Apache License, Version 2.0. Apache and the Apache feather logo are trademarks of The Apache Software Foundation.