Liferay Portal Overview

III parte: Strumenti e concetti per sviluppare portletdi

In questo articolo vedremo i concetti base per lo sviluppo di portlet per Liferay 6. Illustreremo come avvalerci del supporto di strumenti ad hoc per velocizzarne l'implementazione e daremo le prime nozioni di come scrivere una portlet.

Le novità nella versione 6

Con la nuova versione del portale, la 6, il team di Liferay ha introdotto diversi cambiamenti e migliorie [1], sia per quanto riguarda l'aspetto grafico, sia per quello che interessa gli sviluppatori. Vediamo di cosa si tratta.

Chi ha sviluppato con le precedenti versioni di Liferay conosce sicuramente l'extension environment, o ext per brevità. Chi non lo conosce può pensare all'ext come a una sorta di "wrapper" del sorgente core di Liferay. Grazie all'ext è possibile sia sviluppare nuove portlet, sia modificare i comportamenti del core di Liferay senza però sovrascriverlo. Il fatto di mantenere separati ext environment e core, e quindi di non modificare quest'ultimo, garantiva di evitare problemi nell'upgrade di versione. Sebbene l'ext environment esista ancora nella versione più recente, le linee guida "dettate" dal team di Liferay per lo sviluppo di portlet ne sconsigliano l'utilizzo, a meno che non sia l'unico modo per raggiungere i nostri obiettivi.

Fatte queste premesse, come possiamo modificare/estendere il comportamento del portale o sviluppare nuove portlet? Per rispondere al primo quesito useremo gli hooks: tramite questi "ganci" potremo aggiungere nuove funzionalità al core o modificarne altre. Per risolvere il secondo quesito utilizzeremo i Portlet Plugin, il cui sviluppo è l'argomento trattato in questo articolo.

Creare un Portlet Plugin con l'SDK

Le portlet sono delle piccole web application che vivono dentro un portale. Nella versione 6 di Liferay possono essere deployate "a caldo" nel portale come plugin, evitando ogni volta il restart dello stesso. Un singolo plugin può contenere più portlet. Vediamo come realizzarne uno avvalendoci di un comodo strumento, l'SDK di Liferay.

Il Plugin Software Development Kit [2], d'ora in avanti semplicemente SDK, è un framework per la generazione di plugin (portlet, temi, hook, etc.) creato dal team di Liferay. È molto utile come punto di partenza poiche' questo genera la struttura di base di un Portlet Plugin e i file necessari per il deploy. Il plugin può essere scaricato dalla pagina di download del sito Liferay [7].

Una volta scompattato, è già pronto per essere utilizzato. A questo punto abbiamo due possibilità: utilizzare l'SDK da linea di comando oppure utilizzarlo tramite il Liferay IDE [3] che vedremo più avanti nell'articolo. Vediamo prima come utilizzarlo da linea di comando. Bastano due semplici passi:

  • dal terminale posizionarsi all'interno della cartella dove si è scompattato il plugin SDK, dentro la sotto-cartella portlets;
  • lanciare il comando create.bat su Windows o create.sh su Linux/Mac OS con i due argomenti richiesti: nome e titolo. Attenzione: il parametro nome non deve contenere spazi e al nome viene automaticamente aggiunto il suffisso -portlet.

Nel nostro esempio il nome sarà simple-example e il titolo Simple Example Portlet; lanciamo quindi lo script così come illustrato in figura 1:

create.bat simple-example "Simple Example Portlet"

Figura 1 - Creazione di un Portlet Plugin tramite lo script dell'SDK.

 

Se tutto è andato a buon fine riceveremo un messaggio di successo da Ant. Mostrando il contenuto della cartella corrente (DIR su Windows o ls su Linux/Mac OS) vediamo la nostra cartella simple-example-portlet. Qui dentro troviamo la struttura di cartelle e tutti i file necessari per il nostro Plugin. È già pronto per essere deployato sul portale poiche', infatti, al suo interno contiene già una portlet naturalmente "vuota", nel senso che non fa nulla a parte mostrare un messaggio standard che viene generato dall'SDK. Vediamo ora come creare un'altra portlet dentro il Plugin inserendo una semplice logica applicativa.

Liferay IDE

Liferay IDE è un plugin per Eclipse [4] sviluppato anch'esso dal team di Liferay. Sostanzialmente ci permette di fare quello che facciamo con il Plugin SDK, ma in maniera visuale. Per i dettagli d'installazione e configurazione si rimanda al wiki di Liferay [5]. Una volta installato vediamo come utilizzarlo per lo sviluppo del nostro portlet.

Prima di tutto importiamo dentro Eclipse il progetto vuoto che abbiamo creato con il Plugin SDK. Se abbiamo installato il plugin per Eclipse. possiamo importarlo direttamente come progetto Liferay. Scegliamo dal menu File la voce New e poi Other. Nella dialog scegliamo, sotto Liferay, l'opzione Liferay Plug-in Project e clicchiamo su Next (figura 2).

Figura 2 - Creazione di un nuovo progetto Portlet Plugin con Liferay IDE.

 

Nella seconda dialog clicchiamo su Import existing Liferay project... (figura 3).

Figura 3 - Importazione del progetto.

 

Nella terza e ultima dialog selezioniamo la directory in cui si trova il nostro progetto, il target runtime e clicchiamo su Finish (figura 4).

Figura 4 - Selezione della directory del Plugin Portlet da importare.

 

Ora nel nostro package explorer vedremo il progetto con la lettera "L" per icona: questo significa che si tratta, appunto, di un progetto Liferay.

Figura 5 - Ecco come si presenta il nostro progetto appena creato.

La struttura del progetto

Vediamo cosa contiene il nostro progetto:

  • /src: cartella con i sorgenti (quando il progetto è appena creato è vuota);
  • librerie: contengono tutti i JAR necessari a compilare il progetto e tutte le librerie per le API di Liferay;
  • /docroot: in questa cartella troviamo sia le cartelle che contengono CSS e JavaScript (vedremo successivamente dove sono referenziati), sia i file di configurazione.

Vediamo nel dettaglio questi elementi:

  • liferay-display.xml: qui diciamo al portale dove vogliamo che venga visualizzato la nostra portlet nel menù che ci consente di aggiungerla in pagina.
  • liferay-plugin-package.properties: contiene informazioni sul Plugin quali autore, versione, etc.
  • portlet.xml: deployment descriptor per le portlet che contiene descrittori e configurazioni e viene creato automaticamente dall'SDK; ci soffermiamo solo sull'elemento perche' è quello che andremo a modificare. Contiene la classe della nostra portlet che può contenere una di quelle di default o una custom: nel nostro caso useremo una custom che estende com.liferay.util.bridges.mvc.MVCPortlet. La sintassi di questo file è definita nelle Portlet Specification [6].
  • liferay-portlet.xml: estende le funzionalità fornite da portlet.xml ed è specifico di Liferay. Attenzione all'elemento : deve contenere lo stesso nome che c'è in dentro portlet.xml. In questo XML possiamo specificare i CSS e i JS propri della nostra portlet, quelli contenuti nella docroot di cui parlavamo sopra.
  • web.xml: descrittore della webapp.

Proviamo a effettuare il deploy della nostra portlet. Facciamo partire, se non l'abbiamo ancora fatto, il server sul quale gira Liferay. Una volta partito, facciamo il deploy della portlet con ant, target deploy. Se guardiamo il log del nostro server, dopo qualche secondo dovremmo leggere un messaggio del genere:

1 portlet  for simple-example-portlet is available for use

Apriamo un browser su http://localhost:8080/web/guest, effettuiamo il login (username test@liferay.com e password test sono le credenziali di default dell'amministratore) e aggiungiamo la nostra portlet in pagina. In figura 6 vediamo il risultato.

Figura 6 - La nostra portlet aggiunta in pagina.

Aggiungere una portlet al Plugin

A questo punto vediamo come creare un'altra portlet dentro il Plugin implementando una semplice logica di business. Anche per questa operazione utlizzeremo il wizard dell'IDE. Cliccando con il tasto destro sul nostro progetto facciamo New > Liferay Portlet. Nella dialog che si apre scegliamo un nome per la classe (MyFirstPortlet), il package e la superclasse che estende, com.liferay.util.bridges.mvc.MVCPortlet nel nostro caso.

Figura 7 - Creazione di una nuova portlet.

 

Facendo click su Next si aprirà la seconda dialog del wizard in cui possiamo specificare alcune opzioni quali nome, nome visualizzato e titolo della portlet. Inoltre possiamo specificare la cartella per le JSP e se creare il file per la customizzazione e localizzazione dei messaggi (Language.properties). Potremmo anche specificare i Portlet Mode (standard della specifica) o i Liferay Portlet Mode (di Liferay), argomenti che non tratteremo in questo articolo.

Figura 8 - Configurazione della portlet.

 

Cliccando su Finish, l'IDE genera tutti i file necessari per la nostra portlet: classe Java, file XML (in realtà non vengono generati, ma le nuove configurazioni vengono aggiunte ai file esistenti, quelli contenuti dentro WEB-INF). Se guardiamo la struttura del progetto vediamo che in src ora c'è la nostra classe MyFirstPortlet dentro il package it.dvel.portlet, dentro docroot/html troviamo myfirstportlet che a sua volta contiene la JSP che renderizza la view della portlet. Notiamo che l'elemento relativo alla nostra portlet dentro portlet.xml è

it.dvel.portlet.MyFirstPortlet

Ora che abbiamo la nostra portlet, dobbiamo decidere cosa fargli fare: in questo caso implementeremo due funzionalità. La prima recupera la lista di tutti gli utenti del portale e la mostra in pagina, la seconda mostra un dettaglio di ogni singolo utente. Vediamo come fare.

Posizioniamoci dentro la classe MyFirtsPortlet: come si può notare la classe è vuota ed estende MVCPortlet. Come prima cosa facciamo override del metodo

public void doView(RenderRequest renderRequest,RenderResponse renderResponse)
throws IOException, PortletException

Questo metodo viene invocato ogni volta che viene renderizzata la nostra portlet. Qui potremo scrivere il codice che, ad esempio, recupera degli oggetti che vogliamo mostrare in pagina ogni volta che visualizziamo la portlet. Questo è il codice:

@Override
       public void doView(RenderRequest renderRequest,
                    RenderResponse renderResponse) throws IOException, PortletException {
             ThemeDisplay themeDisplay = (ThemeDisplay) renderRequest
                           .getAttribute(WebKeys.THEME_DISPLAY);
             List userGroupList;
             try {
                    //Recupero la lista di tutti gli utenti della community corrente
                    //utilizzando le API del portale.
                    userGroupList = UserLocalServiceUtil.getGroupUsers(themeDisplay
                                  .getScopeGroupId());
             } catch (SystemException e) {
                    userGroupList = Collections.EMPTY_LIST;
             }
             //creazione della lista degli headers.
             //Saranno le intestazioni della tabella in cui mostriamo la lista
             List headers = new ArrayList();
             headers.add("first-name");
             headers.add("last-name");
             headers.add("");
             PortletURL portletURL = renderResponse.createRenderURL();
             //Creazione dell'oggetto SearchContainer
             SearchContainer sc = new SearchContainer(renderRequest,
                           null, null, SearchContainer.DEFAULT_CUR_PARAM, 5, portletURL,
                           headers, "Empty resultset");
             //Il SearchContainer avra' tanti elementi quanti sono gli utenti
             sc.setTotal(userGroupList.size());
            
             ResultRow row = null;
             //Ciclando sul SearchContainer creiamo tante righe
//con le informazioni degli utenti
             //che vogliamo mostrare in pagine.In questo caso
//mostriamo Nome, Cognome e un
             //tasto per invocare l'azione che mostra il dettaglio per l'utente
             for (int i = sc.getStart(); i < sc.getEnd() && i < sc.getTotal(); i++) {
                    boolean bold = false;
                    User item = userGroupList.get(i);
                    row = new ResultRow(item, item.getUserId(), i, bold);
                    row.addText(item.getFirstName());
                    row.addText(item.getLastName());
                    HttpServletRequest httpServletRequest
= (HttpServletRequest) renderRequest
                           .getAttribute(
"com.liferay.portal.kernel.servlet.PortletServletRequest");
                    HttpServletResponse httpServletResponse
= (HttpServletResponse) renderRequest
                           .getAttribute(
"com.liferay.portal.kernel.servlet.PortletServletResponse");
                    ServletConfig servletConfig
= (ServletConfig) renderRequest
                           .getAttribute(
"com.liferay.portal.kernel.servlet.PortletServletConfig");
                    //questa istruzione aggiunge alla riga
//l'inclusione di una jsp, che è quella che
                    //mostrera' il tasto per visualizzare il dettaglio dell'utente
                    row.addJSP("right", SearchEntry.DEFAULT_VALIGN,
                                  "/html/myfirstportlet/actions.jsp",
                                  servletConfig.getServletContext(), httpServletRequest,
                                  httpServletResponse);
                    sc.getResultRows().add(row);
             }
             //mettiamo in request l'oggetto SearchContainer
             renderRequest.setAttribute("searchContainer", sc);
             //invochiamo la doView della superclasse MVCPortlet.
//Questa mostrera' la jsp definita
             //nell'attributo con name="view-jsp" dentro portlet.xml
             super.doView(renderRequest, renderResponse);
       }

L'oggetto SearchContainer è molto utile nei casi in cui si voglia mostrare una lista di oggetti. Infatti una volta popolato, per mostrare i risultati in pagina bastano due sole righe nella JAP:

                                  scope="request"/>

Nella prima riga dichiariamo di voler usare l'oggetto searchContainer che ci aspettiamo di trovare in request; nella seconda utilizziamo una taglib di Liferay che si incarica di generare l'HTML che mostra la tabella con i risultati. In figura 9 possiamo vedere come appare la portlet una volta aggiunta in pagina.

Figura 9 - Visualizzazione della lista degli utenti dentro la portlet.

 

Vediamo come si renderizza l'icona per visualizzare il dettaglio. Abbiamo visto come nelle righe del SearchContainer abbiamo aggiunto una JSP, il cui codice è mostrato sotto:

<%
ResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);
User myUser = (User)row.getObject();
long groupId = themeDisplay.getLayout().getGroupId();
String primKey = String.valueOf(myUser.getUserId());
%>
      
            
      

       url="<%=viewsURL.toString() %>" />

Nella prima parte di codice Java, si recupera l'oggetto User dalla riga del SearchContainer. Nella seconda parte utilizziamo ancora le taglib portlet  per creare l'action URL con il quale è possibile invocare un'azione sulla portlet. In questo caso specifichiamo un solo parametro, resourcePrimKey, in cui mettiamo l'id dell'utente. Attenzione all'attributo name dentro portlet:actionURL: quello è la chiave grazie alla quale viene invocato il metodo corretto nella classe MyFirstPortlet. Infatti nella classe troviamo proprio il metodo implementato:

public void viewUserProfile(ActionRequest request, ActionResponse response) {
       //recuperiamo l'id dell'utente che ci serve per fare la get
       String primKey = ParamUtil.getString(request, "resourcePrimKey");
       if (primKey != null) {
             try {
                    User user = UserLocalServiceUtil.getUser(Long
                                  .parseLong(primKey));
                    //mettiamo in request l'oggetto che rappresenta l'utente
                    request.setAttribute("userDetail", user);
                    //dichiariamo quale jsp utilizzare per renderizzare
                    //il risultato
                    response.setRenderParameter("jspPage",
                                  "/html/myfirstportlet/view_user_detail.jsp");
             } catch (NumberFormatException e) {
                    SessionErrors.add(request, "failed-to-retrieve");
             } catch (PortalException e) {
                    SessionErrors.add(request, "failed-to-retrieve");
             } catch (SystemException e) {
                    SessionErrors.add(request, "failed-to-retrieve");
             }
       }
}

Questo non fa altro che prendere dalla request l'id dell'utente e, tramite le API di Liferay, recupera l'oggetto che rappresenta l'utente e lo mette in request. Infine imposta la JSP da utilizzare per la vista. Il risultato è mostrato in figura 10.

Figura 10 - Vista del dettaglio di un utente.

Conclusioni

Con questo articolo abbiamo visto come partire da zero per creare una portlet per l'ultima versione del Liferay Portal Server. Abbiamo illustrato gli strumenti e le facility che ci assistono in tutto il processo di sviluppo per creare i Portlet Plugin che, come abbiamo detto, possono essere deployati a caldo sul server. Questa possibilità migliora i tempi di sviluppo in quanto non è più necessario riavviare il server ad ogni deploy.

Per quanto riguarda l'implementazione abbiamo visto alcuni aspetti tecnici: l'utilizzo delle API di Liferay, le taglib per le pagine e il SearchContainer per le liste paginate. Si rimanda ai riferimenti [8] e [9] per approfondimenti su TagLib e SearchContainer. Imparare a utilizzare questi strumenti è un modo sicuro per velocizzare lo sviluppo e rendere più pulito il codice prodotto. Naturalmente l'argomento portlet è troppo vasto per essere trattato in un singolo articolo. Per dettagli e specifiche si consiglia di consultare i riferimenti sottostanti.

Riferimenti

[1] Liferay: il sito

http://www.liferay.com/

 

[2] Plugins Software Development Kit (SDK)

http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/the-plugins-sdk

 

[3] Liferay IDE

http://www.liferay.com/community/wiki/-/wiki/Main/Liferay+IDE

 

[4] Eclipse

http://www.eclipse.org

 

[5] Liferay IDE Installation

http://www.liferay.com/community/wiki/-/wiki/Main/Liferay+IDE+Getting+Started+Tutorial

 

[6] JSR-000286 Portlet Specification 2.0

http://jcp.org/aboutJava/communityprocess/pfd/jsr286/index.html

 

[7] L'indirizzo per il download del SDK per i plugin

http://www.liferay.com/downloads/liferay-portal/additional-files

 

[8] UI Taglib su Liferay

http://www.liferay.com/community/wiki/-/wiki/Main/Liferay+UI+Taglib

 

[9] SearchContainer su Liferay

http://www.liferay.com/community/wiki/-/wiki/Main/SearchContainer

 

Condividi

Pubblicato nel numero
160 marzo 2011
Lorenza Amato è nata a Chieti nel 1982. Si è laureata in Informatica presso l‘Università degli studi di Bologna. Dal 2009 svolge, presso la D‘vel Snc, l‘attività di sviluppatrice software in ambiente Java. In particolare si occupa di sviluppo di progetti per Liferay Portal Server.
Articoli nella stessa serie
Ti potrebbe interessare anche