di Massimiliano Dessì

Introduzione

Scopo di questo articolo è mostrare il porting di una portlet con funzionalità CRUD illustrata nell' articolo precedente, su Jetspeed 2, adottando una architettura maggiormente Spring/Ioc-oriented. Nell' articolo non si terrà conto delle autorizzazioni dell' utente e dei messaggi di errore lato client. Nel precedente articolo, è stato mostrato come la portlet era dotata di un metodo per ciascuna delle operazioni, e poneva nel contesto di Velocity gli oggetti per rappresentare i risultati ottenuti, potevamo usare un solo template vm con delle condizioni di visualizzazione in base alle operazioni richieste. Nel caso di Jetspeed2 invece abbiamo due soli metodi di accesso, il processAction e il doView (oltre al doHelp e al doEdit). Perciò useremo il metodo doView per la visualizzazione "normale" in cui operiamo sulle altre portlet, e il processAction per inoltrare tutti i metodi che invocavamo in Jetspeed 1 direttamente alla portlet.

Figura 1 - RicettarioPortlet
Figura 1 - RicettarioPortlet

Nell' articolo precedente abbiamo usato Spring solamente per lo strato di accesso ai dati, qui lo useremo in maniera ancora più estesa, infatti, ad esclusione della sola classe RicettarioPortlet, tutte le altre sono costruite per utilizzare l' Inversion of Control fornito da Spring. Lo strato di accesso ai dati e l'ascoltatore (Servlet Listener) continueranno ad essere quelli dell' articolo precedente, cambia la modalità di recupero, in quanto non avviene più nella portlet, ma nel controller, basandosi sul file di configurazione dei beans, perciò il DAO verrà iniettato nel Controller e non più recuperato dall' ApplicationContext con il lookup.

Architettura

Prendendo spunto dalla architettura MVC di Spring:

Figura 2 - Spring MVC
Figura 2 - Spring MVC

strutturiamo la nostra portlet in questo modo:

Figura 3 - RicettarioPortlet
Figura 3 - RicettarioPortlet

Alla portlet arrivano delle RenderRequest o delle ActionRequest, e viene delegato alla classe che implementa l' Interfaccia IRicettarioController il lavoro vero e proprio. Quindi la nostra portlet fa la parte della Dispatcher servlet, agendo da front controller.

Figura 4 - IRicettarioController
Figura 4 - IRicettarioController

La classe RicettarioController

La classe RicettarioController contiene nel metodo processActionRequest, la logica di business che smista le operazioni richieste, a tutti gli effetti lavora come un Controller di Spring. Il Contoller in ognuno dei suoi metodi assegna il modello e la view (Spring ha un oggetto ModelAndView che li contiene) che viene visualizzato nella jsp prescelta. Il corrispondente della view di SpringMVC, è la jsp che viene inclusa nella nostra dispatcher.jsp. L'HandlerMapping e il ViewResolver nella nostra portlet non sono presenti, in quanto non abbiamo bisogno di gestire url di richiesta diversi ne tantomeno diverse tecnologie di visualizzazione in quanto usiamo le sole jsp.

Implementazione

Vediamo per prima cosa la portlet:

/*
 * Copyright 2000-2001,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.
 */
/**
 *@author    Massimiliano Dessì
 *@created   13 marzo 2005
 */
package org.casamia.cucina.ricettario.portlet;

import org.casamia.cucina.ricettario.services.IServizio;
import org.casamia.cucina.ricettario.util.Costanti;

import javax.portlet.*;
import java.io.IOException;

public class RicettarioPortlet extends GenericPortlet {

  /**
   *  Description of the Method
   */
  public void init() {
    IServizio srv = (IServizio) getPortletContext().getAttribute(
      Costanti.SERVIZIO);
    _rc = (IRicettarioController) srv.getApplicationContext().getBean(Costanti.NOME_CONTROLLER);
  }

  /**
   *  Description of the Method
   *
   *@param  request          Description of the Parameter
   *@param  actionResponse      Description of the Parameter
   *@exception  PortletException  Description of the Exception
   *@exception  IOException     Description of the Exception
   */
  public void processAction(ActionRequest request,
      ActionResponse actionResponse) throws PortletException, IOException {
    _rc.processActionRequest(request, actionResponse);
  }

  /**
   *  Description of the Method
   *
   *@param  request          Description of the Parameter
   *@param  response          Description of the Parameter
   *@exception  PortletException  Description of the Exception
   *@exception  IOException     Description of the Exception
   */
  public void doView(RenderRequest request, RenderResponse response)
      throws PortletException, IOException {
    _rc.processRenderRequest(request, response, getPortletContext());
  }

  public void destroy() {
    _rc.cleanUp();
  }

  private IRicettarioController _rc;
}

Come si vede, nell' init recuperiamo attraverso il Servizio e l' ApplicationContext il RicettarioController, questo è l'unico lookup che viene fatto, e una sola volta alla istanziazione della portlet. I metodi doView e processAction delegano l'elaborazione al RicettarioController.

Ricettario Controller

Vediamo l'implementazione di questa classe in cui le dipendenze sono iniettate da Spring (il IRicettaDao e l' IActionRequestTool):

/*
 * Copyright 2000-2001,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.
 */
/**
 *@author    Massimiliano Dessì
 *@created   13 marzo 2005
 */
package org.casamia.cucina.ricettario.portlet;

import java.io.IOException;
import javax.portlet.*;

import org.casamia.cucina.ricettario.db.dao.IRicettaDao;
import org.casamia.cucina.ricettario.portlet.util.IActionRequestTool;
import org.casamia.cucina.ricettario.util.Config;
import org.casamia.cucina.ricettario.vo.Ricetta;

public class RicettarioController implements IRicettarioController {

  public void cleanUp() {
  }

  /**
   *  Sets the ricettaDao attribute of the RicettarioController object
   *
   *@param  dao  The new ricettaDao value
   */
  public void setRicettaDao(IRicettaDao dao) {
    _dao = dao;
  }

  /**
   *  Sets the requestTool attribute of the RicettarioController object
   *
   *@param  rt  The new requestTool value
   */
  public void setRequestTool(IActionRequestTool rt) {
    _rt = rt;
  }

  /**
   *  Description of the Method
   *
   *@param  request      Description of the Parameter
   *@param  actionResponse  Description of the Parameter
   */
  public void processActionRequest(ActionRequest request,
      ActionResponse actionResponse) {
    String action = request.getParameter(_config.getAction());

    if (action.equals(_config.getNuovaRicetta())) {
      doNuova(request);
    } else if (action.equals(_config.getInserimentoRicetta())) {
      doInsert(request);
    } else if (action.equals(_config.getListaRicette())) {
      doLista(request);
    } else if (action.equals(_config.getDettaglio())) {
      doDetail(request);
    } else if (action.equals(_config.getCancella())) {
      doCancel(request);
    } else if (action.equals(_config.getElimina())) {
      doDelete(request);
    } else if (action.equals(_config.getUpdate())) {
      doUpdate(request);
    } else if (action.equals(_config.getModifica())) {
      doModify(request);
    }
  }

  /**
   *  Description of the Method
   *
   *@param  request          Description of the Parameter
   *@param  response          Description of the Parameter
   *@param  pc              Description of the Parameter
   *@exception  PortletException  Description of the Exception
   *@exception  IOException     Description of the Exception
   */
  public void processRenderRequest(RenderRequest request,
      RenderResponse response, PortletContext pc)
      throws PortletException, IOException {
    PortletSession sess = request.getPortletSession();

    if (null != sess.getAttribute(_config.getViewSwitch())) {
      handshake(sess, request);
    } else {
      request.setAttribute(_config.getRicetta(), _dao.getRicetta());
    }

    dispatch(pc, request, response);
  }

  /**
   *  Description of the Method
   *
   *@param  sess    Description of the Parameter
   *@param  request  Description of the Parameter
   */
  private void handshake(PortletSession sess, RenderRequest request) {
    request.setAttribute(_config.getViewSwitch(), sess.getAttribute(_config.getViewSwitch()));
    sess.removeAttribute(_config.getViewSwitch());
    request.setAttribute(_config.getModel(), sess.getAttribute(_config.getModel()));
    sess.removeAttribute(_config.getModel());
  }

  /**
   *  Sets the modelAndView attribute of the RicettarioController object
   *
   *@param  session   The new modelAndView value
   *@param  view     The new modelAndView value
   *@param  nameModel  The new modelAndView value
   *@param  model    The new modelAndView value
   */
  private void setModelAndView(PortletSession session, String view,
      String nameModel, Object model) {
    session.setAttribute(_config.getViewSwitch(), view);
    session.setAttribute(nameModel, model);
  }

  /**
   *  Sets the view attribute of the RicettarioController object
   *
   *@param  session  The new view value
   *@param  view    The new view value
   */
  private void setView(PortletSession session, String view) {
    session.setAttribute(_config.getViewSwitch(), view);
  }

  /**
   *  Description of the Method
   *
   *@param  pc              Description of the Parameter
   *@param  req             Description of the Parameter
   *@param  res             Description of the Parameter
   *@exception  PortletException  Description of the Exception
   *@exception  IOException     Description of the Exception
   */
  private void dispatch(PortletContext pc, RenderRequest req,
      RenderResponse res) throws PortletException, IOException {
    PortletRequestDispatcher rd = pc.getRequestDispatcher(_jspDispatcher);
    rd.include(req, res);
  }

  /**
   *  Sets the jspDispatcher attribute of the RicettarioController object
   *
   *@param  jspView  The new jspDispatcher value
   */
  public void setJspDispatcher(String jspView) {
    _jspDispatcher = jspView;
  }

  /**
   *  Sets the config attribute of the RicettarioController object
   *
   *@param  config  The new config value
   */
  public void setConfig(Config config) {
    _config = config;
  }

  /**
   *  Sets the ricetta attribute of the RicettarioController object
   *
   *@param  ricetta  The new ricetta value
   */
  public void setRicetta(Ricetta ricetta) {
    _ricetta = ricetta;
  }

  /**
   *  Description of the Method
   *
   *@param  req  Description of the Parameter
   */
  private void doLista(ActionRequest req) {
    setModelAndView(req.getPortletSession(), _config.getListaRicette(),
      _config.getModel(), _dao.getRicette());
  }

  /**
   *  Description of the Method
   *
   *@param  req  Description of the Parameter
   */
  private void doNuova(ActionRequest req) {
    setView(req.getPortletSession(), _config.getNuovaRicetta());
  }

  /**
   *  Description of the Method
   *
   *@param  req  Description of the Parameter
   */
  private void doCancel(ActionRequest req) {
    String idRicetta = _rt.getId(req);

    if (!idRicetta.equals(_config.getNonValorizzatoString())) {
      Ricetta vo = _dao.getRicetta(Integer.parseInt(req.getParameter(_config.getRicettaId())));
      setModelAndView(req.getPortletSession(), _config.getCancella(),
        _config.getModel(), vo);
    }
  }

  /**
   *  Description of the Method
   *
   *@param  req  Description of the Parameter
   */
  private void doDelete(ActionRequest req) {
    String idRicetta = _rt.getId(req);

    if (!idRicetta.equals(_config.getNonValorizzatoString())) {
      int risultato = _dao.eliminaRicetta(Integer.parseInt(idRicetta));
      _rt.inserisciMessaggioDelete(req, risultato, Integer.parseInt(idRicetta));
    }

    doLista(req);
  }

  /**
   *  Description of the Method
   *
   *@param  req  Description of the Parameter
   */
  private void doInsert(ActionRequest req) {
    Ricetta ricetta = _rt.getRicetta(req);

    if (null != ricetta) {
      _rt.inserisciMessaggioInsert(req, _dao.inserisciRicetta(ricetta));
      doLista(req);
    } else {
      _rt.inserisciMessaggioNoDati(req);
    }
  }

  /**
   *  Description of the Method
   *
   *@param  req  Description of the Parameter
   */
  private void doUpdate(ActionRequest req) {
    Ricetta ricetta = _rt.getRicetta(req);

    if (null != ricetta) {
      _rt.inserisciMessaggioUpdate(req, _dao.aggiornaRicetta(ricetta));
    } else {
      _rt.inserisciMessaggioNoDati(req);
    }
  }

  /**
   *  Description of the Method
   *
   *@param  req  Description of the Parameter
   */
  private void doDetail(ActionRequest req) {
    if (!_rt.getId(req).equals(_config.getNonValorizzatoString())) {
      Ricetta vo = _dao.getRicetta(Integer.parseInt(req.getParameter(_config.getRicettaId())));
      setModelAndView(req.getPortletSession(), _config.getDettaglio(),
        _config.getModel(), vo);
    }
  }

  /**
   *  Description of the Method
   *
   *@param  req  Description of the Parameter
   */
  private void doModify(ActionRequest req) {
    if (!_rt.getId(req).equals(_config.getNonValorizzatoString())) {
      Ricetta vo = _dao.getRicetta(Integer.parseInt(req.getParameter(_config.getRicettaId())));
      setModelAndView(req.getPortletSession(), _config.getModifica(),
        _config.getModel(), vo);
    }
  }

  private Config _config;
  private Ricetta _ricetta;
  private String _jspDispatcher;
  private IActionRequestTool _rt;
  private IRicettaDao _dao;
}

Nel suo lavoro il Controller è coadiuvato da due altre classi, una ActionRequestTool, che si occupa di ricostruire gli oggetti prelevandoli dalla request. Il secondo oggetto e l' oggetto Config valorizzato da Spring e che contiene tutte le configurazioni necessarie alla applicazione, infatti non è presente una sola stringa "Hard coded". Nel metodo processActionRequest, viene controllata l'azione richiesta e dirottata sul metodo opportuno, una volta eseguita l'elaborazione, il risultato viene messo nella sessione della portlet. Quando verra fatto il rendering con il doView, il metodo processRenderRequest controllerà se nella PortletSession è stato messo qualche oggetto da visualizzare. Nel caso fosse presente, il metodo handshake provvede a toglierlo dalla sessione e a metterlo nella request, in modo da renderlo disponibile nelle jsp, ma anche per non appesantire la sessione. Gli oggetti (ricetta.model), potrebbero essere presi nella jsp anche dalla sessione con un RenderRequest.getPortletSession().getAttribute(), ma rimarrebbero comunue inutilmente nella sessione della portlet. Se invece il processRenderRequest non trova niente che gli indichi la presenza di oggetti, mostra semplicemente la ricetta nella visualizzazione ordinaria che deve essere mostrata in quel momento.

Figura 5 - IActionRequestTool
Figura 5 - IActionRequestTool
Figura 6 - Config
Figura 6 - Config

Visualizzazione

Ci ritroviamo a dover visualizzare vari stati di funzionamento, partendo però dal presupposto che una portlet secondo le specifiche Portlet 1.0 (jsr 168) ha una jsp per la view, una per l' edit e una per l' help. Scegliamo quindi di avere una jsp che agisce da dispatcher secondo quanto dettato da ciò che viene settato nel metodo setModelAndView nel RicettarioController.

JspDispatcher.jsp

<%-- 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.
--%>
<%@ taglib uri='/WEB-INF/tld/c.tld' prefix='c'%>

<c:choose>
  <c:when test="${v_switch == 'ricetta.list'}">
    <jsp:include page="lista.jsp"/>
  </c:when>
  <c:when test="${v_switch eq 'ricetta.new'}">
    <jsp:include page="nuova.jsp"/>
  </c:when>
  <c:when test="${v_switch eq 'ricetta.detail'}">
    <jsp:include page="dettaglio.jsp"/>
  </c:when>
  <c:when test="${v_switch eq 'ricetta.modify'}">
    <jsp:include page="modifica.jsp"/>
  </c:when>
  <c:when test="${v_switch eq 'ricetta.cancel'}">
    <jsp:include page="cancel.jsp"/>
  </c:when>
  <c:otherwise>
    <jsp:include page="viewOrdinary.jsp"/>
  </c:otherwise>
</c:choose>

La vista normale sarà

<%-- 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.
--%>
<%@ taglib uri='/WEB-INF/tld/portlet.tld' prefix='portlet'%>
<%@ taglib uri='/WEB-INF/tld/c.tld' prefix='c'%>
<%@ taglib uri='/WEB-INF/tld/c-rt.tld' prefix='c-rt'%>

Ricettario portlet
<portlet:defineObjects/>
<DIV style="position:relative">
<a href="<portlet:actionURL>
     <portlet:param name="azione" value="ricetta.list"/>
   </portlet:actionURL>"><font color="#FD0A11">Lista Ricette</font></a>
</DIV>

<table width="458" border="0" cellspacing="0" cellpadding="0">
  <tr>
    <td width="458" align="left" valign="top" background="images/colore_interna.gif" class="contentarticoli">
      <p><img src="/ricettario/images/1.jpg" width="150" height="150" hspace="3" vspace="o" align="right">
      <strong><c:out value="${ricetta.nome}"/></strong><br><br>
      <p><b>Per <c:out value="${ricetta.numeroPersone}"/></b></p>
      <p><b>Ingredienti:</b><c:out value="${ricetta.descrizione}"/></p>
      <p><b>Preparazione</b><br><c:out value="${ricetta.preparazione}"/></p>
      </p>
    </td>
  </tr>
</table>
Figura 7 - Vista ordinaria
Figura 7 - Vista ordinaria

La lista invece sarà

lista.jsp

<%-- 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.
--%>
<%@ taglib uri='/WEB-INF/tld/portlet.tld' prefix='portlet'%>
<%@ taglib uri='/WEB-INF/tld/c.tld' prefix='c'%>
<%@ taglib uri='/WEB-INF/tld/c-rt.tld' prefix='c-rt'%>
<%@ page import="org.casamia.cucina.ricettario.vo.Ricetta"%>
<portlet:defineObjects/>

<portlet:renderURL portletMode="view" var="normal"/>
<a href='<%=normal%>'>Vista Ordinaria Utente</a>

<c-rt:set var="ricette" value="<%=renderRequest.getAttribute("ricetta.model")%>"/>

<c:forEach var="ricetta" items="${ricette}" varStatus="status"><br><br>

<%
  Ricetta ric =(Ricetta)pageContext.getAttribute("ricetta");
  String id = String.valueOf(ric.getId());
  String nome= ric.getNome();
%>

<%=nome%>
  <a href="<portlet:actionURL>
     <portlet:param name="azione" value="ricetta.detail"/>
     <portlet:param name="ricetta.id" value="<%=id%>"/>
   </portlet:actionURL>">Visualizza</font>
  <a href="<portlet:actionURL>
     <portlet:param name="azione" value="ricetta.modify"/>
     <portlet:param name="ricetta.id" value="<%=id%>"/>
   </portlet:actionURL>">Modifica</font>
  <a href="<portlet:actionURL>
     <portlet:param name="azione" value="ricetta.cancel"/>
     <portlet:param name="ricetta.id" value="<%=id%>"/>
   </portlet:actionURL>">Elimina</a>

</c:forEach>

<br><br><br>

<a href="<portlet:actionURL>
  <portlet:param name="azione" value="ricetta.new"/>
</portlet:actionURL>"><font color="#FD0A11">Nuova Ricetta</font></a>
Figura 8 - Lista ricette
Figura 8 - Lista ricette
Figura 9 - Modifica ricetta
Figura 9 - Modifica ricetta
Figura 10 - Conferma eliminazione ricetta
Figura 10 - Conferma eliminazione ricetta

Dominio dati

Il modello dei dati è la Ricetta:

Figura 11 - Ricetta
Figura 11 - Ricetta

Configurazione

Una volta compilate le classi e scritte le nostre jsp, dobbiamo scrivere tre file di configurazione, un web.xml come tutte le web-app, un portlet.xml, un jetspeed-portlet.xml e modificare il default.psml per dire a jetspeed dove la vogliamo vedere (nella pagina di default).

Portlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app id="ricettario" version="1.0">
  <portlet id="Ricettario">
    <portlet-name>Ricettario</portlet-name>
    <display-name>Ricettario</display-name>
    <description>Ricettario</description>
    <portlet-class>org.casamia.cucina.ricettario.portlet.RicettarioPortlet</portlet-class>
    <supports>
      <mime-type>text/html</mime-type>
      <portlet-mode>VIEW</portlet-mode>
    </supports>
    <portlet-info>
      <title>Ricettario</title>
      <short-title>Ricettario</short-title>
      <keywords>Ricettario</keywords>
    </portlet-info>
  </portlet>
</portlet-app>

Jetspeed-portlet.xml

<?xml version="1.0" encoding="UTF-8"?>

<portlet-app id="ricettario" version="1.0" xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
   xmlns:js="http://portals.apache.org/jetspeed"
   xmlns:dc="http://www.purl.org/dc">

  <portlet>
    <portlet-name>Ricettario</portlet-name>
    <dc:title>Ricettario</dc:title>
    <dc:creator>Massimiliano Dessì</dc:creator>
  </portlet>

</portlet-app>

default.psml

aggiungiamo in coda la nostra portlet:

...
  <fragment id="dp-30" type="portlet" name="ricettario::Ricettario">
    <property layout="TwoColumns" name="row" value="4" />
    <property layout="TwoColumns" name="column" value="1" />
  </fragment>
</fragment>

Beans.xml

Vediamo le mappature degli oggetti aggiunti in questo articolo rispetto al precedente:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
-->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

...

  <!-- RicettarioController -->
  <bean id="ricettarioController" class="org.casamia.cucina.ricettario.portlet.RicettarioController">
    <property name="ricettaDao"><ref bean="ricettaJdbcClassic"/></property>
    <property name="jspDispatcher"><value>/jsp/dispatcher.jsp</value></property>
    <property name="requestTool"><ref bean="art"/></property>
    <property name="config"><ref bean="config"/></property>
    <property name="ricetta"><ref bean="ricetta"/></property>
  </bean>

  <!-- ActionRequestTool -->
  <bean id="art" class="org.casamia.cucina.ricettario.portlet.util.ActionRequestTool">
    <property name="config"><ref bean="config"/></property>
    <property name="msgIncomplete"><value>Inserire tutti i dati della ricetta</value></property>
    <property name="msgInsertOk"><value>La ricetta è stata inserita</value></property>
    <property name="msgInsertNo"><value>La ricetta non è stata inserita</value></property>
    <property name="msgUpdateOk"><value>La ricetta è stata aggiornata</value></property>
    <property name="msgUpdateNo"><value>La ricetta non è stata aggiornata</value></property>
    <property name="msgDeleteOk"><value>E' stata eliminata la ricetta con id:</value></property>
    <property name="msgDeleteNo"><value>Non è' stata eliminata la ricetta con id:</value></property>
    <property name="ricetta"><ref bean="ricetta"/></property>
  </bean>

  <!-- Ricetta -->
  <bean id="ricetta" class="org.casamia.cucina.ricettario.vo.Ricetta">
    <constructor-arg type="java.lang.String"><value>Campo non valorizzato</value></constructor-arg>
    <constructor-arg type="int"><value>0</value></constructor-arg>
  </bean>

  <!-- Config -->
  <bean id="config" class="org.casamia.cucina.ricettario.util.Config">
    <property name="action"><value>azione</value></property>
    <property name="cancella"><value>ricetta.cancel</value></property>
    <property name="contentType"><value>text/html</value></property>
    <property name="descrizioneReq"><value>ricetta.descrizione</value></property>
    <property name="dettaglio"><value>ricetta.detail</value></property>
    <property name="elimina"><value>ricetta.delete</value></property>
    <property name="inserimentoRicetta"><value>ricetta.new.insert</value></property>
    <property name="listaRicette"><value>ricetta.list</value></property>
    <property name="messaggio"><value>messaggio</value></property>
    <property name="modifica"><value>ricetta.modify</value></property>
    <property name="nomeController"><value>ricettarioController</value></property>
    <property name="nomeRicettaReq"><value>ricetta.nome</value></property>
    <property name="nonValorizzatoString"><value>Campo non valorizzato</value></property>
    <property name="numeroPersoneReq"><value>ricetta.numero.persone</value></property>
    <property name="nuovaRicetta"><value>ricetta.new</value></property>
    <property name="preparazioneReq"><value>ricetta.preparazione</value></property>
    <property name="ricetta"><value>ricetta</value></property>
    <property name="idRicetta"><value>ricetta.id</value></property>
    <property name="servizio"><value>servizio</value></property>
    <property name="update"><value>ricetta.update</value></property>
    <property name="viewSwitch"><value>v_switch</value></property>
    <property name="model"><value>ricetta.model</value></property>
  </bean>

</beans>

Documentazione dinamica del beans.xml generata con Spring BeanDoc

Figura 12 - Spring beandoc
Figura 12 - Spring beandoc
Figura 13 - Spring beandoc
Figura 13 - Spring beandoc

Una volta impacchettato il tutto in un file war, poniamo il war nella cartella WEB-INF\deploy di Jetspeed e fatta ripartire l' applicazione, ci ritroveremo la nostra portlet.

Conclusioni

In questa articolo abbiamo visto come portare su Jetspeed 2 una portlet funzionante su Jetspeed 1, usando la M2 di Jetspeed 2 con tomcat 5.0.30, e Spring 1.2 RC1. Sul prossimo articolo vedremo e confronteremo il servizio che esegue la rotazione delle varie ricette usando MX4J, e le nuove features di supporto a Jmx introdotte nella versione 1.2 di Spring. Vedremo anche la struttura del database sottostante. In seguito vedremo come integrare una applicazione interamente Spring MVC dentro Jetspeed2.

Riferimenti

  • Apache Portals: http://portals.apache.org
  • Jetspeed 2: http://portals.apache.org/jetspeed-2/
  • Spring framework: http://www.springframework.org
  • Jetspeed 2 parte Seconda: http://www.jugsardegna.org/vqwiki/jsp/Wiki?Jetspeed2_I
  • Jetspeed 2 parte Prima: http://www.jugsardegna.org/vqwiki/jsp/Wiki?Jetspeed2
  • Portlet III JSR 168: http://www.mokabyte.it/2003/09/jportlet-3.htm
  • Spring e IoC: http://www.jugsardegna.org/vqwiki/jsp/Wiki?SpringIoCJet2

Bibliografia

  • J2ee Development without EJB: Rod Johnson, Juergen Hoeller Ed. Wrox
  • Pro Spring: Rob Harrop, Jan Machacek Ed.Apress
  • Spring in Action: Craig Walls, Ryan Breidenbach Ed. Manning

Sviluppo

  • Pluto eclipse: http://plutoeclipse.sourceforge.net/
  • Spring BeanDoc: http://opensource.atlassian.com/confluence/spring/display/BDOC/Home
  • Omondo EclipseUML: http://www.omondo.com/download/index.html
  • Graphviz: http://www.graphviz.org/

Massimiliano Dessì:

Ha iniziato a lavorare presso la Sistemi informativi (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.