Il ciclo di vita delle portlet
Il ciclo di vita di una portlet è piuttosto semplice e per molti aspetti analogo al corrispettivo ciclo delle servlet. Le varie fasi sono individuate dai tre seguenti stati:
1. caricamento delle classi e inizializzazione 2. gestione della request 3. distruzione della portlet
Di seguito sono analizzati i dettagli di questi tre punti.
Fase 1: caricamento delle classi e inizializzazione
Il meccanismo di base del caricamento di una portlet non è dissimile da quello adottato da ogni altra applicazione Java EE. Il container procede secondo le regole (e le priorità) dell'application server sul caricamento delle classi e delle librerie presenti all'interno dell'archivio di deploy della applicazione. La classe principale della portlet application e le classi di supporto sono caricate dal medesimo classloader in modo da condividere lo stesso spazio di indirizzamento. La inizializzazione della portlet avviene al momento dello startup della applicazione o del portal server (se specificato il parametro di autostart) o in alternativa al momento della prima invocazione da parte del client. In questa fase i valori delle variabili sono impostati ai loro valori di default: la specifica impone nella portlet la presenza di un costruttore di default, anche se il buon senso ci dice (come nel caso delle servlet) di non usare questo metodo per la inizializzazione della portlet ma piuttosto il metodo init() dove è disponibile il contesto di configurazione come parametro di invocazione:
public void init(PortletConfig config) throws PortletException, UnavailableException {
super.init(config);
String initParam = config.getInitParameter();
}
Durante la fase di inizializzazione una portlet può andare incontro a problemi di varia natura che ne possono pregiudicare o meno il funzionamento successivo; si possono verificare problemi temporanei (che con una qualche operazioni di recovery possono essere risolti agilmente) o incidenti definitivi (manca la connessione al database, un importante file di configurazione non viene trovato o un altro grave problema che pregiudica l'esecuzione della portlet). Il programmatore può notificare al portal server questi due casi lanciando le eccezioni PortletException o UnavailableException secondo il seguente schema:
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
String format = "EEE d MMM yyyy, hh:mm:ss aaa";
SimpleDateFormat df = new SimpleDateFormat(format);
writer.write("<h3>Ora e Data </h3>");
writer.write(df.format(new Date()).toString());
writer.write("<br>Clicca <A href=" + response.createActionURL().toString()
+ ">qui</a> per invocare la portlet");
}
Fase 2: gestione della request
Durante questa fase vengono invocati i metodi della portlet per la gestione delle richieste. Come già visto negli articoli precedenti, il container può invocare i metodi processAction() e doView(), doEdit() o doHelp() a seconda del tipo di richiesta.
Il PortletRequestDispatcher è l'oggetto che permette di includere risorse JSP o servlet nell'output della portlet. Si ottiene dal PortletContext tramite i metodi:
public PortletRequestDispatcher getNamedDispatcher(java.lang.String name) public PortletRequestDispatcher getRequestDispatcher(java.lang.String path)
La differenza fra i due consiste nella possibilità di ottenere una risorsa per nome (esempio utilizzando il nome della servlet) oppure per path relativo (il filename della JSP contestualizzato nella web application o l'URL di invocazione della servlet). L'oggetto in questione è simile alla RequestDispatcher di javax.servlet, anche se come detto non può eseguire forward, non può passare il controllo a servlet. Il solo metodo permesso è include:
public void include(RenderRequest request, RenderResponse response)
throws PortletException, IOException
Il suo utilizzo è veramente molto semplice:
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType("text/html");
PortletContext portletContext = getPortletContext();
PortletRequestDispatcher prd;
prd = portletContext.getRequestDispatcher("userFile.jsp");
prd.include(request, response);
}
Se si vogliono passare parametri alla risorsa inclusa, questi vanno passati nell'URL:
prd = portletContext.getRequestDispatcher("userFile.jsp?userId=" + userId);
Per quello che si è appena detto poco sopra, non è possibile includere l'output di altre portlet con il RequestDispatcher: il meccanismo infatti non permette di passare il controllo alla risorsa invocata e se volessimo ottenere l'output della portlet dovremmo passarle il controllo. Un modo per ovviare a questa limitazione è l'invocazione diretta dei metodi della portlet. Da notare che al momento (API 1.0) una portlet deve sempre fornire output testuale: se la JSP/servlet produce output binario (per esempio PDF, ZIP) si deve necessariamente produrre una pagina con il link della risorsa da scaricare; non si tenti di modificare il content-type della servlet. Da quanto riportato nelle specifiche preliminari, nella prossima release sarà eliminata questa restrizione.
La gestione della sessione
La gestione della sessione nelle portlet è simile a quella delle servlet, ma con alcune differenze importanti. L'oggetto PortletSession fornisce i seguenti metodi:
public java.lang.Object getAttribute(String name, int scope) public java.util.Enumeration getAttributeNames() public java.util.Enumeration getAttributeNames(int scope) public java.lang.Object getAttribute(String name) public void removeAttribute(String name) public void removeAttribute(String name, int scope) public void setAttribute(String name, Object value) public void setAttribute(String name, Object value, int scope)
Gli scope possibili sono:
PortletSession.PORTLET_SCOPE: visibile solo alla portlet correntePortletSession.APPLICATION_SCOPE: visibile a tutte le portlet della stessa applicazione web
Esempio di utilizzo per la comunicazione tra portlet:
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException {
PortletSession session = request.getPortletSession(true);
String uname = request.getParameter("uname");
session.setAttribute("uname", uname, PortletSession.APPLICATION_SCOPE);
}
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType("text/html");
Writer writer = response.getWriter();
PortletSession session = request.getPortletSession(true);
String uname;
uname = (String)session.getAttribute("uname", PortletSession.APPLICATION_SCOPE);
// usa la variabile se diversa da null per stampare l'output della portlet
}
Fase 3: distruzione della portlet
Quando il portal server decide di rimuovere una portlet dalla memoria (tipicamente allo shutdown del server o al redeploy dell'applicazione), viene invocato il metodo destroy(). Questo metodo permette alla portlet di rilasciare eventuali risorse allocate durante la fase di inizializzazione, come connessioni a database, file aperti, thread attivi, ecc.
Conclusioni
In questo articolo abbiamo approfondito il ciclo di vita delle portlet, la gestione delle sessioni e i meccanismi di inclusione di risorse. Nei prossimi articoli vedremo altri aspetti avanzati della programmazione delle portlet, come la gestione degli eventi e la personalizzazione dell'interfaccia.
