Partendo dai concetti base affrontiamo come si implementa una portlet e quali sono gli strumenti messi a disposizione dalla API per gestire il ciclo di vita della request e della response.

Un po’ di formalismi

Prima di entrare nella programmazione delle portlet è utile introdurre alcune definizioni e concetti basilari. Per prima cosa si tenga a mente che una portlet serve per gestire una porzione del portale (in gergo una portlet-window) che è normalmente rappresentata da un box rettangolare. I vari box (il cui aspetto può variare molto da caso a caso a seconda del layout e dei CSS utilizzati) sono organizzati in modo da comporre la pagina del portale nel suo complesso (detta appunto portal-page).

Figura 1 - Terminologia relativa alle componenti di un portale portlet-based
Figura 1 - Terminologia relativa alle componenti di un portale portlet-based

Ogni portlet, in un determinato istante, può assumere diversi stati e modalità di visualizzazione. Per quanto concerne la modalità una portlet può trovarsi in uno dei seguenti modi:

View mode: è la modalità standard con la quale la porlet visualizza un contenuto o consente di interagire con l’utente durante il suo normale svolgimento del lavoro. Ad esempio una portlet che mostra le previsioni del tempo o un canale RSS, in view mode mostrerà il tempo su una determinata città o le notizie di un certo canale.

Figura 2 - La Weather portlet in modalità di VIEW: l'utente visualizza le previsioni meteorologiche di una determinata città
Figura 2 - La Weather portlet in modalità di VIEW: l'utente visualizza le previsioni meteorologiche di una determinata città

Edit mode: è la modalità con la quale in genere l'utente esegue modifiche sui parametri operativi della portlet. Ad esempio in edit mode la portlet delle previsioni del tempo permetterebbe di scegliere la città della quale visualizzare le previsioni meteo.

Figura 3 - La Weather portlet in modalità di EDIT: l'utente sceglie la città per la quale visualizzare le previsioni meteorologiche
Figura 3 - La Weather portlet in modalità di EDIT: l'utente sceglie la città per la quale visualizzare le previsioni meteorologiche

Help mode: è la modalità con la quale la portlet fornisce informazioni di aiuto all'utente.

Queste modalità devono essere dichiarate nel descrittore di deployment della portlet (portlet.xml):

<supports>
  <mime-type>text/html</mime-type>
  <portlet-mode>VIEW</portlet-mode>
</supports>

Interfaccia Portlet e ciclo di vita

L'interfaccia Portlet definisce i metodi fondamentali per il ciclo di vita:

public void processAction(ActionRequest req, ActionResponse res) throws PortletException, IOException

public void render(RenderRequest req, RenderResponse res) throws PortletException, IOException

La classe GenericPortlet implementa questa interfaccia fornendo un'implementazione base:

public abstract class GenericPortlet extends Object implements Portlet, PortletConfig {
  // ...
}

Il metodo doDispatch smista le richieste ai metodi appropriati in base alla modalità:

public void doDispatch(ActionRequest req, ActionResponse res) throws PortletException, IOException

I metodi per le varie modalità sono:

public void doView(RenderRequest req, RenderResponse res) throws PortletException, IOException

public void doEdit(RenderRequest req, RenderResponse res) throws PortletException, IOException

public void doHelp(RenderRequest req, RenderResponse res) throws PortletException, IOException

Esempio: la prima portlet

Ecco un esempio semplice di portlet:

package com.mokabyte.portlets;

import java.io.IOException;
import java.io.Writer;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class FirstPortlet extends GenericPortlet {

  protected void doView(RenderRequest req, RenderResponse res)
      throws PortletException, IOException {
    res.setContentType("text/html");
    Writer writer = res.getWriter();
    writer.write("Salve mondo, sono la prima portlet del corso sulle portlet");
  }
}

Creazione degli URL: come invocare una portlet in modalità action

Per invocare una portlet si deve creare un URL in modo contestuale al container di esecuzione: in genere tale URL (relativo) che la referenzia può essere creato da codice impostando i parametri di invocazione. Sarà il portal container a contestualizzare tale URL.

I metodi che si possono utilizzare sono i seguenti:

public URL createRenderURL()

public URL createActionURL()

public void setParameter(String name, String value)

public void setParameters(java.util.Map params)

Ecco un esempio di utilizzo:

protected void doEdit(RenderRequest renderRequest, RenderResponse renderResponse)
    throws PortletException, IOException {
  // stampa il form HTML che invocherà la portlet
  writer.write("<H1>Portlet Settings</H1>");
  writer.write("<FORM ACTION=");
  writer.write(renderResponse.createActionURL().toString());
  writer.write(">");
  writer.write("News Title: <INPUT TYPE=text name='newsTitle'>");
  writer.write("NewsBody: <INPUT TYPE=textarea name='newsBody'>");
  writer.write("<P>");
  writer.write("<INPUT TYPE=submit>");
  writer.write("</FORM>");
}

In questo caso si utilizza una tecnica rudimentale per stampare il form di invocazione (stampandone l'HTML riga per riga): normalmente si usano tecniche più evolute che includono pagine JSP o template di vario tipo. Successivamente, dopo che l'utente ha cliccato sul form, si processa l'azione: i parametri sono passati al rendering per visualizzare il nuovo stato.

public void processAction(ActionRequest actionRequest, ActionResponse actionResponse)
    throws PortletException, IOException {
  // ricava i parametri di action
  String newsTitle = actionRequest.getParameter("newsTitle");
  String newsBody = actionRequest.getParameter("newsBody");

  if (newsTitle != null) {
    // salva il parametro per le operazioni di rendering
    actionResponse.setRenderParameter("newsTitle", newsTitle);
  }

  if (newsBody != null) {
    // salva il parametro per le operazioni di rendering
    actionResponse.setRenderParameter("newsBody", newsBody);
  }
}

Infine, il metodo di rendering utilizza i parametri salvati per visualizzare il contenuto:

protected void doView(RenderRequest req, RenderResponse res)
    throws PortletException, IOException {
  // setta il MIME Type
  res.setContentType("text/html");

  // ricava il writer per poter stampare
  Writer writer = res.getWriter();

  // ricava i parametri di request inoltrati dalla action
  String newsBody = req.getParameter("newsBody");
  String newsTitle = req.getParameter("newsTitle");

  if (newsBody != null && newsTitle != null) {
    writer.write(newsTitle);
    writer.write(newsBody);
  } else {
    // se non è stato impostato il content visualizza un messaggio di default
    writer.write("Nessun messaggio da visualizzare");
  }
}

Riferimenti

JSR 168: Portlet Specification - http://jcp.org/en/jsr/detail?id=168

"Building Portals with the Java Portlet API" by Jeff Linwood and Dave Minter