MokaByte 92 - Gennaio 2005
Jetspeed 2
I parte: scopriamo l'architettura di Jetspeed2
di
Massimiliano Dessì
 
Jetspeed2 della Apache Foundation sposta in avanti i riferimenti dei Portal Server jsr-168 compliant, basandosi su Spring e l'Inversion of Control, sui test e su molte altre innovative caratteristiche.

Introduzione
Andiamo a scoprire l'architettura di Jetspeed2 di cui è stata rilasciata la prima Milestone.
La seconda e la terza Milestone apporteranno gli strumenti di amministrazione e di personalizzazione della parte di presentazione, perciò la Milestone 1 è pressoché definitiva in quanto ad architettura, è inoltre già funzionante.
Jetspeed2 è aderente alle specifiche jsr-168 e integrato con Pluto, ha molte caratteristiche che lo distinguono dagli altri Portal Server, soprattutto in quanto ad architettura, basata su Spring, ha una numerosa lista di features che lo rendono anche un ottimo framework su cui costruire Portali Enterprise, grazie anche ai numerosi test che accompagnano le sue classi e che certificano la sua robustezza.

 

Featurues
Ecco una lista sintetica delle caratteristiche del nuovo prodotto

  • JSR 168 Support
  • Component Architecture (Spring)
  • J2EE Security, Portal Security Policy
  • Page and Portal Security Constraints (Bronco)
  • PSML/Folder CMS Navigations, Menus, Links
  • Jetspeed SSO
  • Multithreaded Aggregation Engine
  • Full Localization Support
  • Pipeline Request Processor
  • Rules-based Profiler
  • Basic Page Customization
  • Auto Deployment of Portlet Applications
  • Auto Deployment of Layouts and Decorators
  • Advanced Password Features
  • Navigational State Component
  • Integration with Pluto Portlet Container
  • Tigris CSS
  • Client Indepedent Capability Engine (HTML, XHTML, WML, VML....)
  • Portals Bridges
    • Struts
    • MyFaces
    • Velocity
    • Perl
    • PHP
    • Bridges Framework
  • Admin Portlets
    • Portlet Application Manager
    • Profiler Admin
    • Role Manager
    • Portal Site Manager
    • User Manager
  • Sample Portlets
    • XSLT RSS
    • Rome RSS
    • WebContent
    • IFrame
    • Calendar
    • Bookmark
  • Tree Navigator

Per quanto riguarda le portlet da menzionare:

  • demo contiene alcune portlet dimostrative "convenzionali"
  • jsf-demo contiene una portlet che utilizza le applicazioni dimostrative delle java server faces,
  • pam (pluggable authentication module) è un applicazione che viene abilitata con l' uso di JAAS (Java Authentication and Authorization Service) per autenticare un utente. L' uso di pam permette di astrarre la tecnologia di autenticazione alla applicazione che richiede l' autenticazione.
  • perl permette l' integrazione di applicazioni scritte in perl ( se usato su windows, deve essere presente l' eseguibile per il perl )
  • php permette l' integrazione di applicazioni scritte in php ( se usato su windows, deve essere presente l' eseguibile per il php)
  • rss (rich site sindication) permette la pubblicazione di informazioni di siti remoti che espongono le loro informazioni via xml.
  • security contiene l' applicazione che permette l' autenticazione e l' autorizzazione dell' utente

Da sottolineare che la presenza di Spring porta in dote a Jetspeed anche l'AOP (Aspect oriented Programming) e una facile integrazione con i JDO e Hibernate, e nel caso lo si richieda , anche l' uso dei protocolli Hessian e Burlap.
Di tutte queste features vedremo per prime in questo primo articolo come vengono supportate le specifiche jsr168, come viene realizzata la Pipeline Request Processor con Spring e come sono stati modificati i file psml rispetto alle versioni 1.x di Jetspeed.


Architettura Pipeline Request
Le richieste vengono ricevute dalla servlet Jetspeed (non viene più usato turbine), che provvede nel metodo init a inizializzare l'engine usando le classi AbstractEngine e SpringEngine.


Figura 1
- Architettura Pipeline Request
(clicca sull'immagine per ingrandire)

Durante l' inizializzazione dell' engine, viene istanziata anche la pipeline con le valve definite nel file pipeline.xml.
Queste inizializzazioni vengono effettuate da Spring con tutte le definizioni dei bean contenute nei files xml dentro la directory WEB-INF/assembly.
In questo modo si può costruire una catena di elaborazione modulare e configurabile, anziché operare in maniera programmatica.
La classe deputata al controllo dei passaggi per questa pipeline è la classe JetspeedPipeline,ogni valve finita la propria esecuzione chiama il metodo invokeNext() per passare l' elaborazione alla valve successiva.Di seguito è riportato il codice che sta alla base di tutto.


/*
* Copyright 2000-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jetspeed.pipeline;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jetspeed.pipeline.descriptor.PipelineDescriptor;
import org.apache.jetspeed.pipeline.valve.Valve;
import org.apache.jetspeed.pipeline.valve.ValveContext;
import org.apache.jetspeed.request.RequestContext;

/**
* Flexible implementation of a {@link org.apache.jetspeed.pipeline.Pipeline}.
*
* <br/><br/>
* Suggested order of valves:
* <ul>
* <li>ContainerValve</li>
* <li>CapabilityValve</li>
* <li>UserProfilerValve</li>
* <li>PageProfilerValve</li>
* <li>ActionValve</li>
* <li>LayoutValve</li>
* <li>ContentValve</li>
* <li>AggregateValve</li>
* <li>CleanupValve</li>
* </ul>
*
* @author <a href="mailto:jason@zenplex.com">Jason van Zyl</a>
* @author <a href="mailto:david@bluesunrise.com">David Sean Taylor</a>
* @version $Id: JetspeedPipeline.java,v 1.4 2004/08/01 23:17:25 weaver Exp $
*/
public class JetspeedPipeline
implements Pipeline, ValveContext
{
/** Logger */
private Log log = LogFactory.getLog(JetspeedPipeline.class);

/** Name of this pipeline. */
protected String name;

/** The set of Valves associated with this Pipeline. */
protected Valve[] valves = new Valve[0];

/**
* The per-thread execution state for processing through this
* pipeline. The actual value is a java.lang.Integer object
* containing the subscript into the <code>values</code> array, or
* a subscript equal to <code>values.length</code> if the basic
* Valve is currently being processed.
*
*/
protected ThreadLocal state = new ThreadLocal();

/**
* Descriptor for this pipeline
*/
protected PipelineDescriptor descriptor;

/**
* Constructor that provides the descriptor for building
* the pipeline
*/
public JetspeedPipeline(String name, List valveList)
throws Exception
{
valves = (Valve[]) valveList.toArray(new Valve[valveList.size()]);
setName(name);
}

/**
* @see org.apache.plexus.summit.Pipeline#init()
*/
public void initialize()
throws PipelineException
{
}

/**
* Set the name of this pipeline.
*
* @param name Name of this pipeline.
*/
public void setName(String name)
{
this.name = name;
}

/**
* Get the name of this pipeline.
*
* @return String Name of this pipeline.
*/
public String getName()
{
return name;
}

/**
* @see org.apache.plexus.summit.Pipeline#addValve(Valve)
*/
public void addValve(Valve valve)
{
// Add this Valve to the set associated with this Pipeline
synchronized (valves)
{
Valve[] results = new Valve[valves.length + 1];
System.arraycopy(valves, 0, results, 0, valves.length);
results[valves.length] = valve;
valves = results;
}
}

/**
* @see org.apache.plexus.summit.Pipeline#getValves()
*/
public Valve[] getValves()
{
synchronized (valves)
{
Valve[] results = new Valve[valves.length];
System.arraycopy(valves, 0, results, 0, valves.length);
return results;
}
}

/**
* @see org.apache.plexus.summit.Pipeline#removeValve(Valve)
*/
public void removeValve(Valve valve)
{
synchronized (valves)
{
// Locate this Valve in our list
int index = -1;
for (int i = 0; i < valves.length; i++)
{
if (valve == valves[i])
{
index = i;
break;
}
}
if (index < 0)
{
return;
}

// Remove this valve from our list
Valve[] results = new Valve[valves.length - 1];
int n = 0;
for (int i = 0; i < valves.length; i++)
{
if (i == index)
{
continue;
}
results[n++] = valves[i];
}
valves = results;
}
}

/**
* @see org.apache.plexus.summit.Pipeline#invoke(RequestContext)
*/
public void invoke(RequestContext request)
throws PipelineException
{
// Initialize the per-thread state for this thread
state.set(new Integer(0));

// Invoke the first Valve in this pipeline for this request
invokeNext(request);
}

/**
* @see org.apache.plexus.summit.ValveContext#invokeNext(RequestContext)
*/
public void invokeNext(RequestContext request)
throws PipelineException
{
// Identify the current subscript for the current request thread
Integer current = (Integer) state.get();
int subscript = current.intValue();

if (subscript < valves.length)
{
// Invoke the requested Valve for the current request
// thread and increment its thread-local state.
state.set(new Integer(subscript + 1));
valves[subscript].invoke(request, this);
}
}
}

 

Pipeline.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
Copyright 2004 The Apache Software Foundation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<beans>
<bean id="localizationValve"
class="org.apache.jetspeed.localization.impl.LocalizationValveImpl"
init-method="initialize"
/>

<bean id="capabilityValve"
class="org.apache.jetspeed.capabilities.impl.CapabilityValveImpl"
init-method="initialize"
>
<constructor-arg>
<ref bean="org.apache.jetspeed.capabilities.Capabilities" />
</constructor-arg>
</bean>

<bean id="portalURLValve"
class="org.apache.jetspeed.container.url.impl.PortalURLValveImpl"
init-method="initialize"
/>

<bean id="securityValve"
class="org.apache.jetspeed.security.impl.SecurityValveImpl"
init-method="initialize"
>
<constructor-arg>
<ref bean="org.apache.jetspeed.profiler.Profiler" />
</constructor-arg>
<constructor-arg>
<ref bean="org.apache.jetspeed.security.UserManager" />
</constructor-arg>
</bean>

<bean id="passwordCredentialValve"
class="org.apache.jetspeed.security.impl.PasswordCredentialValveImpl"
init-method="initialize"
>
<constructor-arg>
<!-- expirationWarningDays -->
<list>
<value>2</value>
<value>3</value>
<value>7</value>
</list>
</constructor-arg>
</bean>

<bean id="profilerValve"
class="org.apache.jetspeed.profiler.impl.ProfilerValveImpl"
init-method="initialize"
>
<constructor-arg>
<ref bean="org.apache.jetspeed.profiler.Profiler" />
</constructor-arg>
<constructor-arg>
<ref bean="org.apache.jetspeed.page.PageManager" />
</constructor-arg>
</bean>

<bean id="containerValve"
class="org.apache.jetspeed.container.ContainerValve"
init-method="initialize"
/>

<bean id="actionValve"
class="org.apache.jetspeed.pipeline.valve.impl.ActionValveImpl"
init-method="initialize"
>
<constructor-arg>
<ref bean="org.apache.pluto.PortletContainer" />
</constructor-arg>
</bean>

<bean id="portletValve"
class="org.apache.jetspeed.aggregator.PortletValve"
init-method="initialize"
>
<constructor-arg>
<ref bean="org.apache.jetspeed.aggregator.PortletAggregator"/>
</constructor-arg>
</bean>

<bean id="aggregatorValve"
class="org.apache.jetspeed.aggregator.AggregatorValve"
init-method="initialize"
>
<constructor-arg>
<ref bean="org.apache.jetspeed.aggregator.PageAggregator"/>
</constructor-arg>
</bean>

<bean id="cleanUpValve"
class="org.apache.jetspeed.pipeline.valve.impl.CleanupValveImpl"
init-method="initialize"
>
</bean>

<bean id="jetspeed-pipeline"
class="org.apache.jetspeed.pipeline.JetspeedPipeline"
init-method="initialize"
>
<constructor-arg>
<value>JetspeedPipeline</value>
</constructor-arg>
<constructor-arg>
<list>
<ref bean="localizationValve"/>
<ref bean="capabilityValve"/>
<ref bean="portalURLValve"/>
<ref bean="securityValve"/>
<ref bean="passwordCredentialValve"/>
<ref bean="profilerValve"/>
<ref bean="containerValve"/>
<ref bean="actionValve"/>
<ref bean="aggregatorValve"/>
<ref bean="cleanUpValve"/>
</list>
</constructor-arg>
</bean>

<bean id="action-pipeline"
class="org.apache.jetspeed.pipeline.JetspeedPipeline"
init-method="initialize"
>
<constructor-arg>
<value>ActionPipeline</value>
</constructor-arg>
<constructor-arg>
<list>
<ref bean="localizationValve"/>
<ref bean="capabilityValve"/>
<ref bean="portalURLValve"/>
<ref bean="containerValve"/>
<ref bean="actionValve"/>
</list>
</constructor-arg>
</bean>

<bean id="portlet-pipeline"
class="org.apache.jetspeed.pipeline.JetspeedPipeline"
init-method="initialize"
>
<constructor-arg>
<value>PortletPipeline</value>
</constructor-arg>
<constructor-arg>
<list>
<ref bean="localizationValve"/>
<ref bean="capabilityValve"/>
<ref bean="portalURLValve"/>
<ref bean="containerValve"/>
<ref bean="portletValve"/>
</list>
</constructor-arg>
</bean>
</beans>

Ogni Valve implementa l' interfaccia valve che ha il metodo astratto

invoke(RequestContext request, ValveContext context)

in pratica a tutte le valve che implementano questa classe al momento della invocazione ricevono un RequestContext che come si può immaginare dal nome , contiene tutte le informazioni che sono necessarie per l' elaborazione, un RequestContext è associato a ciascuna request.


Figura 2
- Pipeline

(clicca sull'immagine per ingrandire)

 


Figura 3
- Pipeline

 


Figura 4
- Pipeline
(clicca sull'immagine per ingrandire)

 

Portlet Container - Portal Server
L' adozione delle specifiche jsr-168 ha comportato la divisione funzionale tra chi gestisce il ciclo di vita delle portlet (portlet container) e chi assembla le portlet in delle pagine (Portal Server).
In Jetspeed 1.x non vi era una separazione logico funzionale di questi due aspetti, in Jetspeed 2.x invece questo avviene, il portlet container (Pluto) si occupa del ciclo di vita delle portlet, mentre i "fragment", ossia le porzioni di html,wmlo xhtml prodotti dalle portlet vengono assemblati dal Portal server (Jetspeed2), che si occupa della navigazione, delle sicurezza, della personalizzazione e di tutte le altre funzioni che non siano direttamente riconducibili alla vita delle portlet.
Perciò le richieste arrivano al Portal Server che inoltra queste richieste al Portlet Container che si occupa di fornire un ambiente per l' esecuzione delle portlet, per il loro funzionamento e si occupa di invocarle per rispondere al Portal Server. Il portlet Container, si occupa anche di associare delle "Preferences" a ciascuna portlet in esecuzione, che le portlet possono sia leggere sia modificare, e che consentono la customizzazione.

 

Conseguenze delle specifiche jsr168:
Andiamo ora a vedere nella pratica cosa ha comportato la standardizzazione delle portlet,
le portlet si sono avvicinate alle servlet in quanto a funzionamento, ma si distinguono per i seguenti aspetti:

  • Le portlet generano solo "fragments", le servlet generano le pagine intere
  • Le portlet non possono essere chiamate direttamente dall' url
  • Le portlet ricevono due tipi di richiesta, ACTION o RENDER che hanno i rispettivi url generati da Pluto e disponibili con delle Tag Library.
    In una pagina, viene eseguita per primo il processAction della porta chiamata e in seguito i metodi render delle altre portlet nella pagina.
  • Le portlet hanno degli "stati" in cui possono trovarsi e che definiscono le operazioni che esse compiono, questi stati sono anche essi standardizzati (NORMAL, MAXIMIZED, MINIMIZED), ci sono anche delle modalità in cui lavora la portlet, e anche essi sono standardizzati (EDIT, VIEW, HELP)
  • Le portlet hanno accesso alle informazioni sugli utenti in maniera maggiore di quanto lo abbiano le servlet
  • le portlet possono creare link che sono indipendenti dal portal server
  • Le portlet hanno due session scope in cui conservare le informazioni, Application e Portlet
  • Le portlet invece non possono:
  • Modificare gli header HTTP
  • Non hanno accesso all' url usato dal client usato inizialmente per chiamare il Portal Server

Il risultato delle specifiche è che baseremo le nostre portlet su classi come questa GenericPortlet:

/*
* Copyright 2003,2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ====================================================================
*
* This source code implements specifications defined by the Java
* Community Process. In order to remain compliant with the specification
* DO NOT add / change / or delete method signatures!
*/

package javax.portlet;

/**
* The <CODE>GenericPortlet</CODE> class provides a default implementation
* for the <CODE>Portlet</CODE> interface.
* <p>
* It provides an abstract class to be subclassed to create portlets. A
* subclass of <CODE>GenericPortlet</CODE> should override at least
* one method, usually one of the following:
* <ul>
* <li>processAction, to handle action requests</li>
* <li>doView, to handle render requests when in VIEW mode</li>
* <li>doEdit, to handle render requests when in EDIT mode</li>
* <li>doHelp, to handle render request when in HELP mode</li>
* <li>init and destroy, to manage resources that are held for the life of
* the servlet</li>
* </ul>
* <p>
* Normally there is no need to override the render or the doDispatch
* methods. Render handles render requests setting the title of the
* portlet in the response and invoking doDispatch. doDispatch dispatches
* the request to one of the doView, doEdit or doHelp method depending on
* the portlet mode indicated in the request.
* <p>
* Portlets typically run on multithreaded servers, so please note that a
* portlet must handle concurrent requests and be careful to synchronize
* access to shared resources. Shared resources include in-memory data
* such as instance or class variables and external objects such as
* files, database connections, and network connections.
*/
public abstract class GenericPortlet implements Portlet, PortletConfig
{
private transient PortletConfig config;

/**
* Does nothing.
*/
public GenericPortlet()
{
}


/**
* Called by the portlet container to indicate to a portlet that the
* portlet is being placed into service.
* <p>
* The default implementation just stores the <code>PortletConfig</code>
* object.
* <p>The portlet container calls the <code>init</code>
* method exactly once after instantiating the portlet.
* The <code>init</code> method must complete successfully
* before the portlet can receive any requests.
*
* <p>The portlet container cannot place the portlet into service
* if the <code>init</code> method does one of the following:
* <ol>
* <li>it throws a <code>PortletException</code>
* <li>it does not return within a time period defined by the Web server
* </ol>
*
*
* @param config a <code>PortletConfig</code> object
* containing the portlet
* configuration and initialization parameters
*
* @exception PortletException if an exception has occurred that
* interferes with the portlet normal
* operation.
* @exception UnavailableException if the portlet cannot perform the initialization at this time.
*/
public void init (PortletConfig config) throws PortletException
{
this.config = config;
this.init();
}


/**
*
* A convenience method which can be overridden so that there's no need
* to call <code>super.init(config)</code>.
*
* <p>Instead of overriding {@link #init(PortletConfig)}, simply override
* this method and it will be called by
* <code>GenericPortlet.init(PortletConfig config)</code>.
* The <code>PortletConfig</code> object can still be retrieved via {@link
* #getPortletConfig}.
*
* @exception PortletException if an exception has occurred that
* interferes with the portlet normal
* operation.
* @exception UnavailableException if the portlet is unavailable to perform init
*/
public void init() throws PortletException
{
}


/**
* Called by the portlet container to allow the portlet to process
* an action request. This method is called if the client request was
* originated by a URL created (by the portlet) with the
* <code>RenderResponse.createActionURL()</code> method.
* <p>
* The default implementation throws an exception.
*
* @param request
* the action request
* @param response
* the action response
* @exception PortletException
* if the portlet cannot fulfilling the request
* @exception UnavailableException
* if the portlet is unavailable to process the action at this time
* @exception PortletSecurityException
* if the portlet cannot fullfill this request because of security reasons
* @exception java.io.IOException
* if the streaming causes an I/O problem
*/
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException
{
throw new PortletException("processAction method not implemented");
}


/**
* The default implementation of this method sets the title
* using the <code>getTitle</code> method and invokes the
* <code>doDispatch</code> method.
*
* @param request
* the render request
* @param response
* the render response
*
* @exception PortletException
* if the portlet cannot fulfilling the request
* @exception UnavailableException
* if the portlet is unavailable to perform render at this time
* @exception PortletSecurityException
* if the portlet cannot fullfill this request because of security reasons
* @exception java.io.IOException
* if the streaming causes an I/O problem
*
*/
public void render(RenderRequest request, RenderResponse response)
throws PortletException, java.io.IOException
{
response.setTitle(getTitle(request));
doDispatch(request, response);
}

/**
* Used by the render method to get the title.
* <p>
* The default implementation gets the title from the ResourceBundle
* of the PortletConfig of the portlet. The title is retrieved
* using the 'javax.portlet.title' resource name.
* <p>
* Portlets can overwrite this method to provide dynamic
* titles (e.g. based on locale, client, and session information).
* Examples are:
* <UL>
* <LI>language-dependant titles for multi-lingual portals
* <LI>shorter titles for WAP phones
* <LI>the number of messages in a mailbox portlet
* </UL>
*
* @return the portlet title for this window
*/
protected java.lang.String getTitle(RenderRequest request)
{
return config.getResourceBundle(request.getLocale()).getString(
"javax.portlet.title");
}


/**
* The default implementation of this method routes the render request
* to a set of helper methods depending on the current portlet mode the
* portlet is currently in.
* These methods are:
* <ul>
* <li><code>doView</code> for handling <code>view</code> requests
* <li><code>doEdit</code> for handling <code>edit</code> requests
* <li><code>doHelp</code> for handling <code>help</code> requests
* </ul>
* <P>
* If the window state of this portlet is <code>minimized</code>, this
* method does not invoke any of the portlet mode rendering methods.
* <p>
* For handling custom portlet modes the portlet should override this
* method.
*
* @param request
* the render request
* @param response
* the render response
*
* @exception PortletException
* if the portlet cannot fulfilling the request
* @exception UnavailableException
* if the portlet is unavailable to perform render at this time
* @exception PortletSecurityException
* if the portlet cannot fullfill this request because of security reasons
* @exception java.io.IOException
* if the streaming causes an I/O problem
*
* @see #doView(RenderRequest, RenderResponse)
* @see #doEdit(RenderRequest, RenderResponse)
* @see #doHelp(RenderRequest, RenderResponse)
*/
protected void doDispatch(RenderRequest request, RenderResponse response)
throws PortletException, java.io.IOException
{
WindowState state = request.getWindowState();
if (!state.equals(WindowState.MINIMIZED))
{
PortletMode mode = request.getPortletMode();
if (mode.equals(PortletMode.VIEW))
{
doView(request, response);
}
else
{
if (mode.equals(PortletMode.EDIT))
{
doEdit(request, response);
}
else
if (mode.equals(PortletMode.HELP))
{
doHelp(request, response);
}
else
{
throw new PortletException("unknown portlet mode: " + mode);
}
}
}

}

/**
* Helper method to serve up the mandatory <code>view</code> mode.
* <p>
* The default implementation throws an exception.
*
* @param request
* the portlet request
* @param response
* the render response
*
* @exception PortletException
* if the portlet cannot fulfilling the request
* @exception UnavailableException
* if the portlet is unavailable to perform render at this time
* @exception PortletSecurityException
* if the portlet cannot fullfill this request because of security reasons
* @exception java.io.IOException
* if the streaming causes an I/O problem
*
*/
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, java.io.IOException
{
throw new PortletException("doView method not implemented");
}

/**
* Helper method to serve up the <code>edit</code> mode.
* <p>
* The default implementation throws an exception.
*
* @param request
* the portlet request
* @param response
* the render response
*
* @exception PortletException
* if the portlet cannot fulfilling the request
* @exception UnavailableException
* if the portlet is unavailable to perform render at this time
* @exception PortletSecurityException
* if the portlet cannot fullfill this request because of security reasons
* @exception java.io.IOException
* if the streaming causes an I/O problem
*
*/
protected void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, java.io.IOException
{
throw new PortletException("doEdit method not implemented");
}

/**
* Helper method to serve up the <code>help</code> mode.
* <p>
* The default implementation throws an exception.
*
* @param request
* the portlet request
* @param response
* the render response
*
* @exception PortletException
* if the portlet cannot fulfilling the request
* @exception UnavailableException
* if the portlet is unavailable to perform render at this time
* @exception PortletSecurityException
* if the portlet cannot fullfill this request because of security reasons
* @exception java.io.IOException
* if the streaming causes an I/O problem
*/
protected void doHelp(RenderRequest request, RenderResponse response)
throws PortletException, java.io.IOException
{
throw new PortletException("doHelp method not implemented");
}

/**
* Returns the PortletConfig object of this portlet.
*
* @return the PortletConfig object of this portlet
*/
public PortletConfig getPortletConfig()
{
return config;
}

/**
* Called by the portlet container to indicate to a portlet that the portlet
* is being taken out of service.
* <p>
* The default implementation does nothing.
*
*/
public void destroy()
{
// do nothing
}

//-------------------------------------------------------------------------
// implement PortletConfig
//-------------------------------------------------------------------------
/**
* Returns the name of this portlet.
*
* @return the portlet name
*
* @see PortletConfig#getPortletName()
*/
public String getPortletName()
{
return config.getPortletName();
}

/**
* Returns the <code>PortletContext</code> of the portlet application
* the portlet is in.
*
* @return the portlet application context
*/
public PortletContext getPortletContext()
{
return config.getPortletContext();
}

/**
* Gets the resource bundle for the given locale based on the
* resource bundle defined in the deployment descriptor
* with <code>resource-bundle</code> tag or the inlined resources
* defined in the deployment descriptor.
*
* @return the resource bundle for the given locale
*/
public java.util.ResourceBundle getResourceBundle(java.util.Locale locale)
{
return config.getResourceBundle(locale);
}

/**
* Returns a String containing the value of the named initialization parameter,
* or null if the parameter does not exist.
*
* @param name a <code>String</code> specifying the name
* of the initialization parameter
*
* @return a <code>String</code> containing the value
* of the initialization parameter
*
* @exception java.lang.IllegalArgumentException
* if name is <code>null</code>.
*/
public String getInitParameter(java.lang.String name)
{
return config.getInitParameter(name);
}

/**
* Returns the names of the portlet initialization parameters as an
* Enumeration of String objects, or an empty Enumeration if the
* portlet has no initialization parameters.
*
* @return an <code>Enumeration</code> of <code>String</code>
* objects containing the names of the portlet
* initialization parameters, or an empty Enumeration if the
* portlet has no initialization parameters.
*/
public java.util.Enumeration getInitParameterNames()
{
return config.getInitParameterNames();
}
}


Portlet.xml
I file di configurazione non sono più con estensione xreg, la loro struttura è cambiata per adeguarsi alle specifiche, vediamo qui sotto il file di configurazione portlet.xml per la portlet dimostrativa basata su Struts.

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app id="struts-demo" version="1.0">
<portlet id="StrutsPortletDemo">
<init-param>
<name>ServletContextProvider</name>
<value>org.apache.jetspeed.portlet.ServletContextProviderImpl</value>
</init-param>
<init-param>
<name>ViewPage</name>
<value>/Welcome.do</value>
</init-param>
<init-param>
<name>HelpPage</name>
<value>/Tour.do</value>
</init-param>
<portlet-name>StrutsPortletDemo</portlet-name>
<display-name>Struts Portlet Demo</display-name>
<description>This is the struts MailReader portlet demo</description>
<portlet-class>org.apache.portals.bridges.struts.StrutsPortlet</portlet-class>
<expiration-cache>-1</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
<portlet-mode>HELP</portlet-mode>
</supports>
<portlet-info>
<title>StrutsPortletTitle</title>
<short-title>This is the short title</short-title>
<keywords>Struts</keywords>
</portlet-info>
</portlet>
</portlet-app>


PSML
Anche in questa nuova versione di Jetspeed viene usato un file xml con estensione .psml per mantenere la struttura del sito personale di un utente componendo le portlet. Vediamo infatti il tag

<fragment id="dp-1" type="layout" name="jetspeed::VelocityTwoColumns">

che dispone le portlet su due colonne vi sono i vari "frammenti" con il tipo che è di tipo layout o portlet, e infine le restrizioni di sicurezza dove viene permesso a tutti gli utenti di vedere questo psml.
Si possono però già vedere le differenze con i psml delle versioni 1.x di Jetspeed:

<!--
Copyright 2004 The Apache Software Foundation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<page>
<defaults
skin="orange"
layout-decorator="tigris"
portlet-decorator="tigris"
/>
<title>Welcome to Jetspeed 2</title>
<metadata name="title" xml:lang="fr">
Ma Premiere Page de PSML
</metadata>
<metadata name="title" xml:lang="ja">
Jetspeed 2 ?????
</metadata>

<fragment id="dp-1" type="layout" name="jetspeed::VelocityTwoColumns">
<fragment id="dp-3" type="portlet" name="pam::LocaleSelector">
<property layout="TwoColumns" name="row" value="0" />
<property layout="TwoColumns" name="column" value="0" />
</fragment>
<fragment id="dp-12" type="portlet" name="security::LoginPortlet">
<property layout="TwoColumns" name="row" value="0" />
<property layout="TwoColumns" name="column" value="1" />
</fragment>
<fragment id="dp-7" type="portlet" name="demo::PickANumberPortlet" >
<property layout="TwoColumns" name="row" value="1" />
<property layout="TwoColumns" name="column" value="0" />
</fragment>
<fragment id="dp-16" type="portlet" name="demo::RoleSecurityTest">
<property layout="TwoColumns" name="row" value="1" />
<property layout="TwoColumns" name="column" value="1" />
</fragment>
<fragment id="dp-9" type="portlet" name="demo::IFramePortlet">
<property layout="TwoColumns" name="row" value="2" />
<property layout="TwoColumns" name="column" value="0" />
</fragment>
<fragment id="dp-17" type="portlet" name="demo::UserInfoTest">
<property layout="TwoColumns" name="row" value="2" />
<property layout="TwoColumns" name="column" value="1" />
</fragment>
<fragment id="dp-18" type="portlet" name="demo::BookmarkPortlet">
<property layout="TwoColumns" name="row" value="3" />
<property layout="TwoColumns" name="column" value="1" />
</fragment>
</fragment>

<!-- allow all users to view -->
<security-constraints>
<security-constraint>
<users>*</users>
<permissions>view</permissions>
</security-constraint>
</security-constraints>
</page>

Per richiamare gli altri tab, vengono richiamati altri psml, ad esempio lo struts-demo.psml

<!--
Copyright 2004 The Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<page id="struts">
<defaults
skin="orange"
layout-decorator="tigris"
portlet-decorator="tigris"
/>
<title>Struts Portlet MailReader Demo</title>
<fragment id="sd-1" type="layout" name="jetspeed::VelocityTwoColumns">
<fragment id="15" type="portlet" name="struts-demo::StrutsPortletDemo">
<property layout="TwoColumns" name="row" value="0" />
<property layout="TwoColumns" name="column" value="0" />
</fragment>
</fragment>
<metadata name="title" xml:lang="ja">
Struts ?????????
</metadata>
</page>

Con questo psml viene chiamata l' applicazione struts mail reader.
A questo punto potrebbe sorgere spontanea la domanda nel lettore, come fa Jetspeed ad imbrigliare dentro delle portlet tante applicazioni così diverse ?
La risposta è in delle classi usate da Jetspeed per fare da tramite con queste applicazioni queste sono le classi che fanno parte dei package org.apache.portals.bridges


Figura 5
- Jetspeed2
(clicca sull'immagine per ingrandire)

Conclusioni
In questo primo articolo abbiamo visto una minima parte delle caratteristiche di Jetspeed2 che non ha nulla in comune con il suo predecessore se non il nome, vedremo nei prossimi articoli le altre caratteristiche, e come migrare una portlet scritta per Jetspeed1 a Jetspeed2.

 

Riferimenti
Apache Portals : http://portals.apache.org
Jetspeed2 : http://portals.apache.org/jetspeed-2/
Jetspeed1: http://portals.apache.org/jetspeed-1/
Pluto : http://portals.apache.org/pluto/
Spring : http://www.springframework.org

Spring, Inversion of Control e Jetspeed2: http://www.mokabyte.it/2004/10/spring.htm
jsr 168: http://www.mokabyte.it/2003/09/jportlet-3.htm


Massimiliano Dessì (http://www.jugsardegna.org/vqwiki/jsp/Wiki?MassimilianoDessi) ha iniziato a lavorare presso la Sistemi Informativi S.p.A Società IBM come programmatore Java. Dal 2001 lavora presso Atlantis S.p.A., dove utilizzando metodologie agili quali l'eXtreme programming (Xp), sviluppa applicazioni enterprise Web-based con tecnologia J2EE quali portali e content management system per la promozione del territorio. Nel poco tempo libero, cura la versione italiana di Jetspeed, tiene seminari e corsi su tecnologie J2EE e collabora con società e enti italiani ed esteri per la realizzazione di portali J2EE, è studente di Ingegneria Informatica al Politecnico di Torino.

 
MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it