Cocoon 2.2 – prime impressioni

Bene, alla fine mi sono deciso a mettere le mani sulla nuova versione di Cocoon. Ho lavorato sulla RC2, ed è parsa decisamente stabile. Inizialmente ho giocato un po’ seguendo i tutorials sul nuovo sito, e visto il successo delle prime applicazioni giocattolo, tirate su in due balletti, mi sono buttato a fare l’aggiornamento di una applicazione esistente basata sulla 2.1.10.

Risultati ottimi. E’ filato quasi tutto liscio.

Mi piace molto la gestione del progetto con maven in particolare:

  • grazie agli archetype specifici per Cocoon, semplicità e velocità nella costruzione di un nuovo progetto (ci voleva proprio :) , vedi i tutorial)
  • comoda gestione delle librerie, maven permette di definirle in modo centralizzato e gestise dipende e download in automatico
  • comodità e rapidità nello sviluppo con il Cocoon Maven Plugin, si può sviluppare a runtime senza ricaricare tutto il contesto ad ogni build, il plugin si occupa di ricaricare solo le classi modificate.

Mi ha colpito molto la struttura di partenza di un nuovo progetto creata dagli archetype, praticamente non c’è niente da fare, la configurazione iniziale è minima.

  • files in /META-INF/cocoon/spring/*.xml configurano tramite Spring tutta la nostra applicazione, inizialmente troviamo solo servlet-service.xml e demoservlet-service.xml che configura un bean di demo
  • il file servlet-service.xml inizialmente contiene solo context e mount-point dell’applicazione. Da qui si attivano e configurano i block.

Sitemap e tutto il resto sono nella directory COB-INF, in src i sorgenti java.

La prima impressione è che sia stato fatto un gran bello sforzo per rendere semplice, pulita e ben mantenibile la struttura del progetto. Maven è stato introdotto con molto raziocionio.

Grazie alla comunità di Cocoon

…in attesa del rilascio definitivo della versione 2.2, sembra per la fine di Gennaio 2008

Apache Cocoon e il Design Pattern MVC (2)

Voglio approfondire il Controller. Negli anni il pattern MVC si è evoluto o mutato, infatti oggi si parla di Model1 e Model2. In soldoni l’MVC1 prevede che il Controller si limiti a girare le richieste dalla View al Model. Ma la pratica, o meglio la crescente complessità del Model e dei processi che lo regolano, hanno portato il Controller ad assumere più responsabilità, in particolare ad occuparsi anche del controllo della business logic legata al Model.
In Cocoon è possibile applicare il Model1 utilizzando la sitemap. Come nella figura seguente:

MVC, Model 1

l’unico ruolo del Controller, impersonata dalla sitemap, è quello di gestire le richieste
del client e selezionare le pagine da mostrare.

Il Model2 è invece applicabile con l’utilizzo congiunto di sitemap e continuation:

MVC, Model 2

In questo caso il Controller prima si occupa di intercettare la richiesta tramite la sitemap, poi il controllo passa alla Continuation che dirige la business logic e l’invio del Model alla View.

L’argomento andrebbe approfondito meglio. Ma un concetto deve essere ben fermo: la continaution deve occuparsi solo di dirigire, ovvero deve fare solo da Controller, forte infatti è la tentazione utilizzarla per operazioni che intervengono sul Model. Rispettare il pattern MVC rende l’applicazione più razionale e mantenibile.

Apache Cocoon e il Design Pattern MVC (1)

L’architettura MVC è un design pattern che aiuta a risolvere i problemi
che nascono nel definire le relazioni e le comunicazioni tra i dati, l’applicazione
e la presentazione. Nell’ MVC il Model rappresenta i dati dell’applicazioni
e contiene le regole fondamentali di accesso e mantenimento degli stessi. Tipicamente
il Model, come suggerisce il nome, riflette o modella un processo
del mondo reale, per esempio l’organizzazione di un magazzino, la gestione dei documenti di un sito Web.

La View si occupa di organizzare la presentazione del modello, accede
ai dati e li presenta in modo opportuno. La view ha bisogno di mantenere
una rappresentazione consistente del modello, quindi, se c’è un cambiamento
in quest’ultimo, deve ricevere una notifica del cambio e provvedere ad una
vista aggiornata del modello. Nell’architettura MVC ci possono essere più viste dello stesso modello.

Infine c’è il Controller che cattura le richieste dell’utente, trasformandole
in opportune interazioni tra il modello e le viste.
La Figura seguente

Modello MVC

mostra i componenti dell’architettura MVC e come questi
interagiscono tra di loro. Il beneficio principale che l’architettura MVC offre
allo sviluppo di un’applicazione è quello di aiutare a separare l’organizzazione,
l’elaborazione, la presentazione delle informazioni.
Questa separazione è fondamentale per costruire applicazioni complesse in modo veloce e razionale.
Implementare un pattern MVC in Cocoon è molto semplice, anzi quasi
naturale
.

Apache Cocoon – Control Flow

La mancanza di stato

Quando si sviluppa una applicazione web, si deve fare i conti con una caratteristica del protocollo HTTP: la mancanza di stato. Non c’è nessuna informazione sulla storia delle request/response intercorse tra un
client ed un server. Questo rende problematico lo sviluppo di applicazioni web complesse.

Da un lato la mancanza di stato garantisce un’ottima scalabilità dell’applicazione,
poichè non c’è la necessità di mantenere un servizio o una servlet
per ogni utente. D’altro canto l’onere di gestire lo stato della conversazione
passa tutto sullo spalle dello sviluppatore.

Si può pensare ad una applicazione
web come una coppia di request/response come nella figura seguente:

Modello request/response

Con questo modello il controllo sta tutto nel browser. L’applicazione si
attiva solo in corrispondenza di richieste fatte dal browser. Questo modello
funziona bene se le richieste sono piccole e indipendenti, ma entra in crisi
in contesti complessi. Basta pensare ad un carrello elettronico, se si fa un
doppio click sul pulsante di invio del form, troveremo due oggetti identici nel
nostro carrello.

Continuation

L’idea alla base della continuation è la seguente: ad ogni richiesta del client,
lasciare al framework il compito di caricare e salvare lo stato, all’inizio e alla fine di ogni richiesta.

Cocoon mette a disposizione tre implementazioni della continuation:
una in JavaScript basata su Rhino JavaScript di Mozilla detta
flowscript, due in Java: JavaFlow e l’Apples Flow.
Per capire come funziona la continuazione vediamo subito
un esempio in JavaFlow:

  1. import org.apache.cocoon.components. flow. java.AbstractContinuable;
  2. import org.apache.cocoon.components. flow. java.VarMap;
  3. public class CalculatorFlow extends AbstractContinuable {
  4. public void doCalculator() {
  5. sendPageAndWait(“getNumberA.html”) ;
  6. float a = Float .parseFloat(getRequest() .getParameter(“a”)) ;
  7. sendPageAndWait(“getNumberB.html”) ;
  8. float b = Float .parseFloat(getRequest() .getParameter(“a”)) ;
  9. sendPageAndWait(“getOperator.html”) ;
  10. String op = getRequest() .getParameter(“operator”) ;
  11. i f (op. equals(“plus”)) {
  12. sendPage(“page/calculator?result”, new VarMap() .add(“result”, a + b))
  13. ;
  14. } else i f (op. equals(“minus”)) {
  15. sendPage(“page/calculator?result”, new VarMap() .add(“result”, a ? b))
  16. ;
  17. } else i f (op. equals(“multiply”)) {
  18. sendPage(“page/calculator?result”, new VarMap() .add(“result”, a  b))
  19. ;
  20. } else i f (op. equals(“divide”)) {
  21. i f (b == 0f )
  22. sendPageAndWait(“page/calculator?message”, new VarMap() .add(“
  23. message”, “Error: Cannot divide by zero!”)) ;
  24. sendPage(“page/calculator?result”, new VarMap() .add(“result”, a / b))
  25. ;
  26. } else {
  27. sendPageAndWait(“page/calculator?message”, new VarMap() .add(“message”
  28. , “Error: Unkown operator !”)) ;
  29. }
  30. }
  31. }

Quando un client richiede l’applicazione calcolatrice viene avviata la funzione
doCalculator. Alla linea 5 del listato c’è la funzione speciale
sendPageAndWait; questa funzione è in grado di invocare pagine web,
selezionado una pipeline, e di passare dei parametri, una mappa di parametri
come si vede alla riga 15 dove c’è l’altra funzione speciale sendPage. Quando
la funzione sendPageAndWait invia una pagina, viene bloccata l’esecuzione
del thread fino a quando l’utente cliccando su un link non invia una nuova
richiesta al server. Intercettata la richiesta, l’applicazione riparte dal punto
in cui si era fermata, e continua l’esecuzione del programma, riga 6 del listato, catturando il parametro passato dalla pagina. Al passo seguente viene inviata una seconda pagina al client e rimane in attesa.
E’ evidente che con questo approccio possiamo scrivere il flusso di controllo
di un’applicazione web come quello di un normale programma.

La continuation
può semplificare molto lo sviluppo di applicazioni web. Lo schema
senza stati può esser ridisegnato
come in figura seguente:

Modello ?Applicazione?

Quando un utente avvia una applicazione è il webserver che ha il controllo.
Una applicazione web, invece di essere un insieme di richieste in
ordine casuale, diviene una conversazione con l’utente unificata e controllata.
I vantaggi offerti da questo approccio possono esser così schematizzati:

  • assenza di stato tra le richieste. Il framework può identicare una singola continuation dalla URL e salvarla in sessione. Di conseguenza l’architettura di comunicazione non viene appesantita, anzi nella pratica Cocoon si limita a passare un solo ID alfanumerico che serve ad identificare la continuation.
  • il modello di programmazione è di tipo stateful, ossia con informazioni
    sullo stato. Il framework è in grado di riportarsi in ogni momento ad
    ogni stadio del suo ciclo di vita.
  • è possibile invalidare la continuation, questo può essere utile, per
    esempio, nei casi di invii multipli dello stesso form.
  • il pulsante indietro del browser non crea problemi visto che il
    framework, si occupa di ripristinare anche gli stati precedenti.

Uno svantaggio è che il framework utilizza molta memoria per gestire
la continuation. Questo è da tenere in considerazione per fare un uso oculato
delle risorse. E’ comunque da segnalare che ormai la continuation
in cocoon è una implementazione matura; a conferma di questo ci sono le
esperienze pratiche da me maturate negli ultimi due anni, non si sono mai
verificati decadimenti delle prestazioni e/o condizioni di out of memory anche
in condizioni di stress per il framework.

Apache Cocoon – Cron

Era diverso tempo che non mi serviva… l’ho rispolverato in questi giorni.

Innanzitutto il file local.blocks.properties deve avere la seguente riga commentata:

#include.block.cron=false

fatto il build avremo in WEB-INF/lib/ la libreria cocoon-cron-block.jar.

La configurazione è tutta nel cocoon.xconf e in questo file ci sono già degli esempi di configurazioni.

Per esempio:

<component class="org.apache.cocoon.components.cron.CocoonQuartzJobScheduler" logger="cron" role="org.apache.cocoon.components.cron.JobScheduler">

<!-- Definintions of triggers -->
<triggers>
<trigger name="pipeline-test"
target="org.apache.cocoon.components.cron.CronJob/test-cron"
concurrent-runs="true">
<seconds>10</seconds>
<minutes>*</minutes>
<hours>*</hours>
<days>*</days>
<months>*</months>
</trigger>
</triggers>
</component>
<component role="org.apache.cocoon.components.cron.CronJob/test-cron"
class="org.apache.cocoon.components.cron.CocoonPipelineCronJob"
logger="cron.pipeline">
<pipeline>samples/updateCacheContext.flow</pipeline>
</component>

E’ stato configurato un trigger con nome “pipeline-test” e target …/test-cron che verrà eseguito ogni minuto per tutti i giorni, per la precisione al decimo secondo di ogni minuto. Attenzione all’attributo concurrent-runs, indica al cron se può eseguire un nuovo comando anche se quello precedente ancora non è terminato.

Di seguito il trigger è configurato per invocare una semplice pipeline:

samples/updateCacheContext.flow.

Funziona tutto per bene, quindi anche stavolta non mi sono visto java.util.TimerTask.