MokaPackages è il framework configurabile in XML per la realizzazione di applicazioni web secondo il pattern MVC. Nato qualche anno fa come kernel minimale da utilizzare per lo sviluppo di applicazioni web secondo il modello MVC, si è sviluppato nel tempo fino a divenire uno strumento molto utile e completo.

Nel tempo ho seguito da vicino questo progetto sviluppandone direttamente alcune delle parti più importanti con il valido supporto dei collaboratori del team di MokaByte (un grazie particolare ad Andrea Romagnoli particolare utile per lo sviluppo di alcune parti quando ancora Struts non esisteva e MVC era solo un pattern).

La filosofia che sta alla base di questo sistema è la estrema semplicità con la quale e' possibile scrivere e configurare una applicazione web tramite l'uso di XML.

Fin da quando il progetto ha iniziato ad assumere la giustificazione di framework, ho nutrito il desiderio di dare maggior spazio a questo che per certi versi è un figlio, fratello del progetto MokaByte che resta il primogenito.

Il modulo centrale di MokaPackages fu pensato e realizzato prima dell'avvento di altri sistemi come Struts o WebWork, quando ancora non esistevano sistemi che permettessero la realizzazione di applicazioni web in modo semplice e personalizzabile tramite XML. Proprio la nascita e la diffusione di Struts e di altri sistemi molto famosi, mi ha fatto per lungo tempo dubitare sulla utilità del portare avanti questo progetto e sopratutto nella presentazione al grande pubblico. Struts ha alle spalle un team di sviluppatori di tutto rispetto e la popolarità che ha ormai acquisito è tale che ogni ipotesi di competizione rischia di essere semplicemente una fantasiosa utopia o un programmato suicidio imprenditoriale.

Perché un altro framework MVC?

Se è vero che MokaByte nel 1996 ha anticipato i tempi con la creazione della rivista web, è anche vero che pubblicare adesso un framework per la realizzazione di applicazioni web in MVC potrebbe suonare certamente anacronistico, in un momento in cui ci sono altri framework validi e universalmente accettati data la loro diffusione e rinomata validità.

Perché portare avanti questa idea e perché proporla al grande pubblico ora che Struts ha di fatto monopolizzato il mercato?

Dopo aver disquisito a lungo sulla convenienza di abbandonare il progetto o di proseguire in questa folle sfida, ci siamo convinti che, sebbene sia più sintetico rispetto a Struts, il nostro framework avesse delle potenzialità tali da indurci nel proseguire questa nostra iniziativa. Ma oltre alla bontà del prodotto (che in effetti è tutta da dimostrare agli occhi del lettore), l'obiettivo che qui ci prefiggiamo non è semplicemente quello di presentare un prodotto software, ma anche di dar vita ad una comunità di programmatori open source italiana.

MokaPackages verrà infatti presto rilasciato con licenza open source in modo da coinvolgere il maggior numero di persone per il suo sviluppo e crescita.

Visto l'interesse che ruota intorno alla rivista ed al numero di iscritti nella community, crediamo che la nascita di un gruppo di sviluppo open nel nostro paese sia una operazione troppo importante per non essere tentata.

Infine una considerazione non di poco conto: MokaPackages è un progetto italiano, con documentazione in italiano, applicazioni di esempio in italiano e supporto online in italiano.

Non mi è mai piaciuto sbilanciarmi in affermazioni forti o valutazioni categoriche, ma ovviamente se devo difendere la bontà della mia idea dovrò per forza cercare di espormi. Spero che i lettori non valutino l'iniziativa come un presuntuoso slancio di mania di grandezza del nostro gruppo.

Vediamo di dare un'idea del perché MokaPackages dovrebbe essere adottato o semplicemente valutato.

La lunga attività di articolista, di consulente, di docente per corsi Java (ed anche su Struts ovviamente), nonché di programmatore mi ha portato ad avere una buona esperienza nella valutazione di cosa sia utile e cosa non lo sia nello sviluppo di applicazioni web di tutti i giorni.

Questa esperienza è risultata particolarmente utile nella realizzazione di corsi, di libri (il nostro Manuale Pratico di Java ha il riconosciuto pregio di presentare tutto e solo il necessario per imparare in modo semplice ed efficace quello che serve per iniziare a sviluppare applicazioni professionali) e si è concretizzata anche nello sviluppo del framework.

Credo di poter dire senza falsità che il nostro sistema include al suo interno una serie di funzionalità che mancano altrove mantenendo la sufficiente semplicità necessaria a chi si avvicina a questo mondo per la prima volta.

Semplicità di configurazione e d'uso è quindi la parola d'ordine che ci ha spinto in tutto questo tempo. E' ovvio che questo si è pagato in termini potenza espressiva, per la quale si sarebbe richiesto lo sviluppo di un clone di Struts, operazione questa di sicuro insuccesso.

Dal mio modesto punto di vista Struts pur essendo un sistema molto potente, estremamente ricco di componenti e funzioni, manca di alcune caratteristiche base che sono necessarie nella maggior parte dei casi.

La filosofia open source basa le sue fondamenta sul concetto che dice tutto quello che non c'e' me lo posso scrivere a mano, ma un buon progetto open dovrebbe fornire una implementazione completa e facile da gestire delle funzionalità di base e più usate nella maggior parte delle applicazioni, lasciando al programmatore la estensione di quelle parti specifiche del progetto o del cliente.

Tanto per fare un esempio valuto del tutto carente la gestione dei ruoli utente, così come ritengo grave la mancanza di un sistema automatico di inizializzazione della applicazione web: sono queste alcune delle funzionalità necessarie nel 99% dei casi e per questo ritengo la loro limitatezza una mancanza non da poco.

Mokapackages invece parte da una impostazione completamente differente: costruito mattone dopo mattone in modo semplice limitando l'attenzione per prima cosa ai pezzi più comuni, offre la possibilità di crescere fino a diventare un framework completamente personalizzabile in ogni parte in modo modulare e scalare.

Ecco un breve elenco delle funzionalità già presenti:

  • Modello MVC configurabile tramite file XML che associano viste e moduli di business logic in modo astratto
  • Possibilità di passare parametri di configurazione alle action tramite il file di configurazione XML.
  • Modello sorgente ascoltatore fra azioni (se eseguo la loginAction vorrei in automatico scatenare altre operazioni di logica) configurabile in XML.
  • Disponibilità di alcuni moduli di default (il login generico); questa parte in particolare permetterà al sistema di crescere senza alterare troppo la filosofia di base
  • Completa gestione dei ruoli utente su azioni e su viste
  • Completo sistema automatico di inizializzazione basato su componenti (java beans) configurabili tramite XML (con dei semplici tag posso dire quali componenti voglio inizializzare allo start della applicazione o alla creazione di nuove sessioni)
  • Una sintetica ma completa custom tag library. Questa parte potrà crescere ancora, anche se i tag più importanti ci sono già tutti.
  • Gestione dei messaggi di errore tramite bundle: questa funzione è praticamente una copia di quanto avviene in Struts
  • Predisposizione per l'internazionalizzazione
  • Predisposizione per la validazione
  • Meccanismo di comunicazione inter-web application
  • Gerarchie fra ruoli di autenticazione

Gli ultimi quattro punti non sono ancora inseriti nella attuale release del framework, e dovrebbero essere inseriti con la prossima versione essendo in questo momento in fase di test.

Non elencheremo qui tutte le cose che differiscono rispetto a Struts dato che ci vorrebbe un libro intero. Alcune delle parti che al momento sono mancanti o per precise scelte progettuali (es. la possibilità di definire sotto applicazioni in strus-config.xml riteniamo che possa essere solo spunto per una maggiore confusione; lo stesso risultato si può ottenere con un altri strumenti più semplici), oppure perché in questo momento sono fase di test finale di prerilascio.

In questo articolo presentiamo il framework nelle sue componenti principali, con lo scopo di consentire a tutti di iniziare a sviluppare applicazioni. Prossimamente il progetto verrà rilasciato con sorgenti in licenza Open Source. Chiunque vorrà potrà studiarne il funzionamento dall'interno.

I componenti di MokaPackages

Secondo la terminologia di base del pattern MVC il sistema è composto essenzialmente da tre componenti: il controller, il model e la view. Senza entrare nel dettaglio dei vantaggi e della filosofia di base di MVC si procederà qui alla analisi dei vari componenti che lo costituiscono.

Nota: per gli esempi qui riportati si tenga presente che il package com.mokabyte.mvc fa riferimento a classi inserite direttamente nel framework, mentre il com.mokabyte.testmvc si riferisce a classi inserite nell'applicazione di esempio TestMVC allegata all'articolo.

Il model

Le operazioni di business logic fanno parte del cosiddetto model (M di MVC): in MokaPackages il model è costituito da una serie di classi Java che implementano una interfaccia particolare, la Action e che sono associate a URL pattern del tipo .run. Ogni link del tipo .run verrà associato ad una particolare classe tramite quanto definito nel file actions.xml; questo file ha una struttura simile all'altro (routing.xml usato per mappare i nomi sulle viste), ma è più articolato dato che consente di specificare più informazioni per ogni singola azione. Ecco un esempio:

<actions>
  <action NAME="logout.run" CLASS="com.mokabyte.mvc.actions.commons.LogoutAction">
    <executed PAGE="logout.jsp" FORWARD="true"/>
  </action>

  <action NAME="login.run" CLASS="com.mokabyte.mvc.actions.commons.PluggedLoginAction">
    <executed PAGE="login-ok.show"/>
    <failed PAGE="login-ko.show"/>
    <init-params>
      <init-param>
        <param-name>user-authenticator-classname</param-name>
        <param-value>com.mokabyte.mvc.users.DefaultUserAuthenticator</param-value>
      </init-param>
      <init-param>
        <param-name>temp-dir</param-name>
        <param-value>c:\temp</param-value>
      </init-param>
    </init-params>
  </action>
</actions>

Dato che ogni azione svolge il suo lavoro restituendo una risposta binaria, i tag &lt;executed&gt; e &lt;failed&gt; definiscono i routing associati alla risposta della action.

Ad esempio la comune operazione di login può avere successo oppure no.

In questo senso ad ogni azione viene associata alla risposta positiva un routing ed un altro a quella negativa: la prima si associa al mapping executed-routing l'altra al mapping failed-routing.

Il routing potrà essere il nome di una vista (se login va bene si mostra la vista login-ok.show che a sua volta sarà mappata su loginOk.jsp) oppure di una altra azione (in tal caso il risultato è l'esecuzione di due azioni in cascata).

Questo meccanismo per quanto possa apparire complesso o stranamente articolato permette di svincolare totalmente il codice Java da quello che accade dentro la classe da quello che accade fuori (quale routing verrà eseguito) portando ad una completa riutilizzabilità della action (si veda l'esempio della RoledAction nell'esempio allegato).

Struts da questo punto vista ha una gestione differente, che personalmente non condivido: all'interno della action si deve decidere quale routing eseguire (utilizzando un nome che viene mappato nel file di configurazione di Struts). In questo modo le cose sono un po' più semplici, ma si lega il codice Java della azione al nome che viene definito in XML. In questo modo (sebbene una oculata gestione dell'XML e delle azioni porta allo stesso risultato di MokaPackages) si corre il rischio di non avere disaccoppiamento fra codice di business logic (le azioni) e contesto di configurazione. L'azione di login ad esempio è quasi sempre presente in una web application, ma di volta in volta può essere associata a mapping differenti.

Per aggiungere una funzionalità di logica alla applicazione web sarà quindi sufficiente creare una nuova action e ridefinirne il metodo perform() che ha la seguente firma:

public Routing perform(HttpServlet httpServlet, HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws ActionException

Il controller, a seconda del tipo di URL invocato, ricava la azione corretta da eseguire la esegue e restituisce un risultato che poi verrà utilizzato per mostrare una determinata vista all'utente. Ad esempio il login può essere eseguito tramite la LoginAction all'interno della quale nel metodo perform() si effettuerà un accesso a database o altro sistema di persistenza per controllare le credenziali d'accesso.

Le switch action

Il fatto che le azioni abbiano risposte binarie implica, come si è potuto vedere, il benefico vantaggio di poter disaccoppiare del tutto il codice Java dal contesto di esecuzione (che in fondo è la filosofia di MVC). Si pone però il problema di gestire quei casi in cui una azione debba fornire risposte multiple in funzione dell'input fornito.

Questo problema è stato qui risolto utilizzando una particolare tipo di azione, la switch action che esegue in cascata n azioni: la prima azione che restituisce true (corrispondente al mapping executed-routing) come risultato fornisce il routine da eseguire.

Ecco un esempio di configurazione di una switch action:

<switchAction NAME="switch-delivery" CLASS="com.mokabyte.mvc.actions.DefaultAction">
  <switchCase>item-cc</switchCase>
  <switchCase>item-ct</switchCase>
  <defaultCode PAGE="select-delivery-type.show" FORWARD="true"/>
  <executeAll>false</executeAll>
</switchAction>

Nel caso specifico questa azione è presente nel nostro negozio online e serve per decidere il tipo di operazione da eseguire in funzione del tipo di spedizione scelta per la consegna di un libro comprato.

I codici &lt;switchCase&gt; devono corrispondere a codici di azioni precedentemente definite, altrimenti verrà generato una eccezione.

Nell'esempio riportato il link switch-delivery provoca l'esecuzione delle azioni associate ai codici item-cc e item-ct.

La prima azione che restituisce true fornirà il routing di risposta. Se il flag executeAll è impostato a true allora verranno eseguite tutte le azioni: se più di una azione restituisse true allora verrà generata una eccezione.

La classe associata ad una switch action non svolge nessuna operazione particolare: la sua presenza è necessaria esclusivamente per permettere la creazione del costrutto switch. Per questo si può genericamente utilizzarne una di default, la SwitchActionImpl.

Le fire actions

Nel caso in cui si vogliano eseguire n azioni in cascata all'esecuzione di una determinata azione di base si possono usare le fire actions. Ad esempio se in conseguenza dell'operazione di login si volesse anche eseguire una operazione di notifica verso il sistema dell'accesso di un utente (ad esempio per gestire un sistema di log sugli accessi) si potrà usare questo sistema: la LoginAction diventa una fire action alla quale si possono associare n azioni listeners che verranno eseguite in cascata dopo la login. Il meccanismo è quello tipico del modello sorgente ascoltatore, anche se in questa particolare implementazione l'esecuzione delle classi in cascata è sincrono (ovvero fino a che tutte le azioni dipendenti non hanno terminato la loro esecuzione allora nemmeno la fire action termina la sua esecuzione). Nella prossima implementazione del framework il legame fire action-listeners dipendenti sarà gestito in modalità asincrona multithreaded.

Conclusioni

MokaPackages rappresenta un tentativo di creare un framework MVC italiano, semplice da usare e configurare, con funzionalità che riteniamo manchino in altri framework simili. Il progetto verrà rilasciato come open source per permettere alla comunità di contribuire al suo sviluppo e miglioramento.