X
    Categories: blog

Liferay, episodio 2

Le portlet sono un piccolo frammento di una pagina web, ma all’interno si apre un mondo…

In questo articolo cercheremo di capire come funzionano e proveremo a crearne una da zero.

Chi ha familiarità con le servlet thecnology, non avrà problemi con le portlet, in quanto sono molto simili ma con proprie caratteristiche. Tutto ruota intorno al client che nel nostro caso è il browser.

Principale differenza con le servlet è che quest’ultima ha un URL collegato e ogni pagina web è servita da una sola servlet, mentre le portlet possono essere molteplici in pagina e ogni portlet ha un proprio lifecycle e un proprio pattern URL.

Nell’immagine sopra viene illustrato il lifecycle delle portlet, che riassumiamo qui.

init(PortletConfig config):

Questo metodo è chiamato solo la prima volta quando istanziamo la portlet, cioè quando facciamo drag & drop nella pagina della portlet.

processAction(ActionRequest request, ActionResponse response):

Quando l’utente richiede qualcosa al server, cioè notifica alla portlet un’azione da fare, che possa essere una redirect, una settare dei parametri, persistere a db.

render(RenderRequest request, RenderResponse response):

La fase di render genera i contenuti che devono essere mostrati in pagina dal client. La portlet può produrre markup che possono dipendere dal portlet mode o window state, render parameters, request attributes, persistent state, session data, or backend data.

destroy ():

indica la fine del ciclo di vita della portlet, utile per refreshare risorse.

Ci sono tante altre cose che potremmo dire sulle portlet ma soffermiamoci ora sul pratico e sulla creazione di un primo progetto.

Come abbiamo visto nell’episodio precedente, una volta settato l’ambiente, partiamo da Eclipse per la creazione della portlet. Sul nostro svn abbiamo una portlet example da cui partire per un nuovo progetto con già l’init setup pronto.

Comunque vi mostro anche dall’IDE la schermata da scegliere per creare un progetto nuovo da zero, vi basta scegliere un plugin type Service Builder Portlet in modo da avere anche lo strato per la persistenza. I nostri progetti sono di tipo Maven, Liferay nativo è in ANT, per nostra infrastruttura scegliamo di seguire lo standard maven. Sulla destra potete vedere anche le altre possibili scelte oltre al Service Builder Portlet.

Estratto da svn, il progetto example, abbiamo nel workspace questa alberatura:

La portlet usa il pattern Model View Controller (MVC), per farlo usiamo i file context di Spring, che vi mostro nella guida, ma non approfondisco il concetto, perché ci vorrebbe una guida a parte.

  • example-contex.xml

Identifica il package che contiene le classi del controller e dove sono le jsp per la view.

  • portlet.xml

Definisce dove sono i file context ed altre proprietà di base della portlet

  • liferay-portlet.xml

Ogni portlet può avere propri css e js, qui si possono definire i percorsi o aggiungere altre dipendenze alla portlet.

  • liferay-display.xml

In questo file si definisce l’id della portlet e la categoria di appartenenza, nello stesso progetto plugin possono esserci anche più portlet che possono essere aggiunti negli stessi file, senza crearne altri.

  • service.xml

Il file service.xml lo vedremo nel prossimo articolo per dedicargli un po’ più attenzione.

Ora vediamo il controller, MainController.java

@Controller
@RequestMapping("VIEW")
public class MainController {
private static MaggioliLog systemLog =
MaggioliLogFactory.getSystemLog(MainController.class);
private static MaggioliLog applicationLog =
MaggioliLogFactory.getApplicationLog(MainController.class);

@ActionMapping(params = MVC_ACTION + "=" + ACTION_ADD )
public void handleActionRequest(ActionRequest request, ActionResponse response, Model model) {
String codice = ParamUtil.getString(request, "codice");
String descrizione = ParamUtil.getString(request, "descrizione");
applicationLog.info("Aggiungo un nuovo parametro con codice=" + codice + " descrizione=" + descrizione);
try {
ParametroLocalServiceUtil.aggiungiParametro(codice, descrizione);
} catch (SystemException e) {
systemLog.error("Errore durante l'aggiunta di un parametro con codice=" + codice + " descrizione=" + descrizione);
systemLog.debug(e);
}
}

@RenderMapping
public String handleViewRequest( RenderRequest request, RenderResponse response, Model model) {

MaggioliPortalContainer container = CrossContextContainer.get();
...

if(systemLog.isDebugEnabled())
systemLog.debug("render controller, codice ente: " + codEnte + ", user id: " + userId);

Map<String, Object> responseMap = new HashMap<String, Object>
ResponseBean resp = new ResponseBean();
ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);
PortletConfig portletConfig = (PortletConfig) request.getAttribute(JavaConstants.JAVAX_PORTLET_CONFIG);
PortletURL iteratorURL = PortletURLFactoryUtil.create(request, themeDisplay.getPortletDisplay().getId(), themeDisplay.getPlid(),
PortletRequest.RENDER_PHASE);

int cur = ParamUtil.getInteger(request, SearchContainer.DEFAULT_CUR_PARAM, 1);
int delta = ParamUtil.getInteger(request, SearchContainer.DEFAULT_DELTA_PARAM, 10);
String orderByCol = ParamUtil.getString(request, SearchContainer.DEFAULT_ORDER_BY_COL_PARAM, "descrizione");
String orderByType = ParamUtil.getString(request, SearchContainer.DEFAULT_ORDER_BY_TYPE_PARAM, "asc");

SearchContainer<Parametro> searchContainer = new SearchContainer<Parametro>(request, null, null, SearchContainer.DEFAULT_CUR_PARAM, delta,
iteratorURL, null, LanguageUtil.get(portletConfig, themeDisplay.getLocale(), "Nessun parametro presente"));
List<Parametro> parametri = new ArrayList<Parametro>();
try {
int start = cur-1;
int end = start + delta;
parametri = ParametroLocalServiceUtil.findParametri(start, end, orderByCol, orderByType);
} catch (SystemException e) {
systemLog.error("Errore durante il recupero della lista parametri");
systemLog.debug(e);
}

int count = parametri!=null?parametri.size():0;
searchContainer.setResults(parametri);
searchContainer.setTotal(count);
searchContainer.setOrderByCol(orderByCol);
searchContainer.setOrderByType(orderByType);
responseMap.put("results", parametri);
responseMap.put("total", count);
responseMap.put("searchContainer", searchContainer);
resp.setResponse( responseMap );
model.addAttribute(RESPONSE, resp);
return "view";
}
}

Nel controller, vediamo che ci sono due metodi uno per la render dell’interfaccia ed uno per eseguire la action presente nella view.

Il search container utilizzato è un componente nativo di liferay che serve per creare delle table nella view, e viene popolato da oggetti List. Il finder recupera dal db i valori dei Parametri e vengono passati in pagina. Nella action aggiungi vengono recuperati i parametri dalla form e salvati a db.

Ora vediamo la jsp di view come è composta

<%@page import="javax.portlet.PortletURL"%>
<%@page import="java.util.Map"%>
<%@page import="it.maggioli.informatica.portal.core.response.ResponseBean"%>
<%@page import="it.maggioli.informatica.portal.core.constants.MaggioliPortalCoreConstants"%>
<%@page import="it.maggioli.informatica.jcitygov.example.common.ExampleConstants" %>
<%@include file="init.jsp"%>
<%
	ResponseBean responseBean = (ResponseBean) request.getAttribute(MaggioliPortalCoreConstants.RESPONSE);
	@SuppressWarnings("unchecked")
	Map<String, Object> responseMap = (Map<String, Object>) responseBean.getResponse();
	pageContext.setAttribute("searchContainer", responseMap.get( "searchContainer"));
	pageContext.setAttribute("results", responseMap.get( "results"));
	pageContext.setAttribute("total", responseMap.get( "total"));
%>
<portlet:actionURL var="addUrl">
	<portlet:param name="<%=MaggioliPortalCoreConstants.MVC_ACTION %>" value="<%=ExampleConstants.ACTION_ADD %>"/>
</portlet:actionURL>
<portlet:actionURL var="iteratorURL">
</portlet:actionURL>

<aui:layout>
	<aui:form action="${addUrl}" method="POST">
		<aui:column columnWidth="25">
			<aui:input type="text" name="codice" label="label.codice" cssClass="filter-field"></aui:input>
		</aui:column>
		<aui:column columnWidth="25">
			<aui:input type="text" name="descrizione" label="label.descrizione" cssClass="filter-field"></aui:input>
		</aui:column>
		<aui:column columnWidth="25">
			<aui:button icon="icon-add" type="submit" cssClass="btn-filter" value="button.add" />
		</aui:column>
	</aui:form>
</aui:layout>

<liferay-ui:search-container searchContainer="${searchContainer}">
	<liferay-ui:search-container-results results="${results}" total="${total}" />
	<liferay-ui:search-container-row className="it.maggioli.informatica.jcitygov.example.model.Parametro" keyProperty="idParametro" modelVar="par">
		<liferay-ui:search-container-column-text name="Parametro" value="${par.idParametro}" orderable="true" orderableProperty="idParametro" />
		<liferay-ui:search-container-column-text name="Codice" value="${par.codice}" orderable="true" orderableProperty="codice" />
		<liferay-ui:search-container-column-text name="Descrizione" value="${par.descrizione}" orderable="true" orderableProperty="descrizione" />
	</liferay-ui:search-container-row>
	<liferay-ui:search-iterator paginate="<%=true%>" />
</liferay-ui:search-container>

Liferay ha nativo l’integrazione con AlloyUI, in più mette a disposizione dei tag liferay-ui che estendono delle funzionalità di AUI

Nella jsp sono presenti sia un esempio di form, che un esempio di search container. In alto il codice per recuperare i parametri passati dalla renderPhase.

Da console, ponendosi sotto la cartella jcitygov-example, dove si trova il pom.xml padre, dare il comando:

mvn clean liferay:buildservice install liferay:deploy

La compilazione genera il file jar dei servizi, il war da deployare ed effettua il deploy sull’application server.

Per aggiungere la portlet alla pagina di un sito basta, una volta loggati in liferay (come admin), cliccare su aggiungi, cercare l’applicazione example e fare drag & drop nella pagina della portlet

Vi mostro ora la la portlet prima del salvataggio di un parametro:

e dopo:

Nel prossimo episodio vedremo come funziona il service builder di liferay…stay tuned

Michele Russo: Partenopeo, ingegnere Informatico. Lavoro come Software Engineer in Maggioli dal 2016 per l'area Portali, nello specifico JcityGov, portale per i servizi al cittadino. Amo tutto quello che contiene bit. La mia vita privata resta privata ;)
Related Post