Portlets con el framework Apache Tapestry y Apache Pluto

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

Los portales ofrecen una solución para los casos de uso de integración de aplicaciones, edición de contenido a modo de CMS, agregación de blogs, foros, colaboración entre personas, red social entre otros. La pieza fundamental de un portal en Java es un portlet. Desarrollar un portlet usando la API directamente no es simple, algunos frameworks que usaríamos para desarrollar aplicaciones y páginas web son usables para desarrollar portlets, Apache Tapestry es uno de ellos como muestro con un ejemplo en este artículo.

Apache Tapestry

Apache Pluto

Java

La API ofrecida de los portlets se puede considerar de bajo nivel y para facilitar la tarea de su programación es posible emplear un framework al igual que ocurre al programar una aplicación web con los servlets utilizando el lenguaje Java. Aún así es importante conocer los conceptos subyacentes de los portlets que están explicados de forma didáctica en el libro Portlets in Action.

Explicado como crear un portlet directamente con su API usaré el mismo ejemplo pero usando el framework Apache Tapestry que también es usable para realizar aplicaciones web pero para el que existe un módulo para desarrollar portlets y una pequeña documentación.

Liferay 7 debe usar algún mecanismo especial para cargar las clases no compatible con Tapestry por ello en este ejemplo usaré el contenedor de portlets Apache Pluto. Apache Pluto es la implementación de referencia para la API de los portlets, inicia el servidor significativamente más rápido que Liferay aunque no incorpora tantos portlets listos para usar.

El libro Portlets in Action define portal de la siguiente manera traducida al español:

Un portal es una colección de miniaplicaciones web llamadas portlets. Un portal soporta características como personalización, agregación de contenido o autenticación. Los portlets actúan como aplicaciones web dentro del portal mostradas en ventanas donde cada ventana en una página del portal representa un portlet. El portal agrega la información y proporciona una vista consolidada al usuario.

El ejemplo consiste en un mensaje de saludo que incluye un nombre el cual se introduce con un formulario desde el mismo portlet. Los portlets usando Apache Tapestry son muy similares a una aplicación web, con lo que gran parte del conocimiento es el mismo, pero en los que están a disposición varios servicios adicionales de la API de los portlets como PortletRequestGlobals donde están agregados varios objetos relativos las peticiones y respuestas como PortletRequest para la petición con ActionRequest, EventRequest, RenderRequest y ResourceRequest, y PortletResponse para la respuesta con ActionResponse, EventResponse, RenderResponse, ResourceResponse, … y la amplia colección de componentes reusables de toda aplicación con uno específico para enlazar páginas en un portlet, PortletPageLink. Apache Tapestry permite abstraerse en gran medida de la API subyacente de los portlets y las diferencias con una aplicación web no serán muy significativas. Todos los conceptos conocidos para desarrollar aplicaciones y páginas web sirven prácticamente en su totalidad para desarrollar portlets.

Los componentes usables en las plantillas de las páginas, salvo alguno adicional son los mismos que en una aplicación web. Unos 57 componentes listos para usar además de los propios y específicos de la aplicación.

Al igual que en el framework están incluidos múltiples componentes el contenedor de dependencias que tiene integrado Tapestry ofrece muchos servicios que es posible inyectar en las páginas y en componentes nuevos y propios, también desarrollar nuevos servicios que necesiten los portlets.

Para hacer el ejemplo Hola Mundo hay que definir dos páginas, una para mostrar el mensaje y otra para obtener un nombre, cada página se compone de una clase Java y una plantilla asociada que genera el HTML, ambos elementos son muy simples. El portlet hará uso del mecanismo de preferencias proporcionado por el portal con la clase PortletPreferences y que se encarga de persistir en su propia base de datos para obtener y guardar valores en este caso el nombre usado en el mensaje. El portlet usa un formulario que envía el nombre donde se utilizan los componentes Form, Label y TextField.

 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
package io.github.picodotdev.blogbitix.tapestry.portlet.pages;

import org.apache.tapestry5.portlet.services.PortletRequestGlobals;

import javax.inject.Inject;

public class Index {

    @Inject
    private PortletRequestGlobals globals;

    public String getMensaje() {
        if (globals.getRenderRequest() == null) {
            return "¡Hola mundo!";
        }
        String nombre = globals.getRenderRequest().getPreferences().getValue("nombre", null);
        if (nombre == null) {
            return "¡Hola mundo!";
        } else {
            return String.format("¡Hola %s!", nombre);
        }
    }

    public boolean isAutenticado() {
        if (globals.getRenderRequest() == null) {
            return false;
        }
        return globals.getRenderRequest().getUserPrincipal() != null;
    }
}
Index.java
1
2
3
4
5
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd" xmlns:p="tapestry:parameter">
<body>
	${mensaje} <t:if test="autenticado">(Usa la página de preferencias para cambiar este mensaje)</t:if>
</body>
</html>
Index.tml
 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
package io.github.picodotdev.blogbitix.tapestry.portlet.pages.preferences;

import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.portlet.services.PortletRequestGlobals;

import javax.inject.Inject;
import javax.portlet.PortletMode;

public class Index {

    @Property
    private String nombre;

    @Inject
    private PortletRequestGlobals globals;

    void setupRender() {
        nombre = globals.getRenderRequest().getPreferences().getValue("nombre", null);
    }

    String onSuccess() throws Exception {
        globals.getActionRequest().getPreferences().setValue("nombre", nombre);
        globals.getActionRequest().getPreferences().store();
        globals.getActionResponse().setPortletMode(PortletMode.VIEW);
		return "index";
    }
}
preferences-Index.java
1
2
3
4
5
6
7
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd" xmlns:p="tapestry:parameter">
<body>
	<t:form t:id="preferences" class="form-inline">
	    <t:label for="nombre"/>: <t:textfield t:id="nombre" value="nombre" label="Nombre"/> <t:submit value="Enviar"/>
	</t:form>
</body>
</html>
preferences-Index.tml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package io.github.picodotdev.blogbitix.tapestry.portlet.services;

import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.ioc.MappedConfiguration;

public class AppPortletModule {

	public static void contributeApplicationDefaults(MappedConfiguration<String, Object> configuration) {
		configuration.add(SymbolConstants.ENABLE_PAGELOADING_MASK, false);
		configuration.add(SymbolConstants.JAVASCRIPT_INFRASTRUCTURE_PROVIDER, "jquery");
		configuration.add(SymbolConstants.BOOTSTRAP_ROOT, "classpath:/META-INF/assets");
		configuration.add(SymbolConstants.HMAC_PASSPHRASE, "A8c7qS59h46J8nA");
	}
}
AppPortletModule.java

El resto son varios archivos descriptores necesarios entre los que está el típico web.xml de las aplicaciones web Java y el descriptor específico de los portlets portlet.xml.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://xmlns.jcp.org/xml/ns/portlet" version="3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/portlet http://xmlns.jcp.org/xml/ns/portlet/portlet-app_3_0.xsd">
	<portlet>
		<portlet-name>Index</portlet-name>
		<portlet-class>org.apache.tapestry5.portlet.ApplicationPortlet</portlet-class>
		<supports>
			<mime-type>text/html</mime-type>
			<portlet-mode>view</portlet-mode>
			<portlet-mode>edit</portlet-mode>
		</supports>
		<portlet-info>
			<title>Tapestry Portlet</title>
			<short-title>Tapestry Portlet</short-title>
			<keywords>Tapestry Portlet</keywords>
		</portlet-info>
		<supported-public-render-parameter>publicParam</supported-public-render-parameter>
	</portlet>
	<default-namespace>http://localhost:8080</default-namespace>
	<public-render-parameter>
		<identifier>publicParam</identifier>
		<name>publicParam</name>
	</public-render-parameter>
</portlet-app>
portlet.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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>Tapestry Portlet</display-name>
	<context-param>
		<param-name>tapestry.app-package</param-name>
		<param-value>io.github.picodotdev.blogbitix.tapestry.portlet</param-value>
	</context-param>
	<context-param>
		<param-name>tapestry.combine-scripts</param-name>
		<param-value>false</param-value>
	</context-param>
	<filter>
		<filter-name>AppPortlet</filter-name>
		<filter-class>org.apache.tapestry5.TapestryFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>AppPortlet</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>  
web.xml

Con Apache Tapestry están a nuestra disposición todas las facilidades de un framework de más alto nivel que la API de los portlets, con la API de los portlets accesible si es necesaria, altamente productivo y con el que reutilizaremos en otros portlets o proyectos los componentes desarrollados con una librería de componentes.

Construido el portlet con una tarea de Gradle el portlet se despliega copiando el archivo war al directorio de despliegue de los portlets de Apache Pluto, en webapps. Una vez desplegado accediendo a la página de administración se configuran los portlets que incluye cada página. El usuario y contraseña para iniciar sesión en Apache Pluto es pluto para el usuario y pluto para la contraseña.

Página de administración de portlets de Apache Pluto Inicio de sesión de Apache Pluto

Páginas de administración y sesión de Apache Pluto

Insertado el portlet en una página y accediendo a ella el portlet muestra el mensaje que emite y seleccionando la opción edit acceder a la página de preferencias acceder a la página que muestra el formulario y permite cambiar el mensaje. Este es el resultado del portlet desplegado en Apache Pluto.

Portlet con Apache Tapestry en Apache Pluto Página de preferencias del portlet

Portlet usando un dato almacenado en las preferencias

Portlet con Apache Tapestry en Apache Pluto

Para conocer más sobre los portlets el libro Portlets in Action es una buena fuente de documentación.

Descargado y descomprimido el binario de Apache Pluto para inicia con el comando startup.sh. Apache Pluto es en realidad un servidor Apache Tomcat con las adiciones para proporcionarle la funcionalidad de portal en la dirección http://localhost:8080/pluto.

 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
description = 'TapestryPortlet'
group = 'io.github.picodotdev.blogbitix.tapestry.portlet'
version = '0.1'

apply plugin: 'java'
apply plugin: 'war'

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

repositories {
	flatDir { dirs 'libs' }
    mavenCentral()
}

dependencies {
	compile "org.apache.tapestry:tapestry-core:5.4.3"
	compile "org.apache.tapestry.portlet:tapestry5-portlet:5.4.0"

	compile "org.apache.logging.log4j:log4j-slf4j-impl:2.4.1"
	compile "org.apache.logging.log4j:log4j-api:2.4.1"
	compile "org.apache.logging.log4j:log4j-core:2.4.1"

	providedCompile 'javax:javaee-api:7.0'
	providedCompile "javax.portlet:portlet-api:2.0"
}
build.gradle
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:
./pluto-3.0.0/bin, ./gradlew build

Portada libro: PlugIn Tapestry

Libro PlugIn Tapestry

Si te interesa Apache Tapestry descarga gratis el libro de más de 300 páginas que he escrito sobre este framework en el formato que prefieras, PlugIn Tapestry: Desarrollo de aplicaciones y páginas web con Apache Tapestry, y el código de ejemplo asociado. En el libro comento detalladamente muchos aspectos que son necesarios en una aplicación web como persistencia, pruebas unitarias y de integración, inicio rápido, seguridad, formularios, internacionalización (i18n) y localización (l10n), AJAX, ... y como abordarlos usando Apache Tapestry.



Comparte el artículo: