En la primera parte de este post vimos cómo crear un service builder, ahora vamos a trabajar para terminar el portlet.
Para no perder el foco, así es como quedará nuestro portlet gestor de tareas para Liferay.
Ahora en este post vamos a seguir desarrollando el portlet, si recordáis dejamos el portlet justo después de pinchar en service builder, el árbol del proyecto ha quedado más o menos así.
Ahora vamos a seguir desarrollando el portlet, para ello utilizaremos un patrón MVC, donde ya tenemos casi terminado el modelo, ¡y no hemos escrito nada de código! Solo un archivo y mira ya todo lo que hemos hecho 😉
Creando una utilidad
Como vimos en un post anterior, el controlador es el encargado de manejar a la vista y al modelo, para ello vamos a crearnos una clase que nos será muy útil.
Esta clase se llamará “ModelAndView” y lo único que definiremos será un Map (clave, valor) para relacionará cada una de las vistas con los modelos. La clase tiene una pinta como esta:
Creando el controlador
En este portlet vamos a desarrollar 4 controladores, pero no hay que asustarse, lo que conseguimos con esto es desacoplar todo lo posible todas las funcionalidades del portlet. Los controladores son:
- BalancerController: Es una clase que implementa MVCPortlet, Cuando realizamos cualquier acción sobre nuestro portlet Liferay se encarga de transferir el control a este Controlador. BalancerController transfiere el control a DefaultController.
- DefaultController: Es el discriminador, dependiendo de la petición que haya llegado (AddList o AddItem) este controlador da el control alguno de lso siguientes Controladores, esto nos sirve por si en un futuro añadimos más funcionalidad al portlet.
- AddListController: Si interactuamos para añadir, editar una lista este será en controlador que gestionará nuestras peticiones.
- AddItemController: Cuando estemos trabajando con ítems, este será el encargado de que todo funcione bien 😉
BalancerController
Este es la primera clase que vamos a crear, esta clase extenderá a MVCPortlet y la pondremos en un paquete llamado com.jesuslc.demos.browntasks.controller para tener el código bien ordenado.
Este es el código de la clase que explicaremos a continuación:
package com.jesuslc.demos.browntasks.controller; import java.io.IOException; import java.util.Map; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import com.jesuslc.demos.browntasks.util.ModelAndView; import com.liferay.util.bridges.mvc.MVCPortlet; public class BalancerController extends MVCPortlet { public void processAction(ActionRequest request, ActionResponse response) throws IOException, PortletException { String action = ""; if (request.getParameter("action") != null) action = request.getParameter("action"); if (action.equals("addList")) new AddListController().handleActionRequest(request, response); else if (action.equals("addItem")) new AddItemController().handleActionRequest(request, response); else new DefaultController().handleActionRequest(request, response); } public void render(RenderRequest request, RenderResponse response) throws IOException, PortletException { String action = ""; if (request.getParameter("action") != null) action = request.getParameter("action"); ModelAndView mav = null; if (action.equals("addList")) mav = new AddListController() .handleRenderRequest(request, response); else if (action.equals("addItem")) mav = new AddItemController() .handleRenderRequest(request, response); else mav = new DefaultController() .handleRenderRequest(request, response); Map<String, Object> v = mav.getMap(); for (int i = 0; i < v.keySet().toArray().length; i++) { String k = "" + v.keySet().toArray()[i]; request.setAttribute(k, v.get(k)); } include("/jsp/" + mav.getPage() + ".jsp", request, response); } }
Implementamos 2 métodos processAction que lo que hace es procesar la petición (request) y enviarla al controlador correspondiente y el método rederAction que crea un nuevo objeto ModelAndView para que el controlador correspondiente pueda interactuar con los datos de la BB y las páginas JSP.
DefaultController
Es el controlador por defecto, cuando no se añade ni una lista, ni un objeto este es el controlador que actúa, maneja las peticiones para cambiar entre listas de tareas. Implementa dos métodos handleRenderRequest, El método importante es el que devuelve un objeto ModelAndView.
package com.jesuslc.demos.browntasks.controller; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import com.jesuslc.demos.browntasks.model.TaskItem; import com.jesuslc.demos.browntasks.model.TaskList; import com.jesuslc.demos.browntasks.service.TaskItemLocalServiceUtil; import com.jesuslc.demos.browntasks.service.TaskListLocalServiceUtil; import com.jesuslc.demos.browntasks.util.ModelAndView; import com.liferay.portal.kernel.dao.orm.DynamicQuery; import com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil; import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil; public class DefaultController { public void handleActionRequest(ActionRequest request, ActionResponse response) throws IOException, PortletException { } public ModelAndView handleRenderRequest(RenderRequest request, RenderResponse response) throws IOException, PortletException { // objeto que representa al modelo y vista de Map<String, Object> model = new HashMap<String, Object>(); try { if (request.getParameter("order") != null) { model.put("order", request.getParameter("order")); } // view item String itemid = request.getParameter("itemid"); if (itemid != null) { TaskItem l = TaskItemLocalServiceUtil.getTaskItem(Integer .parseInt(itemid)); TaskList lObj = TaskListLocalServiceUtil.getTaskList(l .getList()); model.put("listObj", lObj); model.put("item", l); model.put("list", "" + l.getList()); model.put("statuses", new String[] { "Normal", "Waiting for", "Cancelled", "Finished" }); return new ModelAndView("viewitem", model); } // read params String tab = request.getParameter("tabs1"); String listid = request.getParameter("list"); String order = request.getParameter("order"); if (tab == null) tab = ""; if (order == null) order = "weight"; if (order.equals("")) order = "weight"; model.put("formTab", tab); /* Load Lists */ DynamicQuery dq = DynamicQueryFactoryUtil.forClass(TaskList.class); dq.addOrder(PropertyFactoryUtil.forName("weight").asc()); List<TaskList> ls = TaskListLocalServiceUtil.dynamicQuery(dq); String names = ""; for (int i = 0; i < ls.size(); i++) { TaskList l = ls.get(i); if (i == 0 && listid == null) listid = "" + l.getId(); if (i != 0) { names += ","; } names += l.getDescription(); if (("" + l.getId()).equals(listid) || (l.getDescription().equals(tab) && !tab.equals(""))) { model.put("formTab", l.getDescription()); model.put("list", l.getId()); } } model.put("formNames", names); /* Load Items */ DynamicQuery dqi = DynamicQueryFactoryUtil.forClass(TaskItem.class); dqi.add(PropertyFactoryUtil.forName("list").eq(model.get("list"))); dqi.addOrder(PropertyFactoryUtil.forName(order).asc()); List<TaskItem> is = TaskItemLocalServiceUtil.dynamicQuery(dqi); model.put("itemList", is); } catch (Exception ex) { ex.printStackTrace(); } return new ModelAndView("view", model); } }
Lo que hacemos en este método es desgranar la petición (request) sacando todos los parámetros para ir creando el objeto ModelAndView.
Lo que hacemos es primero ver si estamos en un una “vista de tarea” if (itemid != null) { si se cumple creamos esa lista con todos los parámetros almacenados en la base de datos.
Si no estamos en la vista por defecto donde tendremos que cargar los elementos correspondientes en ModelAndView. El código es bastante claro, aunque cabe destacar que para acceder a nuestras entidades utilizamos los objetos XXXLocalServiceUtil, en nuestro caso TaskItemLocalServiceUtil y TaskListLocalServiceUtil. También utilizamos DinamicQuery para poder acceder a la base de datos.
Creando controlador para AddList
Como hemos visto todos los controladores implementan dos métodos: public ModelAndView handleRenderRequest(RenderRequest request, RenderResponse response) y public void handleActionRequest(ActionRequest request, ActionResponse response). En este caso lo que hacemos es comprobar si la petición es para borrar una lista completa, para crearla o para actualizar dicha lista.
Solo tenemos que crear una nueva clase y listo.
package com.jesuslc.demos.browntasks.controller; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import com.jesuslc.demos.browntasks.model.TaskList; import com.jesuslc.demos.browntasks.service.TaskListLocalServiceUtil; import com.jesuslc.demos.browntasks.util.ModelAndView; import com.liferay.counter.service.CounterLocalServiceUtil; public class AddListController { public void handleActionRequest(ActionRequest request, ActionResponse response) throws IOException, PortletException { try { int id=-1; if ( request.getParameter("id")!=null ) { id=Integer.parseInt( request.getParameter("id") ); } response.setRenderParameter("action","view"); if ( request.getParameter("order")!=null ) response.setRenderParameter("order", request.getParameter("order") ); if ( request.getParameter("delete")!=null ) { TaskListLocalServiceUtil.deleteTaskList( (long) id ); return; } TaskList l; if ( id==-1 ) l=TaskListLocalServiceUtil.createTaskList( CounterLocalServiceUtil.increment(TaskList.class.getName()) ); else l=TaskListLocalServiceUtil.getTaskList( id ); l.setDescription( request.getParameter("description") ); l.setWeight( Integer.parseInt( request.getParameter("weight") ) ); if ( id==-1 ) TaskListLocalServiceUtil.addTaskList( l ); else TaskListLocalServiceUtil.updateTaskList( l ); response.setRenderParameter("list", ""+l.getId() ); } catch (Exception ex) { ex.printStackTrace(); } } public ModelAndView handleRenderRequest(RenderRequest request, RenderResponse response) throws IOException, PortletException { Map<String, Object> model = new HashMap<String, Object>(); try { String list=request.getParameter("id"); if ( list!=null ) { TaskList l=TaskListLocalServiceUtil.getTaskList( Integer.parseInt( list ) ); model.put( "item", l ); } model.put( "list", request.getParameter("list") ); if ( request.getParameter("order")!=null ) model.put("order", request.getParameter("order") ); } catch (Exception ex) { ex.printStackTrace(); } return new ModelAndView("addlist", model); } }
Como vemos todas las listas de tareas vienen caracterizadas por su id y es importante destacar los bloques try y catch, ya que nos ayudarán a solucionar los errores que tengamos, de momento, no hemos hablado demasiado sobre test y errores, pero seguro que más adelante dedicaremos algún que otro post.
AdditemController
Con este controlador lo que haremos será CRUD sobre las tareas (tasks), su código es similar al controlador de AddListController
package com.jesuslc.demos.browntasks.controller; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.PortletException; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import com.jesuslc.demos.browntasks.model.TaskItem; import com.jesuslc.demos.browntasks.service.TaskItemLocalService; import com.jesuslc.demos.browntasks.service.TaskItemLocalServiceUtil; import com.jesuslc.demos.browntasks.util.ModelAndView; import com.liferay.counter.service.CounterLocalServiceUtil; public class AddItemController { TaskItemLocalService taskItemLocalService=TaskItemLocalServiceUtil.getService(); public void handleActionRequest(ActionRequest request, ActionResponse response) throws IOException, PortletException { int id=-1; if ( request.getParameter("id")!=null ) { id=Integer.parseInt( request.getParameter("id") ); } try { response.setRenderParameter("action","view"); if ( request.getParameter("order")!=null ) response.setRenderParameter("order", request.getParameter("order") ); if ( request.getParameter("list")!=null ) response.setRenderParameter("list", request.getParameter("list") ); if ( request.getParameter("delete")!=null ) { taskItemLocalService.deleteTaskItem( (long) id ); return; } TaskItem l; if ( id==-1 ) l=taskItemLocalService.createTaskItem( CounterLocalServiceUtil.increment(TaskItem.class.getName()) ); else l=taskItemLocalService.getTaskItem( id ); l.setList( Integer.parseInt( request.getParameter("list") ) ); l.setLabel( request.getParameter("label") ); l.setDescription( request.getParameter("description") ); l.setWeight( Integer.parseInt( request.getParameter("weight") ) ); l.setStatus( Integer.parseInt( request.getParameter("status") ) ); if ( id==-1 ) taskItemLocalService.addTaskItem( l ); else taskItemLocalService.updateTaskItem( l ); } catch (Exception ex) { ex.printStackTrace(); } } public ModelAndView handleRenderRequest(RenderRequest request, RenderResponse response) throws IOException, PortletException { Map<String, Object> model = new HashMap<String, Object>(); String itemid=request.getParameter("itemid"); try{ if ( itemid!=null ) { TaskItem l=taskItemLocalService.getTaskItem( Integer.parseInt( itemid ) ); model.put( "item", l ); } model.put( "list", request.getParameter("list") ); if ( request.getParameter("order")!=null ) model.put("order", request.getParameter("order") ); } catch (Exception ex) { ex.printStackTrace(); } return new ModelAndView("additem", model); } }
Un comentario en “Service Builder para desarrollar un portlet con gestión de base de datos Parte 2/3”