Nel nostro corso sulla implementazione di portali in Java tramite la portlet API, questo mese parliamo di alcune interessanti funzioni e di come la specifica si sta evolvendo verso la nuova release, la API 2.0 (JSR 286).
Prosegue la trattazione della Portlet API: questo mese affrontiamo alcune interessanti caratteristiche utili per la realizzazione di un portale e introduciamo quelle che che dovrebbero essere le nuove caratteristiche della prossima release (JSR 286, ovvero la Portlet API 2.0), di prossima pubblicazione.
Portlet TAG lib
Una portlet, come ogni altro componente che deve interagire con il client web (servlet, pagine JSP), deve produrre in risposta a una request del codice HTML che verrà poi renderizzato dal browser. Come si è avuto modo di vedere nelle puntate precedenti a parte la tecnica che prevede la stampa riga per riga del codice di risposta, una tecnica certamente più raffinata e potente è quella che prevede l‘inclusione di pagine JSP nella risposta da inviare al client. Per poter gestire in modo ancora più potente e automatizzato tali pagine, a partire dalla relese JSP 1.1 è possibile utilizzare librerie di tag custom che consentono sia di risparmiare tempo di stesura del codice, sia di automatizzare alcune operazioni ripetitive.
Anche nel mondo delle portlet è possibile sfruttare questa possibilità tanto che la specifica impone che ogni portlet container di fornire una implementazione della Portlet Tag Library.
Per utilizzare la tag library nella JSP che è inclusa nella portlet è necessario per prima cosa definire la libreria tramite apposito URI
<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
e poi procedere all‘utilizzo come si fa in una comune applicazione JSP based:
<%@ taglib uri="portlet.tld" prefix="portlet" %>...
Portlet 2 - Page 1
Portlet Webflow Test:
In genere i vari tag sono pensati per semplificare l‘accesso agli oggetti render/request o per automatizzare la creazione (e quindi l‘inserimento nella pagina) di portlet action URL.
Volendo fare una breve carrellata dei tag più utili e utilizzati è certamente utile partire dal tag
il quale è necessario per consentire l‘uso nella pagina JSP gli oggetti impliciti renderRequest, renderResponse e portletConfig.
Il tag
crea un action URL con il quale è possibile invocare una azione sulla portlet corrente: tale tag permette di specificare request type e parametri di invocazione:
Infine il tag
ha funzionamento analogo al tag precedente.
Le preferenze utente
Per quanto concerne l‘interazione con l‘utente, la specifica impone che ogni utente possa personalizzare l‘aspetto delle portlet, personalizzazione che viene poi mappata con un set di properties catalogate tramite un oggetto PortletPreferences.
La personalizzazione è resa persistente ed è carico del portal server il quale salva ogni modifica in modo trasparente e automatico agli occhi della portlet. Ogni utente, ovvero ogni sessione mantiene quindi una istanza personalizzata del properties: questo significa che non è questo lo strumento per imporre delle personalizzazione della applicazione che debbano essere rese attive per tutti gli utenti; per esempio un utente con ruolo di amministratore di una portlet non può usare le preferenze utente per impostare una visualizzazione per tutti gli utilizzatori, sebbene alcuni prodotti offrano funzioni proprietarie in tal senso.
Di seguito è riportato un piccolo esempio che mostra come utilizzare le proprietà utente per stampare username dopo un restart del server:
protected void doView(RenderRequest rRequest, RenderResponse rResponse) throws ...
response.setContentType("text/html");
String userName = (String)request.getPreferences().getValue("userName",null);
if (userName != null)
response.getWriter().println("Ciao" + userName);
else
response.getWriter().println("Vai in edit mode e inserisce il tuo username");
}
public void processAction(ActionRequest aRequest, ActionResponse aResponse) throws ...
String userName = aRequest.getParameter ("userName");
PortletPreferences pref = aRequest.getPreferences();
pref.setValue("userName",userName);
pref.store();
actionResponse.setPortletMode(PortletMode.VIEW);
}
Cache
La specifica definisce un meccanismo di cache temporanea che in base alla terminologia ufficiale prende il nome di “expiration-based caching mechanism” il cui significato dovrebbe essere piuttosto ovvio. Tale funzionalità è l‘intersezione dei domini per-portlet e per-client: la cache non può essere condivisa fra differenti utenti che visualizzano la stessa portlet, meno ancora se si tratta di portlet differenti. La portlet che vuole salvare in cache il proprio contenuto deve definire la durata della cache tramite il seguente pezzo di codice di configurazione XML:
300
Dal punto di vista del portlet container, il supporto per il caching non è obbligatorio e può essere disabilitato in ogni momento; se la cache è supportata si ha un indubbio beneficio per le prestazioni e in ogni caso è bene che il programmatore conosca questi aspetti (e sappia se il portlet container supporta la cache) per non incorrere in misteriosi bug.
Per capire il funzionamento e le implicazioni della cache si supponga di avere in una pagina la Portlet-A configurata per lavorare con una cache di 300 secondi. Nella stessa pagina si può immaginare che sia presente la Portlet-B sulla quale l‘utente esegue attivamente una qualche operazione (presumibilmente invocandone un actionURL), che prima o poi provoca un refresh della pagina. Il container può a tal punto scegliere di usare il contenuto in cache della Portlet-A invece di invocarne il metodo render(). Nel caso in cui si esegue una operazione (action request) su Portlet-A il container deve obbligatoriamente invalidare la cache e forzare la chiamata a render().
Un simpatico esempio (“simpatico” è una parola grossa…) può essere quello della SleepingBeautyPortlet (La Bella Addormentata) che come la protagonista della famosa fiaba, aspetta che il Principe Azzurro (in questo caso l‘utente) la risvegli dal profondo sonno in cui è caduta.
In questo caso l‘utente può cliccare infinite volte sul link di rendering (il quale a sua volta è il prodotto dell‘output in automatico) senza poter osservare alcun cambiamento (l‘output non viene aggiornato proprio perché il container fornisce sempre quello presente in cache). Non appena l‘utente clicca sul link associato alla actionURL allora si potrà notare un cambiamento nel testo stampato dalla portlet
public class SleepingBeautyPortlet extends GenericPortlet {
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
//stampa la data e ora corrente
String format = "EEE d MMM yyyy, hh:mm:ss aaa";
SimpleDateFormat df = new SimpleDateFormat(format);
writer.write("Ora e Data
");
writer.write(df.format(new Date()).toString());
writer.write("
Clicca qui per risvegliare la portlet");
}
public void processAction(ActionRequest actionRequest, ActionResponse actionResponse)
throws PortletException, IOException{
System.out.println("processAction(): enter");
}
}
Si noti come in questo caso il metodo processAction() non esegue alcuna operazione interessante dal punto di vista della business logic, ma serve solamente per permettere, con la sua esecuzione, di risvegliare la portlet dal profondo sonno (ovvero di consentire l‘aggiornamento della cache).
JSR 286, la Portlet API 2.0
La nuova release rappresenta un importante passo avanti, sottolineato anche dal nuovo numero di JSR: l‘integrazione con il mondo dei web services, un nuovo supporto per la sessione distribuita e condivisa, unitamente a nuove interessanti novità , sono certamente i punti più importanti della nuova versione. Purtroppo i molti ritardi nei rilasci e nelle pubblicazioni degli stati di avanzamento del gruppo di lavoro JCP fanno dubitare che ci sia un reale interesse e un forte impegno nel portare a conclusione la nuova API Portlet. A circa due anni dall‘annuncio, un paio di mesi fa è stato annunciato il risultalto finale della votazione della early draft 2.0 (presentata in luglio 2007). È presumibile che entro la fine dell‘anno (o al più tardi nel primo trimestre 2008), venga finalmente rilasciata la versione definitiva della specifica.
WSRP
Dal punto di vista dei contenuti la novità più importante è probabilmente l‘introduzione della specifica Web Service for Remote Portlet (WSRP). Tramite un interessante meccanismo di proxy-portlet stub, è possibile creare portlet che siano utilizzate contemporaneamente da più portali senza per questo impattare sulle prestazioni, dato che il core della portlet non verrà replicato per ogni client invocante.
L‘integrazione con il mondo dei web service in realtà comporta un‘altra importante conseguenza: adesso è possibile costruire e aggregare contenuti per portali ricavando le informazioni anche da provider non scritti in Java utilizzando il canale dei web service.
Si prenda ad esempio il caso in cui un portale debba fornire un servizio per informazioni finanziarie e un servizio di previsioni meteo: entrambi i servizi potrebbero essere disponili come Web Services deployati altrove (in un altro portal container) per cui può convenire inglobare tali servizi, affittarli, comprarli.
La soluzione classica che poteva essere adottata con la API 1.0 (JSR 168) era quella di creare due portlet che accedessero ai due servizi tramite WS e distribuire le portlet nei vari Portal Server secondo il bisogno. In questo scenario appare ovvio però che se si hanno n portali su cui effettuare il deploy delle portlet, si dovrà distribuire n volte le portlet negli n portali, con un ovvio aumento dei costi di amministrazione, di distribuzione e una maggiore complessità per l‘allineamento alle nuove release. Il costo è proporzionale al numero dei portali.
Figura 1 – Con la JSR 168, se si devono condividere portlet comuni, si deve effettuarne il deploy in ogni application server che ne abbia bisogno. Questo implica un aumento dei costi proporzionale al numero dei portali.
La WSRP portlet accede ai servizi, il cui deploy è stato effettuato altrove, tramite WS: ogni portlet è l‘interfaccia utente del servizio Web Services sottostante, in modo da limitare la complessità al crescere della architettura nel suo complesso.
Ogni portale che voglia utilizzare una remote portlet dovrà semplicemente istanziare un proxy verso la portlet: con n portali le portlet sono distribuite una sola volta, e solo i proxy saranno n.
Figura 2 – La nuova WSRP permette di realizzare portali che accedono a contenuti remoti senza che la complessità cresca in modo esponenziale. Ogni portlet è l‘interfaccia utente del servizio Web Services sottostante.
Questo nuovo modo di organizzare l‘architettura mantiene i costi di amministrazione, distribuzione e allineamento proporzionali al numero di servizi, non al numero di portali.
Resource Serving
Una interessante novità è rappresentata dalla possibilità di inviare al client risorse (oggetti, immagini, contenuti binari) che risiedono sul server protette in un qualche repository (tipicamente il file system).
Con la API attualmente utilizzabile (1.0) per poter ottenere questo risultato è necessario fornire una pagina JSP (o analogamente stampare le righe HTML da inviare al browser) in modo da consentire al client di cliccare su un link e scaricare la risorsa richiesta; per esempio
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
ResourceURL resURL = response.createResourceURL();
resURL.setResourceID("image");
// stampa HTML con il link all‘immagine da inviare al browser
writer.println("");
// altre operazioni di rendering
}
Questa tecnica, oltre a essere piuttosto scomoda per il client (l‘utente deve eseguire un ulteriore request per ottenere l‘immagine), è poco elegante e piuttosto rudimentale anche dal punto di vista del programmatore (in epoca di Web 2.0, stampare HTML è una operazione che nessun neo-assunto vuole fare).
Ecco come, con la JSR 286 si potrà ottenere questo risultato in modo certamente più elegante: la nuova GenericPortlet offre infatti un nuovo metodo da implementare che potrebbe essere “riempito” in questo modo:
public void serveResource(ResourceRequest resRequest, ResourceResponse resResp)
throws PortletException, IOException {
resResp.setContentType("image/png");
byte[] b = getImage("MyImage.PNG"); //Returns image bytes
resResp.getPortletOutputStream().write(b);
Nasce in questo caso il concetto di “resource URL” (o meglio di resourceRequest e resourceResponse) accanto agli action URL e ai render URL. Per maggiori approfondimenti si può far riferimento alla pagina [RS] del blog di Satya Ranjan, membro del JCP della API.
Portlet Filter
Dopo l‘introduzione delle WSRP, i Portlet Filter sono probabilmente la novità più importante introdotta con la nuova release: adesso è possibile creare portlet che intercettino il flusso delle operazioni in modo da gestire l‘interazione con sottosistemi o per controllare il flusso in operazioni complesse (p.e.: file upload). Prende il posto delle implementazioni “custom” che ogni programmatore ha effettuato.
Nuovo sistema di eventi
Una novità della quale al momento si sa ancora poco è la possibilità (molto potente sulla carta) di creare un sistema di eventi personalizzati (o comunque più ampio del set base) tramite i quali sia più semplice realizzare tecniche di inter portlet communication e portal communication. Tramite una semplice interfaccia da implementare (eventualmente il portale dovrebbe fornire helper classes come implementazioni concrete), si potrebbero gestire nuovi eventi (er il momento gli eventi standard sono pochi) come per esempio la possibilità di “sincronizzare” ascoltatori sull‘evento di logout (molto richiesto dalla comunità dei programmatori, come testimoniano alcune discussioni su blog in tema).
Per il momento non ho potuto fare prove approfondite su questo tema, in attesa di una reference implementation stabile e funzionante.
Condivisione di dati in sessione
È questo un tema quando mai caldo a giudicare dal confronto fra la documentazione ufficiale da un lato unitamente ai molti thread di discussione su blog e forum. Se da un lato i puristi dicono che questa possibilità dovrebbe essere proibita (il motivo più importante risiede nella necessità di mantenere le portlet indipendenti fra loro per migliorare le operazioni di refresh e computazione), dall‘altro in molti acclamano l‘arrivo di uno spazio condiviso dove “parcheggiare” dati da scambiare fra le varie portlet.
Di fatto questa possibilità apre nuove potenzialità ma anche costi computazionali e rischi di incosistenza nel presentation layer. Pare che la portlet shared session verrà introdotta nella API 2.0, ma non è chiaro nel dettaglio il suo funzionamento nà© il livello di supporto da parte delle varie implementazioni dei portal server. Resta da attendere un po‘ ancora per veder svelati gli ultimi dubbi.
Conclusioni
Come si è visto le recenti innovazioni introdotte con la nuova specifica dovrebbero in parte rivoluzionare il modo con il quale saranno implementate le portlet del futuro. Come accaduto per il rilascio della versione JSR 168 (API 1.0) anche la nuova specifica pare che abbia un percorso piuttosto travagliato. Dopo molto attendere pare che ormai manchi poco al gran momento. Se da un punto di vista enterprise l‘introduzione della WSRP dovrebbe far fare un gran balzo in avanti nella classifica delle API più importanti, mi immagino che nell‘uso di tutti i giorni molto peso avranno le novità relative alla nuova gestione degli eventi, alla sessione condivisa e alle altre novità puntuali, richieste a gran voce dai programmatori.
Personalmente, quale amante della semplicità e della facilità di utilizzo, gradirei una maggiore pressione da parte del comitato dello standard per imporre una qualche forma di standardizzazione degli strumenti di amministrazione e deploy che i vari portal server mettono a disposizione: se sviluppare una semplice portlet è una operazione relativamente poco costosa (in termini di tempo necessario per apprendere la teoria e implementare in pratica), spesso si deve spendere del tempo (a volte non indifferente) per apprendere il funzionamento del portal server scelto per la propria piattaforma. Purtroppo, da un punto di vista più realistico, credo che questo mio desiderio resti del tutto inatteso, vista la disomogeneità che si ha a livello di container Java EE e di conseguenza di portal server.
Non resta che affidarsi alla documentazione per imparare a domare questi strani animali, i portal server.
Riferimenti
[286] JSR 286: Portlet Specification 2.0
http://jcp.org/en/jsr/detail?id=2867
[PTL] Descrizione della Portlet Tag Library
http://edocs.bea.com/wlp/docs70/jsp/mngprtal.htm
[RS] New Feature: Resource Serving in JSR 286
http://blogs.sun.com/satya/entry/new_feature_resource_serving_in