Introducción a los portales y ejemplo de portlet con Liferay

Escrito por el , actualizado el .
java planeta-codigo programacion
Enlace permanente Comentarios

Muchas organizaciones usan portales para mantener su presencia en internet. Los portales son herramientas muy versátiles que incluyen la gestión de contenidos y flujo de trabajo para publicarlo, foros, blog, … Liferay es uno de los más conocidos que usa la plataforma Java. La unidad básica funcional de un portal es un portlet que en ciertos aspectos son similares en otros diferentes a lo que son los servlets en las aplicaciones web Java.

Java

Liferay

Los portales son una especialización de un sitio web que presenta información diversa de una forma integrada y uniforme. Suelen aplicarse cuando una entidad tiene necesidades de presentar información según el usuarios autenticado, su rol, los usuarios necesitan colaborar o se necesita integrar información de múltiples fuentes. Son usados por entidades públicas como gobiernos, ayuntamientos y también por corporaciones de tamaño mediano y grande.

Algunos de sus casos de uso son:

Uno de los servidores de portales más destacados y usados es Liferay aunque no es el único siendo Apache Pluto el servidor de referencia. En lo poco que los he probado Liferay comparado con Apache Pluto el primero tarda bastante más en iniciarse, se nota más lento y me ha dado problemas al usar el framework Apache Tapestry para desarrollar un portlet, sin embargo, Liferay incorpora más portlets con multitud de funcionalidades, es más usado y solicitado en ofertas de trabajo. Tanto Liferay como Apache Pluto implementan la especificación de los portlets de Java que son la pieza básica funcional de un portal.

Página inicial de Liferay

Liferay es el contenedor de portlets y proporciona un entorno de ejecución similar a lo que los contenedores de servlets como Tomcat proporcionan para los servlets. Las similitudes y diferencias entre un servlet y un portlet son las siguientes:

  • Los portlets son gestionados por un contenedor.
  • Su ciclo de vida está gestionado por el contenedor.
  • Generan contenido dinámico.
  • Interactúan con el cliente mediante peticiones y respuestas.

Y se diferencia en que:

  • Los portlets generan únicamente un fragmento de la página web.
  • No están asociados directamente a una URL.
  • No pueden generar contenido arbitrario, si se solicita text/html los portlets deben generar text/html.

El contenedor de portlets proporciona funcionalidades como:

  • Almacenamiento persistente para las preferencias.
  • Procesamiento de solicitudes.
  • Modos de los portlets.
  • Estado de la ventana o fragmento.
  • Información de usuario,

Liferay incluye más de 60 portlets listos para usar que cumplen las funciones de CMS, foros, blogs, agregador de blogs, wiki, calendario, encuestas, anuncios, herramientas sociales, de comercio electrónico, integración de contenido de sistemas externos, geolocalización, tiempo, administración, gestión de flujo de trabajo y otros muchos más ofrecidos en el marketplace.

Desde la página de descargas se puede obtener la edición para la comunidad de Liferay además de otros productos eligiendo la versión deseada y en la red para desarrolladores obtener documentación y material de referencia. Una vez descargado el archivo de la distribución de Liferay y descomprimido se inicia con el comando ubicado en tomcat-8.0.32/bin/startup.sh. En el archivo tomcat-8.0.32logs/catalina.out se emiten las trazas y mensajes del servidor. Iniciado Liferay se presenta una página de configuración, se han de aceptar los términos y condiciones e iniciar sesión con el usuario creado en la primera página de configuración.

Configuración básica de Liferay

Para añadir un portlet propio a Liferay hay que acceder al Panel de control > Aplicaciones > Gestor de aplicaciones y pulsar la opción cargar ubicada en la parte superior derecha de la página. En la salida del servidor aparecerán varias trazas relativas al despliegue del portlet.

Gestor de aplicaciones Instalar aplicación

Los portlets se distribuyen por lo general como archivos de aplicaciones web .war con varios descriptores adicionales con información que usa Liferay para el despliegue del portlet.

En el siguiente ejemplo comentaré cómo crear un portlet Hola Mundo sin ayuda de ningún framework como Spring o Apache Tapestry aunque Liferay proporciona ayuda y documentación para desarrollarlos con Liferay MVC Portlet o Spring MVC.

El archivo descriptor principal es portlet.xml donde se describen los portlets de la aplicación indicando por ejemplo su nombre, la clase que lo implementa o los modos que soporta, otros archivos descriptores son web.xml, liferay-portlet.xml y liferay-display.xml con unas propiedades exclusivas de Liferay indicando el icono y la categoría en la que ubicar el portlet en la paleta de portlets.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app version="2.0"
		xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
	<portlet>
		<portlet-name>hola-mundo-portlet</portlet-name>
		<display-name>Hola Mundo Portlet</display-name>
		<portlet-class>io.github.picodotdev.blogbitix.portlet.HolaMundoPortlet</portlet-class>
		<expiration-cache>0</expiration-cache>
		<supports>
			<mime-type>text/html</mime-type>
			<portlet-mode>view</portlet-mode>
			<portlet-mode>edit</portlet-mode>
		</supports>
		<portlet-info>
			<title>Hola Mundo Portlet</title>
			<short-title>Hola Mundo Portlet</short-title>
			<keywords>hola-mundo-portlet</keywords>
		</portlet-info>
	</portlet>
</portlet-app>
portlet.xml
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
		 http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
	<display-name>Hola Mundo Portlet</display-name>
</web-app>
web.xml
1
2
3
4
5
6
7
8
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 7.0.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_7_0_0.dtd">
<liferay-portlet-app>
	<portlet>
		<portlet-name>hola-mundo-portlet</portlet-name>
		<icon>/icon.png</icon>
		<instanceable>true</instanceable>
	</portlet>
</liferay-portlet-app>
liferay-portlet.xml
1
2
3
4
5
6
7
<?xml version="1.0"?>
<!DOCTYPE display PUBLIC "-//Liferay//DTD Display 7.0.0//EN" "http://www.liferay.com/dtd/liferay-display_7_0_0.dtd">
<display>
	<category name="Custom">
		<portlet id="hola-mundo-portlet" />
	</category>
</display>
liferay-display.xml

Un portlet es una clase Java que extiende de GenericPortlet. En el caso del ejemplo es muy sencillo ya que solo emite un mensaje usando una preferencia de configuración que Liferay se encarga de persistir, tiene un modo de edición y procesa una acción para cambiar el valor de una preferencia que se utiliza al emitir el mensaje.

Los portlets con sus diferencias funcionales con los servlets tienen muchas similitudes y una API con clases equivalentes a los servlets. Así la clase principal de la que hay que heredar para crear un portlet es GenericPortlet o implementar la interfaz Portlet. Las peticiones en los portlets siguen una serie de fases que se van ejecutando en el siguiente orden ActionPhase, EventPhase, HeaderPhase y RenderPhase. Para los recursos como imágenes o documentos hay una fase específica ResourcePhase.

Fases del ciclo de vida de una petición de un portlet Métodos de ciclo de vida de un portlet

Fases y métodos del ciclo de vida de un portlet

Para cada una de estas fases en la API de los portlets hay un método específico que son processAction, procesEvent, renderHeaders y render. Los portlets poseen modos que se visualizan con los métodos doEdit, doHelp y doView o el correspondiente anotado con @RenderMode. Cada uno de esos métodos para cada una de las fases reciben dos parámetros uno que representa a la petición que heredan de PortletRequest y son ActionRequest, ClientDataRequest, EventRequest, HeaderRequest, RenderRequest y ResourceRequest. Los objetos que representan a las respuestas heredan de PortletResponse y son ActionResponse, EventResponse, HeaderResponse, MimeResponse, RenderResponse, ResourceResponse y StateAwareResponse.

La interfaz PortletPreferences obtenida con el método getPreferences() de una clase que herede de PortletRequest también es importante ya que con ella el portlet es capaz de persistir incluso entre reinicios del servidor los datos relativos a su funcionamiento que desee aunque esto no sustituye a la utilización de una base de datos como PostgreSQL o MongoDB. Los portlets también tienen el equivalente de filtros de los servlets con la clase PortletFilter y el equivalente de sesión con la clase PortletSession.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package io.github.picodotdev.blogbitix.portlet;

import javax.portlet.*;
import java.io.IOException;
import java.util.Arrays;

public class HolaMundoPortlet extends GenericPortlet {

    public void doView(RenderRequest request, RenderResponse response) throws IOException, PortletException {
        PortletPreferences preferences = request.getPreferences();
        String name = preferences.getValue("name", null);
        if (name == null) {
            response.getWriter().write(String.format("¡Hola!"));
        } else {
            response.getWriter().write(String.format("Hola %s", name));
        }
    }

    public void doEdit(RenderRequest request, RenderResponse response) throws IOException, PortletException {
        PortletURL url = response.createActionURL();
        url.setParameter("addName", "addName");

        String template = 
            "<form id=\"%sForm\" action=\"%s\" method=\"post\">\n" +
            "   <label for=\"%sNameInput\">Name:</label>\n" +
            "   <input id=\"%sNameInput\" type=\"text\" name=\"%sname\">\n" +
            "   <input type=\"submit\" name=\"submit\" value=\"Enviar\" title=\"Enviar\">\n" +
            "</form>\n";

        String namespace = response.getNamespace();
        String html = String.format(template, namespace, url.toString(), namespace, namespace, namespace);

        response.getWriter().write(html);
        response.getWriter().close();
    }

    public void processAction(ActionRequest request, ActionResponse response) throws IOException, PortletException {
        String name = request.getParameter("name");
        if (name != null) {
            PortletPreferences preferences = request.getPreferences();
            preferences.setValue("name", name);
            preferences.store();
            response.setPortletMode(PortletMode.VIEW);
        }
    }
}
HolaMundoPortlet.java

Usando como herramienta de construcción del proyecto Gradle el archivo .war a desplegar el Liferay se genera con la tarea build en el directorio build/libs/HolaMundoPortlet-0.1.war. Esta archivo hay que desplegarlo en Liferay para posteriormente incluirlo en alguna página, se visualice el contenido que genera y se pueda interactuar con él.

Añadir portlet Portlet HolaMundo

Preferencias del portlet Portlet HolaMundo con preferencias

Desarrollar un portlet con su API directamente es una tarea costosa si la funcionalidad o complejidad del portlet es mucha. Al igual que en Java no se suele utilizar la API de los servlets directamente, aunque es la API subyacente, y se suele utilizar alguno de los muchos frameworks disponibles para los portlets también hay varios frameworks entre los que elegir. En el artículo Portlets con el framework Apache Tapestry y Apache Pluto muestro un ejemplo usando un framework de alto nivel, orientado a componentes y altamente productivo.

Aunque el libro Liferay in Action o Portlets in Action no están actualizados a la última versión sirven para conocer los conceptos básicos de su funcionamiento, explican la teoría e incluyen ejemplos de código de como crear un portlet.

Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew build


Comparte el artículo: