Manual de Rreferencia (actualizado a la versiĆ³n 0.3.4)

Índice

1. Arquitectura

SerfJ proporciona una arquitectura MVC, pero no hace nada con los modelos, sus actores principales son los controladores y éstos son dirigidos por las peticiones REST.

Los controladores representan los recursos de la aplicación, por lo que la forma de enviar mensajes a los recursos es a través de peticiones REST. Cuando una solicitud es atendida por el servlet principal (net.sf.serfj.RestServlet), se busca un recurso (controlador) y se le pregunta si es capaz de responder a la solicitud. En ese caso la acción se ejecuta, y la respuesta es servida al cliente. La respuesta puede ser una página, un objeto serializado, o un código de estado HTTP 204 que significa que no hay contenido.

1.1 URLs tipo REST

Ya que las peticiones REST controlarán el flujo de las aplicaciones SerfJ, es muy importante leer esta sección. Sin embargo, los conceptos que se explican a continuación son simples, así que todo va a ser fácil de entender.

Los métodos HTTP soportados por SerfJ son:

Por ejemplo, si quieres presentar una página que tiene un formulario para crear o actualizar un recurso, es necesario enviar una petición GET, no un PUT. Sin embargo, el botón de envío de ese formulario (cuya intención es actualizar información) tiene que enviar una petición PUT.

Los patrones para las peticiones REST, son:

Ten en cuenta que el nombre del recurso debe ser plural, y que los identificadores deben comenzar con un número. Pero SerfJ es capaz de analizar URLs con recursos anidados:

Si finalizas la URL con una extensión, el resultado no será una página si no un objeto serializado. De esa forma, dependiendo de la extensión utilizada, puedes recibir un objeto JSON, XML, etc:

1.2 URLs Estándar

Hay algunas URL que siempre van a llamar a los mismos métodos del controlador:

Método HTTPURLMétodo del ControladorVistaSignificado
GET/accountsindex()indexMuestra todos los recursos
POST/accountscreate()createCrea un nuevo recurso
GET/accounts/1show()showMuestra el recurso con ID 1
PUT/accounts/1update()updateActualiza el recurso con ID 1
DELETE/accounts/1delete()deleteElimina el recurso con with ID 1
GET/accounts/1/newResourcenewResource()newMuestra un formulario para crear un recurso
GET/accounts/1/editedit()editMuestra un formulario para actualizar el recurso con ID 1

2. Controladores

Los controladores son los actores principales de SerfJ, las peticiones REST se envian a ellos, y las responden. Las respuestas podrían ser una página, un objeto serializado, o nada (un código HTTP).

Hay dos maneras de escribir un controlador (habrá más en las próximas versiones de SerfJ), extender de la clase net.sf.serfj.RestController, o incluso escribir un JavaBean. Este último caso es raro porque el controlador no será capaz de hacer algunas acciones como obtener parámetros de la petición, o redirigir a otra página, o enviar objetos a las páginas JSP.

Por ahora, extender la clase RestController es la mejor manera de escribir un controlador. Los métodos que atienden a las solicitudes no deben tener parámetros, pero pueden devolver objetos y lanzar excepciones. Por ejemplo, si necesitamos un controlador para atender peticiones tipo /accounts, tenemos que escribir una clase como esta:

            public class Account extends RestController {
            }
        

2.1 Anotaciones

Hay varias anotaciones que indican al framework el método HTTP que acepta el método de un controlador.

Además, hay otra anotación, @DoNotRenderPage, que le dice al framework después de ejecutar el método de un controlador, niniguna página será presentada al cliente, sino que se responderá con un código HTTP 204. Los métodos que devuelven objetos no renderizan una página como resultado, por tanto no tienen porqué ser anotados con @DoNotRenderPage. Sin embargo, un método que no devuelve nada (un método void), pero el desarrollador no quiere para hacer una página como resultado para el mismo, sí tiene que ser anotado.

2.2 Obtener parámetros de las peticiones

Obviamente, la clase que escribimos antes de no hará nada, no va a responder a niniguna petición. Vamos a escribir más cosas. Si necesitamos un método para actualizar información de una cuenta (PUT /accounts/1), podemos escribir un método como este:

            public class Account extends RestController {
			    @PUT
			    public void updateAccount() {
				    String accountId = this.getId("account");
				}
            }
        

Vemos que el método puede recuperar el identificador de la cuenta de la request con getId (String). Vamos a ver cómo conseguimos los demás identificadores, si las solicitudes han anidado recursos (PUT /banks/1/accounts/2).

            public class Account extends RestController {
                @PUT
                public void updateAccount() {
                    String accountId = this.getId("account");
                    String bankId = this.getId("bank");
                }
            }
        

Si guardas algunos parámetros en la petición (Request), el método también puede recuperarlos. Los parámetros pueden viajar en la query string o en la request.

            public class Account extends RestController {
                @PUT
                public void updateAccount() {
                    String accountId = this.getId("account");
                    String someInfo = this.getStringParam("some_info_param_name");
                }
            }
        

Pero ¿qué pasa con el envío de objetos que no son cadenas?. Bueno, primero necesitas serializarlos antes de enviarlos, a continuación, en el controlador, deserializarlos. SerfJ proporciona una clase para serializar/deserializar objetos a strings en Base 64. Pero puedes usar lo que quieras para hacerlo.

            public class Account extends RestController {
			    private Base64Serializer serializer = new Base64Serializer();
				
                @PUT
                public void updateAccount() {
                    String accountId = this.getId("account");
                    String someInfo = this.getStringParam("some_info_param_name");
					Balance balance = (Balance) serializer.deserialize(this.getStringParam("balance"));
                }
            }
        

2.3 Enviar parámetros en la respuesta

Ahora sabemos cómo obtener los parámetros de la request, pero a veces tendremos que enviar objetos a una JSP, por ejemplo. Obviamente los objetos deben implementar java.io.Serializable.

            public class Account extends RestController {
                @PUT
                public void updateAccount() {
				    Account account = // some code to get an account
				    this.addObject2Request("my_object_param_name", account);
                }
            }
        

Será muy común que los métodos devuelvan objetos. Como hemos visto en la sección 1.1, los métodos se debe llamar con URLs REST que terminen con una extensión. La extensión dirá al framework qué serializador se debe utilizar para dar la respuesta. SerfJ ofrece tres serializadores diferentes (XML, JSON o Base64) para tres diferentes extensiones (.xml, .json, .64), pero los desarrolladores pueden escribir sus propios serializadores (véase la sección 3).

Por ejemplo, un método que responda a URLs como /accounts/1/balance.xml se podría escribir de dos maneras. Veamos la primera forma de hacerlo:

            public class Account extends RestController {
                @GET
                public Balance balance() {
				    Balance balance = new Balance();
					return balance;
                }
            }
        

Este método siempre tratará de devolver un objeto, cómo se serializa el objeto depende de la extensión utilizada. Pero puede ser que necesitemos un método que devuelva un objeto cuando se recibe una extensión, o mostrar una página en los demás casos:

            public class Account extends RestController {
                @GET
                public void balance() {
                    Balance balance = new Balance();
                    if (response.getSerializer() != null) {
                        response.serialize(balance);
                    } else {
					    // This is optional, we need it only if we want to send the object to the page
                        this.addObject2Request("balance", balance);
                        response.renderPage("balance");
                    }
				}
            }
        

2.4 Presentar páginas

Los métodos del controlador siempre muestran una página después de su ejecución a menos que el método devuelva un objeto o esté anotado con @DoNotRenderPage. La página que tratará de mostrar debe estar en un subdirectorio cuyo nombre es el nombre del recurso, y debe estar bajo el directorio definido en la propiedad views.directory (el valor por defecto es views). Las páginas deben tener extensiones .jsp, .html o .htm. Por ejemplo:

ControladorMétodoVista
Accountvoid index()views.directory/account/index
Accountvoid show()views.directory/account/show
Accountvoid newResource()views.directory/account/new
Bankvoid edit()views.directory/bank/edit
Carvoid update()views.directory/car/update
Accountvoid create()views.directory/account/create
Accountvoid delete()views.directory/account/delete
Accountvoid myMethod()views.directory/account/myMethod
AccountObject myMethod()Devuelve un objeto serializado
Account@DoNotRenderPage void myMethod()Devuelve un código HTTP 204

Así es como el framework muestra las páginas por defecto, pero hay maneras de mostrar otras páginas. Hay tres métodos para mostrar páginas:

3. Serializadores

Cuando llega una petición REST, si tiene una extensión, se busca un serializador capaz de serializar la respuesta (lea la sección 5 para saber cómo se buscan los recursos). SerfJ proporciona serializadores para XML, JSON y Base 64, pero los desarrolladores pueden hacer los suyos propios, y pueden hacer otros para tratar diferentes extensiones.

Es muy fácil desarrollar serializadores nuevos, sólo tienes que implementar la interfaz net.sf.serfj.serializers.Serializer, que es tan simple que no necesita más explicaciones que su propio Javadoc:

			package net.sf.serfj.serializers;
			
			/**
			 * Interface for Serializers.
			 * 
			 * @author Eduardo Yáñez
			 */
			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();
			}
		

El nombre de la clase debe comenzar con la extensión para la que se ha hecho el serializador, seguido por el nombre del recurso, y debe terminar con Serializer:

4. Configuración

Este framework trata de seguir el concepto de la Convención sobre Configuración, por lo que para usarlo es casi innecesario configurarlo. Por supuesto que necesita ser configurado, pero sólo un poco. Sin embargo, si los desarrolladores quieren hacerlo funcionar mejor, se pueden configurar varias propiedades para evitar que SerfJ haga predicciones para encontrar algunos recursos.

SerfJ sólo tiene un archivo de configuración serfj.properties que tiene que estar en /config dentro del classpath. Sólo necesita una propiedad main.package para poder funcionar. Esta propiedad debe apuntar a un paquete donde SerfJ buscará los controladores y serializadores, pero eso no significa que todos los controladores y serializadores deban estar en ese paquete, la forma en que el framework busca los recursos se explica en la siguiente sección.

4.1 Parámetros de configuración

5. Buscar recursos

Hay tres formas en las que SerfJ busca recursos, se llaman::

Si no se define ninguna en el fichero de configuración, el framework usa cada una en ese orden para encontrar los recursos. Así que si un desarrollador quiere decirle a SerfJ cómo se deben buscar los recursos, debe definir la propiedad packages.style.

5.1 Estilo 'Functional'

Si se está buscando un controlador, el nombre de la clase será:

main.package + "." + alias.controllers.package + "." + capitalized(singularized(resource name)) + suffix.controllers.

Si tenemos la siguiente configuración:

El framework buscará un controlador cuya clase será:

net.sf.serfj.tests.controllers.BankCtrl

Si el recurso buscado es un serializador, entonces se usará un prefijo para el nombre de la clase. El prefijo no es configurable, será la extensión de la request con la primera letra en mayúsculas (Xml, Pdf, Json, etc.). Por ejemplo, teniendo la siguiente configuración:

Buscando un serializador para accounts en JSON, la clase será:

net.sf.serfj.tests.serializers.JsonAccountSerializer

5.2 Estilo 'Functional by model'

En esta estrategia el nombre del recurso se singulariza y se añade al valor de main.package. Si está buscando un controlador, el nombre de la clase será:

main.package + "." + singularized(nombre del recurso) + "." + alias.controllers.package + "." + capitalized(singularized(nombre del recurso)) + suffix.controllers.

Así, teniendo la siguiente configuración:

El framework buscará un controlador con el siguiente nombre de clase:

net.sf.serfj.tests.bank.ctrl.Bank

Si el recurso buscado es un serializador, entonces se utiliza un prefijo para el nombre de la clase. El prefijo no es configurable, va a ser la extensión de la solicitud con la primera letra en mayúsculas (Xml, Pdf, Json, etc.). Por ejemplo, con la siguiente configuración:

Buscando un serializador XML para accounts, dará como resultado una clase con el siguiente nombre:

net.sf.serfj.tests.account.serial.XmlAccountSerializer

5.3 Estilo 'By model'

En esta estrategia el nombre del recurso se singulariza y se añade al valor de main.package, pero el alias no se usa. Si se busca un controlador, el nombre de la clase esperado será:

main.package + "." + singularized(nombre del recurso) + "." + capitalized(singularized(nombre del recurso)) + suffix.controllers.

Es decir, teniendo esta configuración:

El framework buscará un controlador con el siguiente nombre de clase:

net.sf.serfj.tests.bank.Bank

Si el recurso buscado es un serializador, se utiliza un prefijo para el nombre de la clase. El prefijo no es configurable, va a ser la extensión de la request con la primera letra en mayúsculas (Xml, Pdf, Json, etc.). Por ejemplo, teniendo la siguiente configuración:

Buscando un serializador para accounts en formato CSV, el nombre esperado de la clase será:

net.sf.serfj.tests.account.CsvAccountSerializer

6. Cliente de SerfJ

SerfJ proporciona la clase net.sf.serfj.client.Client para hacer peticiones REST. Dispone de cuatro métodos públicos (get, post, put, y delete) para hacer las peticiones. La interfaz es muy simple, por lo que el Javadoc debería ser suficiente para utilizar esta clase.

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.