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.
|