Anteriormente repasamos cuales eran las fases de un portlet, sumemos ahora una nueva herramienta que nos permitirá desarrollar portlets con uno de los frameworks mas utilizados en el ecosistema de Java.
¿Qué es Spring MVC?
Spring MVC es un proyecto que nos permite aplicar el patrón MVC (Model-View-Controller) al crear aplicaciones web basadas en Servlets. Dentro del ecosistema de los portlets, ¿Existirá algo similar?
La respuesta es portletMVC4spring, un fork del abandonado Spring Portlet MVC que nos provee una manera de utilizar nuestros portlets dentro del ecosistema del framework, ahora bajo la bandera de Liferay.
Si venimos utilizando Spring sabemos que una parte importante es conocer las anotaciones que utilizaremos en nuestras clases para aprovechar las bondades de Spring, veamos algunas de las que nos provee este proyecto:
@Controller
Nos permite indicar que nuestra clase actuará como controlador de spring mvc.
@RequestMapping
Con esta anotación indicamos a nivel clase o método a que tipo de peticiones va a responder nuestro portlet. Normalmente a nivel de clase indicamos qué modo (como VIEW o EDIT) y luego con anotaciones a nivel de método vamos refinando según necesitemos, según el tipo de petición y mas específicamente con parámetros de request a través de params:
@Controller @RequestMapping("VIEW") public class ControladorUsuario{ @RequestMapping public String showList(Model model){} @RequestMapping(params = "mierror=db") public String vistaConErrorDB(ModelMap modelMap){} @RequestMapping(params = "mierror") public String vistaConErrorDefault(RenderRequest request, RenderResponse response){} @RequestMapping public void crearUsuario(@ModelAttribute("user") User user, ActionResponse response) {} @RequestMapping(params = "action=actualizar") public void actualizarUsuario(ActionRequest request, ActionResponse response) {} }
A nivel clase estamos indicando que ControladorUsuario es un controller, y que aceptara las requests del modo VIEW del portlet. El método showList servirá las peticiones de render por defecto, si especificamos, por ejemplo, un parámetro miError con valor timeout, como no tenemos ningún método especifico para ese error, vistaConErrorDefault se encargara de esta petición. Por el contrario, si especificamos mierror con el valor db, vistaConErrorDB se encargará de mostrarnos la vista correspondiente.
Si usamos @RequestMapping a nivel de método, portletMVC4Spring se basará en la firma del método para determinar que tipo de request estamos manejando, pero también nos da la posibilidad de ser más específicos usando @RenderMapping, @ActionMapping, @ResourceMapping y @EventMapping.
En este primer ejemplo de código tenemos RenderMappings y ActionMappings, también se puede escribir de esta manera:
@Controller @RequestMapping("VIEW") public class ControladorUsuario{ @RenderMapping public String showList(Model model){} @RenderMapping(params = "mierror=db") public String vistaConErrorDB(ModelMap modelMap){} @RenderMapping(params = "mierror") public String vistaConErrorDefault(RenderRequest request, RenderResponse response){} @ActionMapping public void crearUsuario(@ModelAttribute("user") User user, ActionResponse response) {} @ActionMapping(params = "action=actualizar") public void actualizarUsuario(ActionRequest request, ActionResponse response) {} }
La firma de los métodos es bastante flexible a nuestras necesidades, podemos usar como argumentos:
- Request y Response objects (como RenderRequest o ActionResponse).
- InputStream / Reader para tener acceso directo al contenido del request.
- OutputStrem / Writer para poder escribir directamente la respuesta.
- Map / Model / ModelMap para interactuar con el modelo de spring MVC directamente
- @ModelAttribute para pasar un objeto desde un form utilizando thymeleaf o JSP
- BindingResult para realizar validaciones con hibernate validator.
En cuanto a los valores de retorno también tenemos varias posibilidades:
- View / String nos permiten especificar una vista para resolver y además se actualiza implícitamente el modelo de Spring (Los objetos que reciba como ModelAttribute por parámetro o métodos con esta anotación pertenecientes a la misma clase también se agregan sin tener que especificarlo)
- ModelAndView / Model / Map que nos permite actualizar el modelo de Spring explícitamente (con código dentro del método) además de la actualización implícita.
- void si el método se encarga de escribir la respuesta directamente.
- Cualquier otro tipo de retorno se agrega al modelo que se expone a la vista actual.
Estos son algunos ejemplos que pueden resultar útiles y podemos encontrar la lista completa en la documentación.
También cabe aclarar que en ocasiones tenemos varias formas de hacer una operación, por ejemplo, a través de un ResponseObject, podemos obtener su OutputStream sin necesidad de agregarlo a los parámetros de nuestro método, pero si solo necesitamos usar el OutputStream, queda mas conciso y legible recibirlo como parámetro directamente. Esto por supuesto es una recomendación ya que ambas formas son válidas.
@ResourceMapping
ResourceMapping tiene la particularidad de poder definir un ID de recurso para identificarlo, por ejemplo este método escribe directamente una respuesta en formato JSON cuando se le solicita un usuario en particular:
@ResourceMapping("usuarioIvan") public void obtenerUsuarioIvan(Writer salida) throws IOException { salida.write("{ \"nombre\": \"Ivan\", \"apellido\": \"Gomez\" }"); }
En este ejemplo utilizamos una firma que nos permita acceder fácilmente a lo que necesitamos (el writer del response) y escribimos una respuesta como string directamente en el formato requerido.
Ahora bien, teniendo los métodos anotados y habiendo especificado los parámetros o id’s si los hubiera para tener un mapeo acorde a nuestras necesidades ¿nos falta algo?
URLs de un portlet
Si utilizamos alguna vez SpringMVC notamos una diferencia fundamental en las anotaciones: la ausencia de URLs explicitas, esto se debe a la naturaleza de los portlets: como puede haber mas de uno por pagina o estar en diferentes porciones de la pagina, no tenemos una url específica e inamovible para nuestro portlet per se, sino que es nuestro portal (en este caso Liferay) el que las generará de manera dinámica al instanciar el portlet en una página.
A través de la tecnología que usemos para nuestras vistas tendremos formas de acceder a estas URLs desde el front-end (con o sin parámetros) para poder realizar nuestras peticiones, pero eso lo veremos en el próximo artículo… ¡Hasta la próxima!