sviluppo di una interfaccia grafica basata sul linguaggio pql per
Post on 07-Jan-2017
230 Views
Preview:
TRANSCRIPT
Anno Accademico 2013/2014
Corso di laurea Magistrale in Ingegneria Informatica
Tesi di laurea
Sviluppo di una interfaccia grafica
basata sul linguaggio PQL per
interrogazioni di modelli di processo in
Apromore
Relatore
Prof. Giancarlo FORTINO
Candidato
Relatore
Prof. Marcello LA ROSA
Candidato
Luigi CORNO
matr. 153260
~ 1 ~
Abstract
Apromore è una piattaforma web sviluppata per la gestione e il salvataggio di
modelli di processo. Una caratteristica unica di questa piattaforma è la sua
capacità di salvare e lavorare con modelli scritti in diversi linguaggi (BPMN,
YAWL, EPML, ecc). Le operazioni che Apromore mette a disposizione degli
utenti riguardano principalmente l’analisi dei modelli presenti nel repository,
vedasi la similarity search e il clustering (operazioni queste che si soffermano solo
sulla struttura dei modelli). Un’interessante problematica di ricerca, riguarda la
definizione e sviluppo di procedure di analisi della semantica dei modelli, al fine
di comprendere quando due differenti modelli facciano riferimento al medesimo
contesto. Affinché sia possibile raggiungere tale obiettivo, bisogna innanzitutto
trovare un modo per interrogare un insieme di modelli. Purtroppo però, i
“normali” linguaggi di interrogazione per modelli di processo non sono
sufficienti, dato che si concentrano solo sulla struttura dei modelli stessi. Nel
presente lavoro di tesi si descriverà l’integrazione del linguaggio PQL (un
linguaggio di interrogazione per modelli di processo sviluppato da un ricercatore
del QUT) in Apromore ed in particolare si proporrà la progettazione ed
implementazione di un’interfaccia grafica capace di supportare tale linguaggio.
~ 2 ~
Sommario Abstract ............................................................................................................................... 1
Introduzione ........................................................................................................................ 4
Ringraziamenti .................................................................................................................... 6
CAPITOLO 1: Apromore (Advanced Process Model Repository) ........................................ 7
1.1 Zk ............................................................................................................................... 8
1.1.1 Background: Static-HTML e applicazioni puramente AJAX ................................ 8
1.1.2 Zk: Cos’è e cosa non è ...................................................................................... 11
1.1.3 Zk: Architettura del framework ....................................................................... 13
1.1.4 Components, Pages e Desktop ........................................................................ 16
1.1.5 Ciclo di vita dei componenti ............................................................................. 18
1.1.6 Performance: Zk vs JSF (Java ServerFaces) ...................................................... 21
1.2 Apache Maven ........................................................................................................ 25
1.2.1 Convenzione sulla configurazione ................................................................... 25
1.2.2 Interfaccia comune .......................................................................................... 27
1.2.3 A cosa servono i plugin Maven? ...................................................................... 27
1.2.4 POM (Project Object Model) ............................................................................ 28
1.2.5 Plugin ................................................................................................................ 33
1.2.6 Ciclo di vita di Maven ....................................................................................... 34
1.2.7 Sistema di gestione delle dipendenze.............................................................. 36
1.2.8 Riepilogo .......................................................................................................... 38
1.3 Spring framework .................................................................................................... 38
1.3.1 Architettura di Spring ....................................................................................... 40
1.3.2 Dependency Injection ...................................................................................... 44
1.3.3 Spring AOP (Aspect Oriented Programming) ................................................... 47
1.4 Architettura ............................................................................................................. 52
1.5 CPF (Canonical Process Format).............................................................................. 55
1.6 Interfaccia Apromore .............................................................................................. 60
1.7 Vantaggi nell’uso di Apromore ............................................................................... 66
CAPITOLO 2: PQL: A Process Query Language .................................................................. 69
2.1 Sintassi .................................................................................................................... 71
2.2 Semantica di PQL .................................................................................................... 79
~ 3 ~
2.3 Esempi di interrogazioni PQL .................................................................................. 83
Progettazione modulo per PQL ......................................................................................... 87
3.1 Specifiche ................................................................................................................ 87
3.2 Integrazione modulo PQL nell’architettura Apromore ......................................... 101
3.3 Progettazione ........................................................................................................ 110
3.4 Progettazione modulo PQL ................................................................................... 111
3.4.1 FolderProcessTree: l’albero dei modelli di processo ..................................... 116
3.4.2 VariableText: dichiarazione delle variabili ..................................................... 120
3.4.3 QueryText: definizioni dell’interrogazione .................................................... 122
3.4.4 ButtonAction: shortcut per aiutare l’utente .................................................. 129
3.4.5 TableProcess: la tabella delle versioni ........................................................... 130
3.4.6 Salva/Carica: memorizzazione e riuso vecchie interrogazioni ....................... 133
3.5 Portal-client: l’applet comunica col portale .......................................................... 135
3.5.1 TabQuery: come vengono rappresentati i risultati ........................................ 139
3.6 Indicizzazione dei modelli di processo .................................................................. 142
3.6.1 Multi-threading: indicizzazione più efficiente ............................................... 147
Conclusioni ...................................................................................................................... 151
Bibliografia ...................................................................................................................... 153
~ 4 ~
Introduzione
BPM, o Business Process Management, è un approccio sistematico mirato ad
analizzare e migliorare i risultati derivanti da un processo in seno ad
un’organizzazione o ad un’azienda. Definire il concetto “migliorare i risultati di
un processo”, può risultare più difficile di quanto si pensi. Infatti, in dipendenza
del processo che l’azienda vuole analizzare tramite le tecniche di BPM, migliorare
può riferirsi al tempo con cui un processo è eseguito, alla quantità di prodotti alla
fine della lavorazione, al soddisfacimento di determinate condizioni, ecc. [16]
Naturalmente, l’obiettivo che si vuole conseguire non dipende solo dal tipo di
processo che si sta analizzando, ma dipende anche dalle specifiche dettate
dall’azienda in cui quel processo si svolge. Oltretutto non è pensabile, ma neanche
possibile, analizzare un processo a fondo senza avere a disposizione determinati
tool o senza avere delle precise regole di schematizzazione dei processi. Per
ovviare a queste “carenze”, il BPM adotta non solo software mirati per l’analisi
dei modelli di processo, ma soprattutto si basa su un vero e proprio standard di
rappresentazione dei modelli.
I modelli di processo costituiscono un aiuto cospicuo nella gestione di un ente, sia
esso privato o pubblico, grazie alla loro capacità schematica di riuscire a
rappresentare ciò che davvero accade in un’organizzazione. I loro punti di forza
sono sicuramente la facilità di lettura, anche per chi non è un esperto di BPM,
nonché la presenza di standard di rappresentazione (come scrivere le etichette in
un’attività di lavoro, o come rappresentare transizioni temporali). Di contro però,
esistono molte varietà di linguaggi (BPMN, EPML, YAWL, ecc.) mediante i quali
poter rappresentare un modello, e questo crea grossi problemi in fase di analisi e
salvataggio degli stessi. Infatti, pur rappresentando lo stesso processo o comunque
avendo due modelli di processo simili, le rappresentazioni in due diversi linguaggi
possono variare anche sostanzialmente. Inoltre essendo scritti in linguaggi diversi,
~ 5 ~
non si ha la possibilità di salvarli mediante un unico programma né tantomeno si
riesce ad analizzarli.
Una soluzione a questi problemi è la piattaforma web Apromore [12], che grazie
alla creazione di un linguaggio che rappresenta solo la struttura dei modelli, è
riuscita a fornire all’utente validi strumenti di analisi, nonché un database in grado
di salvare tutti i modelli in diversi linguaggi. In questo modo l’utente è in grado di
poter confrontare modelli e stabilirne la somiglianza, raggrupparli secondo
algoritmi di clustering, effettuare persino interrogazioni che possano restituire i
modelli rispondenti a determinati criteri, senza dimenticare naturalmente la
creazione, il salvataggio e l’importazione dei modelli stessi.
Nella presente tesi, si proporrà un’ampia panoramica circa le funzionalità della
piattaforma web Apromore, ponendo particolare attenzione sulle tecnologie che
usa e che consentono alla piattaforma di avere notevoli prestazioni (Capitolo 1).
Successivamente l’attenzione si sposterà sulla formulazione di un linguaggio di
interrogazione (PQL) capace di analizzare la semantica di un modello e che dia la
possibilità di poter confrontare in virtù di ciò una grande quantità di modelli nel
modo più fedele possibile (Capitolo 2). Infine, si descriverà la progettazione e lo
sviluppo di un’interfaccia grafica capace di esaltare le potenzialità del linguaggio
PQL, principale contributo della presente tesi. Infine (Capitolo 3). Infine si
presenteranno le conclusioni e alcune direzioni di sviluppi futuri.
~ 6 ~
Ringraziamenti
Desidero ricordare tutti coloro che mi hanno aiutato nella stesura della tesi con
suggerimenti, critiche ed osservazioni: a loro va la mia gratitudine, anche se a me
spetta la responsabilità per ogni errore contenuto in questa tesi.
Ringrazio anzitutto i professori Giancarlo Fortino e Marcello La Rosa, Relatori,
per il loro supporto e la loro guida nella stesura della tesi, ma soprattutto per
avermi dato l’opportunità di svilupparla in Australia.
Proseguo ringraziando i dottori Raffaele Conforti e Artem Polyvyanyy,
Supervisori, per la pazienza dimostrata durante lo sviluppo del mio progetto di
tesi.
Vorrei ringraziare la mia famiglia, sempre pronta a sostenermi durante questi
lunghissimi anni di università. Grazie per i numerosi sacrifici che mi hanno
permesso di arrivare a questo giorno. Grazie per il grandissimo affetto che mi
avete dato, e che spero continuerete a darmi. Questa tesi è soprattutto dedicata a
loro, con la speranza che sia solo la prima delle numerose soddisfazioni che potrò
offrirgli in futuro.
Grazie a alla famiglia Straface: Santo, Luisa, Samuele, Marika e Mirko, che mi
hanno visto crescere e che sono come una seconda famiglia.
Un sentito “Grazie!” va ai miei amici: Andrea, Carmine, Elina, Emanuele,
Fabiola, Ferdinando, Francesca, Iacopo, Ivan, Karim, Marika, Manuel, Pierluigi,
Roberta; grazie per i tanti momenti passati insieme, per avermi contagiato con la
loro voglia di scoprire il mondo ma soprattutto per esserci stati quando ne avevo
bisogno.
Infine, ma non perché meno importanti, vorrei ringraziare tutte quelle persone
conosciute durante questi sei anni di università: Antonio, Emanuele, Francesco,
Gabriele, Giacomo, Nicola, Romualdo, Rosario (Barresi), Rosario (Bova),
Simone, Virginio; grazie per avermi tenuto compagnia in questi anni, per la
vostra ospitalità e per avermi “svegliato”.
Spero di non aver dimenticato nessuno, ma qualora così fosse, un grazie anche a
te che in qualche modo hai contribuito a rendermi come sono.
~ 7 ~
CAPITOLO 1: Apromore (Advanced Process Model
Repository)
La modellazione dei processi di business è diventata una forma molto popolare di
modellazione concettuale. Un modello di processo descrive, spesso attraverso
notazioni grafiche, come una certa procedura sia in realtà formata da diverse
azioni, che tipo di risorse vengono utilizzate da ogni azione, e quale sia lo scopo
ultimo di questo processo. Tali processi possono far riferimento ad una varietà di
ambiti anche molto diversi tra loro: dal settore IT a quello commerciale,
dall’ambito sanitario a quello dei trasporti.
Grazie alla diffusione di applicazioni che permettono di modellare in veste grafica
un processo aziendale, molte organizzazioni hanno iniziato a produrre centinaia, e
a volte perfino migliaia, di modelli rappresentanti le loro attività. Come
conseguenza di questo uso quasi indiscriminato di rappresentazione di modelli, le
varie organizzazioni hanno avuto la necessità di trovare applicazioni in grado di
immagazzinare questa grande mole di informazioni, ma soprattutto un modo per
poter analizzare ed elaborare tali dati. Inoltre un’ulteriore difficoltà per le aziende
è rappresentata dalla varietà di linguaggi con cui un modello può essere espresso
in via grafica. Infatti pur esistendo uno standard a livello internazionale (il BPMN
2.0 standardizzato da OMG nel gennaio 2011), alcune aziende hanno modelli
scritti in altri linguaggi (YAWL, EMPL, ecc.) che non vengono tradotti in BPMN
magari per motivi di tempo o perché semplicemente non lo ritengono necessario.
In tale contesto è stato sviluppato Apromore (Advanced Process Model
Repository), un open-source SaaS (Software as a Service) in grado di offrire una
serie di tecniche per il salvataggio, l’analisi e la visualizzazione del contenuto
informativo dei modelli di processo. La scelta di rendere Apromore open-source
mira ad incoraggiare coloro i quali vogliono aggiungere funzionalità al repository,
ma anche quegli enti a cui esso è rivolto, che possono usufruire di tutti i tool già
presenti, in maniera del tutto gratuita.
~ 8 ~
A seguire analizzerò sia i framework usati per lo sviluppo di Apromore (Zk,
Maven e Spring), che la struttura della piattaforma web, soffermandomi non solo
sull’architettura, ma avendo un occhio di riguardo su cosa lo rende diverso da altri
tool commerciali (in particolare come la piattaforma riesca a gestire ed analizzare
modelli di processo scritti con diversi linguaggi di rappresentazione, senza che sia
necessario effettuare traduzioni da un linguaggio ad un altro).
1.1 Zk
1.1.1 Background: Static-HTML e applicazioni puramente AJAX
Le prime applicazione Web erano
basate sul modello di comunicazione
stateless, ovvero un modello in cui una
pagina web conteneva tutte le
informazioni visualizzate all’utente ed
era scambiata tra il client ed il server.
Come si può vedere in [Figura 1], ogni
richiesta da parte del client era
elaborata grazie ad una specifica
servlet (ovvero un programma java
residente sul server in grado di gestire
determinate richieste del client), che finita la sua elaborazione reindirizzava ad
un'altra pagina web.
Un approccio di questo tipo però, non era in linea con le esigenze dei client. Basti
pensare che anche per semplici operazioni, era necessario ricorrere a più pagine
statiche, ognuna delle quali conteneva uno step dell’operazione richiesta
dall’utente. Così facendo era impossibile per l’utente avere una visione globale di
ciò che stava facendo.
Ricorrendo ad un esempio, se un cliente richiedeva un preventivo per l’acquisto di
un oggetto, doveva aprire una pagina per la ricerca, successivamente occorreva
Figura 1 - Ottenimento di nuove pagine web statiche mediante servlet
~ 9 ~
aprirne una seconda per la visualizzazione dei prezzi, ed infine una terza per
effettuare l’acquisto. Ciò risultava in utenti confusi e scontenti. Parlando invece di
aspetti maggiormente tecnici, si vede come il server sia oberato di operazioni da
effettuare: dal processare la richiesta dell’utente alla visualizzazione delle varie
pagine, dalla gestione degli errori alla gestione dei link presenti nelle pagine. [2]
Per avere prestazioni migliori, ma soprattutto per creare applicazioni che si
avvicinassero maggiormente ai bisogni dell’utente, le pagine web hanno subito
una graduale evoluzione: da statiche sono diventate dinamiche, e quest’ultime
hanno lasciato spazio alle applicazioni AJAX (Asynchronous JavaScript and
XML). Questa evoluzione, oltre ad essere dettata dalla necessità di avere
applicazioni leggere lato server e con prestazioni elevate, fu resa necessaria anche
per avvicinarsi alle esigenze degli utenti. Infatti, col passare del tempo non solo la
mole d’informazioni con cui l’utente doveva lavorare aumentava, ma soprattutto
le operazioni da svolgere online diventavano più complesse. Inoltre, questi due
fattori mal combaciavano con applicazioni sviluppate su più pagine web, che
risultavano essere molto dispersive. Il passo successivo, fu così l’introduzione di
applicazioni AJAX tramite cui l’utente era in grado di ottenere prestazioni simili a
quelle di un’applicazione desktop
sul web, avendo al contempo la
possibilità di visualizzare grandi
quantità d’informazioni in una
pagina (garantendo una visione
globale sul contesto al quale si
lavorava). [Figura 2]
Dal punto di vista tecnico, AJAX
riesce a cambiare dinamicamente il
contenuto di una pagina web in base
alle esigenze dell’utente. Per farlo,
usa il linguaggio di scripting
JavaScript, tramite cui riesce a Figura 2 - Aggiornamento contenuti in pagine web dinamiche
~ 10 ~
catturare e gestire gli eventi generati dall’utente, ed in base a tali eventi, a
ristrutturare il contenuto della pagina in cui si trova l’utente. Naturalmente a
mutare, non saranno solo le informazioni contenute nella pagina, ma anche la sua
struttura HTML (il cosiddetto Document Object Model o DOM). Mediante tali
operazioni si evita di dover richiedere ogni volta una pagina al server, ma si
richiedono solo le nuove informazioni da visualizzare. Inoltre, per rendere
flessibile e leggero lo scambio d’informazioni col server, si usa XML, in modo da
poter strutturare i dati scambiati tra client e server secondo le necessità del
programatore. Le comunicazioni tra i due lati dell’applicazione, inoltre,
avvengono in maniera asincrona, aumentando così le prestazioni
dell’applicazione, nonché evitando all’utente frequenti periodi di attesa (nel caso
di pagine web statiche, ogni qualvolta l’utente effettuava un’operazione, veniva
inviata una richiesta al server che rispondeva inviando una nuova pagina. Questo
meccanismo costringeva l’utente ad aspettare ogni volta che l’intera pagina fosse
scaricata prima di poter nuovamente riprendere ad interagire con essa). Una
precisazione, non bisogna farsi fuorviare eccessivamente da “AJAX usa
JavaScript o XML”. Infatti, ai programmatori è consigliato il loro uso soprattutto
per la compatibilità con la maggior parte dei browser web, ma nessuno vieta
l’utilizzo di un qualsiasi linguaggi di scripting o di trasmissione dati sul web.
Naturalmente rientrerà poi nelle mansioni del programmatore adattare la sua
applicazione ai browser, magari attraverso l’uso di plugin.
Nonostante AJAX riesca ad offrire buone prestazioni, purtroppo però ci sono
alcuni lati negativi che hanno spinto col tempo sempre più programmatori a
trovarne una valida alternativa. Alcune delle maggiori limitazioni sono: la
notevole complessità che l’intera architettura ha per i neofiti di AJAX, la necessità
di ricorrere a JavaScript per l’elaborazione del DOM delle pagine web nonché per
lo scambio di informazioni col server, ed infine la necessità di replicare i dati e
parte della logica dell’applicazione sul browser (questo per poter consentire una
migliore interazione utente-applicazione, con tutte le conseguenti problematiche
inerenti alla consistenza dei dati ed al loro aggiornamento). Dunque le pagine
AJAX sembrano mantenere di fondo le medesime problematiche di cui le vecchie
~ 11 ~
applicazioni page-based soffrivano, anche se con risultati indiscutibilmente
migliori.
Occorre trovare una soluzione che abbia prestazioni simili, se non migliori a
quelle offerte da AJAX, ma che riesca al contempo a superarne le limitazioni
esposte in precedenza. Una valida alternativa sembra essere costituita da Zk (una
framework sviluppato dalla Potix Corporation) che sta riscuotendo un particolare
successo. Tale piattaforma, pur avendo elementi in comune con AJAX, adotta un
approccio differente che mira soprattutto alle prestazioni e alla semplicità nello
sviluppare un’applicazione anche da parte dei neofiti di applicazioni sul web.
1.1.2 Zk: Cos’è e cosa non è
Zk è un framework event-driven basato su componenti ad hoc che permette lo
sviluppo di R.I.A. (Rich Application Web), ovvero applicazioni web dinamiche
anche molto complesse. Gli elementi caratterizzanti tale framework sono tre: un
motore AJAX event-driven, una vasta gamma di tag nei linguaggi XUL [Codice
3] e XHTML [Codice 2] e un linguaggio di markup chiamato ZUML [Codice 1],
o ZK User Interface Markup Language (tutti i precedenti linguaggi sono basati sul
formato eXtensible Markup Language o XML).
Se uno sviluppatore avesse intenzione di costruire un’applicazione web tramite
Zk, non deve far altro che costruire la grafica mediante i tag XUL, XHTML e
ZUML e definire le eventuali azioni in risposta agli eventi generati dall’utente.
Tutto il resto è svolto dal motore AJAX di cui è fornito Zk, dalla sincronizzazione
dei contenuti della pagina rispetto alle informazioni contenute lato server al
processamento degli eventi.
In questo modo, si eliminano
a monte il problema di
decidere cosa elaborare lato
client e cosa elaborare
invece lato server, come avveniva nelle applicazioni AJAX pure. Inoltre non si
Codice 1
~ 12 ~
hanno problemi di consistenza dei dati dovuti alla loro replicazione, dato che
adesso tutto ciò che riguarda i dati è contenuto lato server al servizio del motore
AJAX di cui dispone Zk. [3]
Tutto questo permette di avere prestazioni paragonabili ad un'applicazione
desktop, sia per quanto riguarda la velocità di risposta ad un’azione da parte
dell’utente, sia per quanto concerne la facilità nel programmare una pagina per la
propria applicazione. Inoltre, per quanto riguarda la creazione della grafica per
una pagina web, l’utente può ricorrere a diverse soluzioni: programmare
interamente in XML (utilizzando quindi i tag XUL, XHTML oppure usando
entrambi nella stessa pagina grazie al linguaggio ZUML), programmare
interamente in Java (creando quindi una classe che eredita da un container Zk,
Codice 2
Codice 3
~ 13 ~
come descritto nelle API online), oppure poter fondere le due tipologie precedenti.
Anche se per adesso si è fatto riferimento solo agli elementi di base di Zk, il
medesimo discorso può essere fatto per gli elementi sviluppati dal programmatore
(siano essi scritti in XML o in Java). Infatti, esattamente come nei linguaggi di
programmazione ad oggetti, si può creare un nuovo componente partendo da un
elemento base. In questo modo si può ampliare il set di tag a cui un
programmatore può attingere, che non sarà costretto ad inventarsi soluzioni
sempre nuove che si avvicinino alle sue esigenze. Infine, si possono introdurre
espressioni EL (o Expression Language, una sorta di linguaggio ispirato a xpath
ed user-friendly capace di accedere ad oggetti esterni direttamente dalla pagina
web) nonché codice di scripting (che può essere di qualsiasi tipo, da Java a PHP,
da JavaScript a Groovy, ecc.) all’interno di una pagina ZUML (da notare come
tale codice insito nella pagina ZUML, venga poi eseguito lato server e non lato
client, in ordine a ciò che prima si è accennato). [14]
Purtroppo, come le soluzioni viste in precedenza, anche ZK soffre di alcune
pecche, di cui le maggiori riguardano: il non disporre di sistemi di persistenza dei
dati e di comunicazione inter-server; il non fornire all’utente un modo per far
comunicare il lato client col lato server (non si può cioè disporre di tunnel quali
RMI o API per scambiare informazioni tra client e server); il non forzare l’utente
ad usare il pattern MVC per la costruzione delle proprie applicazioni (anche se
quest’ultimo aspetto è da rimandare sicuramente alle capacità dello sviluppatore).
1.1.3 Zk: Architettura del framework
Come già accennato, Zk include un sistema AJAX per automatizzare
l’interattività tra i vari componenti architetturali, un vasto insieme di componenti
XUL e un linguaggio di markup per semplificare lo sviluppo della propria
applicazione. Questi tre aspetti del framework, si rispecchiano nei tre componenti
fondamentali di cui è costituita l’architettura:
~ 14 ~
ZK Loader: componente responsabile del caricamento e
dell’interpretazione di una pagina Zk. La selezione della pagina è
effettuata mediante l’URL Request proveninete dal client browser. Il
risultato è una pagina HTML che viene inviata al client pronta per essere
visualizzata;
Zk Client Engine: componente che invia richieste “ZK Request” ed ottiene
risposte “ZK Response” dal server. Tramite queste risposte, l’albero DOM
della pagina da visualizzare è aggiornato lato client. E’ la parte adibita alla
cattura degli eventi generati dall’utente sulla pagina, che a sua volta li
invia al ZK AU Engine per ottenere una risposta. Pertanto, si può
affermare che il Client Engine, è la parte client di AJAX;
ZK AU (Asynchronous Update) Engine: è la parte server di AJAX ed ha il
compito di processare gli eventi generati dall’utente. Seguendo le
istruzioni specificate a priori dal programmatore, si generano messaggi che
inviati lato client permettono la modifica del DOM, e il conseguente
aggiornamento della pagina sul browser. Da notare come questo processo
non sia sincrono, come specificato peraltro nel nome del componente, e
dunque non costringe l’utente a rimanere bloccato in attesa dell’update
della pagina;
In [Figura 3] è mostrato il processo di richiesta, acquisizione, interazione e
aggiornamento di una pagina Zk con cui un utente lavora, e a seguire una breve
descrizione di come siano gestite le richieste e le risposte dal motore AJAX. [6]
~ 15 ~
Figura 3 – Architettura framework Zk
1. Quando un utente digita un URL o clicca su un collegamento ipertestuale,
una richiesta è inviata al Web Server. IL Zk Loader prende in consegna
tale richiesta.
2. Lo Zk Loader carica la specifica pagina e la elabora, in modo da costruire i
componenti al suo interno.
3. Dopo averla interpretata, lo Zk Loader trasforma la pagina in una HTML
web-page pronta per essere inviata al Zk Client Engine.
4. Lo Zk Client Engine è adibito al riconoscimento delle azioni da parte
dell’utente. Così quando esso individua un cambiamento di valore, un
click del mouse o un qualsiasi altro evento, il Zk Client Engine lo cattura e
lo invia al Zk AU Engine tramite una Zk Request.
5. Una volta ricevuta una Zk Request dal Client Engine, l’AU Engine
aggiorna il contenuto del componente che ha generato l’evento, salvo poi
notificare l’applicazione tramite il corrispondente event-handler.
6. Se l’applicazione deve cambiare il contenuto di un componente,
rimuoverlo o aggiungere di nuovi, allora lo Zk AU Engine notifica lo Zk
Client Engine tramite una Zk Response.
~ 16 ~
7. Le Zk Response contengono al loro interno comandi su come cambiare
l’albero DOM della pagina che l’utente sta visionando.
1.1.4 Components, Pages e Desktop
Dopo aver visto il funzionamento dell’architettura Zk, mi concentro ora su come
costruire un’interfaccia grafica, iniziando dai tasselli fondamentali: componenti,
pagine e desktop [Figura 4].
Figura 4 – Struttura di una pagina web in Zk
Per quanto riguarda i componenti, essi sono gli elementi di base che permettono la
costruzione di una pagina (label, button, textbox, ecc). Lo sviluppatore
combinando tali elementi, e creandone anche di nuovi basandosi sugli elementi di
base, è in grado di costruire la propria interfaccia grafica. Tali componenti però,
devono essere inseriti in una Page per poter essere visualizzati nel browser, e tale
operazione è effettuata dallo Zk Loader nel momento in cui va a interpretare la
page.
Una particolarità di Zk è però il componente Desktop, creato per poter contenere
una o più pagine che servono la medesima Zk Request. In questo modo si evita di
dover fare più chiamate al server risparmiando in termini di tempo. Naturalmente
una qualsiasi azione dell’utente, se prevista da un gestore degli eventi, è in grado
di rimuovere o aggiungere una o più pagine da un Desktop, garantendo ancora una
volta un'unica chiamata al server ed al Zk AU Engine. Questo approccio mira
~ 17 ~
soprattutto a non appesantire il server
con chiamate multiple quando in realtà
ne basta semplicemente una.
Una piccola parentesi ivi è doverosa.
Come già accennato precedentemente,
lo sviluppatore può scegliere se
costruire la grafica mediante classi
java, usando i componenti forniti da
XHTML, ZUL o ZUML, oppure unire i
due approcci in accordo alle proprie
esigenze. Ebbene qualunque sia la scelta dello sviluppatore, la pagina web che
l’utente finale vedrà sembrerà essere fatta interamente in HTML, nascondendo
così l’implementazione che vi è dietro. Inoltre, in ogni pagina possono essere
presenti tutti i componenti del framework e anche più componenti radice
(componenti cioè che sono figli direttamente al tag Page). Viceversa non è detto
che ogni componente possa avere come figli tutti i componenti forniti dal
framework (vedasi come esempio il componente Listbox che accetta solo
determinati componenti come figli e non tutti). Inoltre, ogni componente è fornito
di un identificatore, che può essere dato dall’utente o viene generato
automaticamente in fase di creazione della pagina. Questo permette allo
sviluppatore di poter acquisire un determinato componente tramite un’espressione
EL o mediante metodi Java appositi, e cambiarne il contenuto o il layout.
Attenzione però, questi identificatori sono univoci solo all’interno di un ambito,
che può essere anche il semplice componente all’occorrenza. Questo per
permettere allo sviluppatore di usare lo stesso ID per diversi componenti
appartenete a diversi ambiti, anche se tali componenti si trovano nella stessa Page.
In [Figura 5] si può vedere come nella Page P, esistano tre ambiti: P, A, C. I
componenti figli di P sono: A, C, F e G; i componenti figli di A sono: B, C, D; i
componenti figli di C sono: E. All’interno di ambiti diversi si possono avere due
componenti che hanno come id X (ad esempio il componente F e il componente
B, appartenendo ad ambiti diversi possono avere entrambi ID pari a X). Viceversa
Figura 5 – Esempio di ambiti in una pagina Zk
~ 18 ~
se due componenti appartengono al medesimo ambito, essi devono avere ID
diverso (ad esempio B e D che appartengo ad A, non possono avere X come ID).
Inoltre se un “componente ambito” ha come figlio un altro “componente ambito”
(vedasi la relazione tra A e C), allora i figli del secondo ambito non appartengono
al primo ambito (il componente E appartiene all’ambito C soltanto e non ad A.
Rimarrà comunque discendente di A come tag XHTML, ZUL o ZUML, ma
semplicemente questo).[15]
Come ulteriore metodo per l’identificazione di un componente, esiste l’UUID
(Universal Unique ID), che è poco usato dagli sviluppatori e serve in realtà allo
Zk Client Engine per poter individuare il componente che deve essere cambiato a
seguito di un evento. Tale identificatore non può essere assegnato dall’utente, ma
è generato automaticamente dallo Zk Loader in fase di costruzione della pagina,
ed è immutabile. In questo modo la logica lato server potrà interagire con la
grafica automaticamente senza il coinvolgimento dello sviluppatore.
1.1.5 Ciclo di vita dei componenti
Si passa ora ad analizzare le varie fasi che occorrono allo Zk Loader per la
creazione dei componenti in una pagina.
1. Page Initial Phase: ZK processa le istruzioni chiamate “init”, ovvero
quelle istruzioni da effettuare quando il componente non è ancora stato
creato e la pagina a cui quest’ultimo appartiene non è stata aggiunta ad un
Desktop. Se tutti i componenti di una pagina sono creati tramite Java, e
non tramite ZUML, allora questa fase è tralasciata.
2. Component Creation Phase: è la fase in cui lo Zk Loader interpreta una
pagina ZUML, creando ed inizializzando i componenti delle varie pagine.
Questa fase si suddivide a sua volta in più parti:
a. Per ogni elemento viene calcolato il valore degli attributi if e
unless, qualora siano presenti. Se le condizioni non sono rispettate,
~ 19 ~
allora l’elemento che ha la condizione booleana, così come tutti i
suoi discendenti, sono ignorati.
b. Se è specificato l’attributo forEach con una collezione di elementi,
allora i seguenti passi sono ripetuti per ogni elemento appartenente
alla suddetta lista.
c. Il componente viene creato basandosi sul nome del tag (<textbox>,
<listbox>, …), oppure in base alla classe Java specificata
nell’attributo use presente nel tag corrente.
d. Si inizializzano i membri uno ad uno basandosi sull’ordine degli
attributi specificati nella pagina ZUML.
e. Si interpretano gli elementi innestati del tag corrente e si ripetono
le operazioni precedentemente esposte.
f. Si invoca il metodo afterCompose qualora qualche elemento ne sia
provvisto, in modo da effettuare le operazioni in esso specificate.
g. Quando ogni componente è stato creato nella pagina, si invia il
metodo onCreate al rispettivo componente per permettere
l’inizializzazione del contenuto di alcuni componenti.
3. Event Processing Phase: Zk invoca ogni ascoltatore per ogni evento nella
coda degli eventi per il Desktop corrente. Tale operazione è effettuata
mediante un thread separato che scorre il DOM della pagina e per ogni
elemento ne controlla il relativo ascoltatore. In questo modo si consente
all’utente di fermare il thread, e quindi la gestione degli eventi in esso
contenuti, per permettere ad altri eventi di poter essere eseguiti perché
maggiormente prioritari (come forse si sarà intuito, ad ogni Desktop è
affidato un proprio thread che processa gli eventi al suo interno. In questo
modo si dà libertà al programmatore di scegliere quali eventi di quali
Desktop far concludere prima).
~ 20 ~
4. Rendering Phase: dopo aver processato tutti gli eventi, Zk raffigura
questi componenti in una pagina HTML e invia tale pagina al browser.
Le suddette fasi, ad eccezione della prima e della seconda che sono prettamente
usate per la creazione di una pagina, sono riprese con qualche modifica anche nel
caso in cui occorra aggiornare una pagina a seguito della generazione di un
evento. Si avrà così:
1. Request Processing Phase: lo ZK AU Engine aggiorna i contenuti dei
componenti interessati dalla richiesta, in modo che le informazioni
contenute lato server siano allineate con quelle contenute lato client.
2. Event Processing Phase: svolge la medesima funzione espressa nella
creazione di una pagina.
3. Rendering Phase: dopo che tutti gli eventi sono stati processati, Zk
raffigura i componenti aggiornati, genera le corrispondenti Zk Response, e
invia le corrispondenti risposte al client. In questo modo il Client Engine
può aggiornare l’albero DOM della pagina (senza aver bisogno di creare
una nuova pagina, ma cambiando semplicemente alcuni tag).
Urge però una piccola precisazione su come sia gestita la distruzione dei
componenti che, magari a seguito di un evento, sono rimossi da una pagina. Fin
tanto che l’elemento non è rimosso dalla pagina, e quindi dal DOM, esso continua
a farne parte. Con quest’ultima affermazione, non si intende che un componente
che appartiene ad una pagina sia sempre visualizzato all’utente, ma solo che
l’elemento è contenuto nel DOM (infatti un elemento può trovarsi in una pagina,
ma essere “hidden”, ovvero invisibile al client). Affinché la JVM su cui gira
l’applicazione possa riacquisire lo spazio occupato da un componente, esso deve
essere rimosso fisicamente dal DOM. In questo modo l’applicazione non ha più
alcun riferimento al componente che viene quindi acquisito dal garbage collector,
proprio come se ci trovassimo in un’applicazione Java che non ha più riferimenti
ad una variabile.
~ 21 ~
1.1.6 Performance: Zk vs JSF (Java ServerFaces)
Per capire perché uno sviluppatore dovrebbe scegliere Zk come framework di
sviluppo, piuttosto che un altro ambiente di programmazione sul web come
AJAX, JSF (JSP) o altro ancora, vengono riportati di seguito dei test effettuati su
due indici prestazionali: il tempo di risposta del server e la memoria consumata.
Il test è stato effettuato su 4 distinte configurazioni (due configurazioni riguardano
Zk, in cui vengono usate sia la versione Standard che la versione Enterprise,
ovvero a pagamento; le restanti due riguardano JSF con differenti impostazioni),
che dovranno lavorare su una tabella contenente 2000 righe e 10 colonne. [7]
Di seguito sono riportate le specifiche hardware e software:
Hardware
o CPU: AMD Athlon II X4 635 Processor @2.90 GHz
o Memoria: 8 GB
Software
o ZK 6.0.1 CE
o ZK 6.0.1 EE
o PrimeFaces 3.2 impostazioni S/C/R
o PrimeFaces 3.2 impostazioni S/S/S
o JDK 1.6.0.32
o JBOSS 6
o JMeter 2.6 e Visual VM (tool per il calcolo dei tempi di risposta
dal server e l’occupazione di memoria
Configurazioni
~ 22 ~
o JBOSS 6
Session time-out: default
-Xms 2048 MB
-Xmx 2048 MB
-XX:PermSize 1024 MB
-XX:MaxPermSiza 1024 MB
maxThreads: 2000
acceptCount: 1024
Il test è stato condotto su una tabella avente 2000 righe e 10 colonne. Di queste 10
colonne, 3 sono caselle di testo mentre le restanti 7 sono label [Figura 6] e [Figura
7]. Il test è stato condotto caricando la pagina una sola volta, al seguito del quale
sono state fatte due ROD Request (ovvero richieste per ottenere la restante parte
della pagina dovuta allo scroll).
Figura 6: Tabella costruita grazie a Zk
~ 23 ~
Figura 7: Tabella costruita grazie a PrimeFaces
Figura 8 - Tempi di risposta: in ordinate è riportato il tempo di risposta, mentre in ascissa il numero di thread
~ 24 ~
Figura 9 – Memoria occupata: in ordinata è riportata la memoria occupata, mentre in ascissa il numero di thread
Come si può notare dalle statistiche riportate in [Figura 8] e [Figura 9] si vede
come ZK fornisca una migliore risposta in termini di tempo, pagando però questo
aspetto in termini di memoria occupata, che risulta essere fino a 1.5 volte
superiore del corrispettivo usato da JSF. Sta di fatto però che nonostante questa
unica pecca, Zk sia preferibile ad un’implementazione PrimeFaces di
un’applicazione, dato che elevati livelli di memoria non sono più un problema per
le applicazioni moderne.
~ 25 ~
1.2 Apache Maven
“Maven è uno strumento di gestione di progetti comprendente un file chiamato
POM (Project Object Model), un insieme di standard, un determinato ciclo di vita
del progetto, un sistema di gestione delle dipendenze ed una logica per
l’esecuzione dei plugins obiettivi durante le fasi del ciclo di vita del progetto.”[8]
Anche se piuttosto scarna, questa definizione mette in luce alcuni degli aspetti
fondamentali su cui Maven si fonda:
POM
Plugins
Sistema di gestione delle dipendenze
Ciclo di vita di un progetto
Le suddette parole chiave verranno trattate in maggior dettaglio in seguito,
mostrando in che modo entrino a far parte dell’architettura del progetto Maven, e
come essi aiutino lo sviluppatore nella costruzione del proprio software. Inoltre si
spiegheranno di seguito alcuni concetti utilizzati da Maven e che hanno portato in
auge, in poco tempo, questo framework di sviluppo:
Convenzione sulla configurazione
Interfaccia comune
A cosa servono i plugins Maven?
1.2.1 Convenzione sulla configurazione
Esprime la necessità di avere impostazioni di default, in modo da semplificare la
vita del programmatore durante lo sviluppo di un progetto (non è più il
programmatore a decidere come organizzare il progetto, al suo posto ci pensa
Maven). A differenza di altri framework come EJB 3 e Ruby on Rails che hanno
iniziato a sposare questa filosofia, Maven è stato costruito apposta per
rappresentarne al meglio le potenzialità.
~ 26 ~
Analizzando la struttura di un progetto Maven [Figura 10], ci si rende subito conto
di come l’utente sia pilotato nelle directory in cui andare a posizionare il codice
sorgente (${basedir}/src/main/java), le risorse del progetto
(${basedir}/src/main/resources), gli eventuali test per il controllo di
errori nel codice (${basedir}/src/test), i file compilati
(${basedir}/target/classes) e perfino l’eventuale jar del progetto che si
vuole produrre (${basedir}/target).
Figura 10 – Struttura di un progetto Maven
Anche se può sembrare una forzatura per lo sviluppatore, basti pensare che alcuni
framework come Ant costringono l’utente a specificare le directory di ogni tipo di
file che si vuole produrre, il che molte volte può causare non pochi problemi allo
sviluppatore meno esperto. [18]
Oltre alla struttura “rigida” delle directory, Maven adotta la tecnica della
convenzione sulla configurazione anche per quanto riguarda i suoi plugin
principali, ovvero quelli messi a disposizione per la compilazione del codice, la
generazione di artefatti da poter distribuire e molti altri processi. Ed è proprio da
queste due principali caratteristiche che deriva la forza, ma al contempo, la facilità
di Maven. L’unico sforzo che è richiesto al programmatore è quello di porre il
codice sorgente nella giusta directory, sarà poi il framework, tramite il suo ciclo di
vita predeterminato, a svolgere la costruzione del progetto e la sua compilazione.
Bisogna comunque sottolineare che Maven non costringe necessariamente a
seguire la filosofia della “convenzione sulla configurazione” alla lettera.
Nonostante la sua struttura apparentemente rigida, il framework consente
~ 27 ~
comunque una certa flessibilità su dove mettere il codice sorgente, come
denominare il file jar prodotto dall’esecuzione e molte altre caratteristiche. Basta
solo che lo sviluppatore acquisisca familiarità con i plugin che vengono messi a
disposizione e Maven farà il resto. [8]
1.2.2 Interfaccia comune
Prima che Maven offrisse un’interfaccia comune per la compilazione del codice,
ogni progetto doveva prevedere qualcuno che si occupasse esclusivamente di
come integrare le varie parti di un progetto e compilarle. Ciò portava a grandi
sprechi di tempo qualora si dovevano aggiungere nuovi test o file sorgente che
dovevano essere integrati nel progetto. Infatti prima ancora di poter passare alla
compilazione del codice, bisognava capire come poter inserire il nuovo codice
senza causare il blocco dell’intero progetto.
Ad oggi invece, grazie all’introduzione di Maven, progetti complessi e modulari,
possono essere compilati con grande facilità proprio perché il framework usa
un’interfaccia comune che permette di non curarsi della struttura di un progetto,
ma solo della sua implementazione. In tal modo basta scrivere a riga di comando
mvn install e Maven farà il resto, grazie anche all’ausilio dei suoi plugin che si
occuperanno della gestione delle dipendenze di un progetto (sia rispetto a librerie
esterne sia rispetto a moduli inter-progettuali).
1.2.3 A cosa servono i plugin Maven?
Nei paragrafi precedenti, il termine “plugin” è stato espresso più volte, anche se
non è mai stato approfondito né tantomeno calato nel contesto dell’architettura del
framework.
Per avere subito un quadro abbastanza completo dell’interazione che ha Maven
con i suoi plugins, basta sottolineare come il framework di base non fa altro che
interpretare alcuni file XML e tenere traccia del ciclo di vita di un progetto.
~ 28 ~
Quindi essenzialmente Maven da solo può essere considerato abbastanza
“dummy” (stupido) da questo punto di vista. A rendere il framework davvero
potente, è l’insieme di plugins messi a disposizione dal Central Maven
Repository, ovvero la banca dati online da cui un progetto Maven prende non solo
le dipendenze necessarie affinché il progetto funzioni, ma anche alcuni plugins
fondamentali che permettono di effettuare testing del codice, packaging e molte
altre funzionalità [19]. Da sottolineare però, che non tutti i plugins sono messi a
disposizione dello sviluppatore quando si esegue il comando mvn install. Tale
scelta è dettata dal fatto sia di rendere Maven leggero, ma soprattutto perché in tal
modo uno sviluppatore può sempre avere la versione aggiornata di un plugin,
senza avere l’onere di effettuare egli stesso un aggiornamento. Inoltre il fatto di
poter ricevere dipendenze e plugins dal Central Maven Repository, consente di
avere codice che è riusabile da qualsiasi sviluppatore Maven, senza la
preoccupazione di dover cambiare il proprio codice per integrare librerie esterne
oppure di scrivere file di configurazione.
Dopo aver esposto in cosa “Maven crede”, si è ora pronti a mostrare cosa “Maven
offre” ai suoi programmatori per poter sviluppare codice pulito, performante e
portabile.
1.2.4 POM (Project Object Model)
I progetti Maven, le dipendenze, gli artefatti, sono tutti oggetti che possono essere
descritti e modellati tramite un apposito file XML inserito in un progetto. Tale
file, chiamato Project Object Model (POM), descrive che tipo di progetto, cosa
modificare e come dei file sorgente contenuti nelle directory specificate.
Esattamente come un’applicazione Java per il web, ha il suo web.xml per
descrivere, configurare e personalizzare il codice, così un’applicazione Maven ha
il pom.xml. Ci sono anche altri tools a cui poter fare riferimento e che sembrano
avere un file di configurazione simile a quello di Maven (vedasi il build.xml di
Ant o il Makefile di GNU). Ma a differenza dei precedenti, il Project Object
~ 29 ~
Model avrà a che fare con descrizioni del tipo: dov’è il codice sorgente? Dove
sono le risorse? Che tipo di packaging bisogna produrre? Tutto il resto è
semplificato dalla struttura che ogni progetto Maven ha e dalla maggior parte dei
suoi plugins che svolgono il lavoro “sporco” al posto del programmatore. [20]
Anche se spesso il framework è usato per lo sviluppo di applicazioni Java sul
web, questo non vuol dire che Maven, attraverso il suo pom.xml, non possa
lavorare con altri linguaggi come C#, Scala, Ruby e molti altri. Se il
programmatore conosce i plugin giusti, un progetto può compilare e produrre ciò
che si vuole: dal produrre war piuttosto che jar per il packaging del progetto fino a
suddividere un progetto unico in moduli che possono essere esportati per altri
progetti. Di seguito si riporta un’immagine che sintetizza le varie informazioni
contenute in un file pom.xml [Figura 11].
Figura 11 – Elementi costitutivi del POM Maven
Il POM contiene quattro tipi d’informazione, due descrittive e due di
configurazione:
1. General Project Information: sono le informazioni relative al nome del
progetto, dell’organizzazione che l’ha sviluppato, dell’URL in cui trovare
tale progetto, degli sviluppatori e dei collaboratori che vi hanno
partecipato, nonché la licenza sotto cui il software è rilasciato.
~ 30 ~
2. Build settings: ivi si può personalizzare la compilazione e la struttura del
nostro progetto. Affinchè sia possibile cambiare la locazione dei sorgenti e
dei tests, si possono aggiungere nuovi plugin, si possono aggiungere
obiettivi al ciclo di vita di un plugin preesistente, e si possono
personalizzare i parametri di generazione del progetto.
3. Build eviroment: l’ambiente di sviluppo consiste di diversi profili che
possono essere attivati a discrezione del programmatore, per consentire di
lavorare al progetto in differenti contesti operativi. Ad esempio potremmo
voler lavorare in fase di sviluppo su un determinato server, mentre in fase
produttiva spostarci su un altro server con differenti caratteristiche.
4. POM relantionships: raramente un progetto è un’entità a se stante, o
comunque tanto semplice da non richiedere codice esterno. Per venire
incontro a queste esigenze si sono previste dipendenze che aiutano ad
integrare codice esterno nella propria applicazione, scomposizione di
progetti in moduli, ereditare impostazioni da un progetto padre, e molte
altre funzionalità.
Tutte queste informazioni vanno a completare quelle di base che ogni progetto
Maven eredita dal POM padre, il file di configurazione che viene scaricato
automaticamente quando si installa Maven per la prima volta. Tali informazioni
permettono ad ogni progetto di avere accesso ad un insieme fondamentale di
plugins (quelli di base che servono per la compilazione, l’installazione, la
generazione del jar, ecc inerenti ai progetti sviluppati) ed inoltre specificano la
struttura base di ogni progetto Maven. Tale file si può trovare nel jar maven-
2.0.10-uber.jar ubicato in ${M2_HOME}/lib:
~ 31 ~
~ 32 ~
Dal file POM padre [Figura 12], ognuno dei suoi figli acquisisce in modo standard
quattro configurazioni standard:
1. Definisce un Maven repository remoto avente come ID central, comune ad
ogni client, e da cui vengono scaricati i plugin di base. Quest’impostazione
può essere sovrascritta tramite il settings.xml che si trova nella directory di
Maven sul localhost.
2. Il central Maven repository contiene i plugin ed ha l’attributo updatePolicy
pari a false per non permettere l’update automatico dei plugin in fase di
compilazione del software. Quando un utente vorrà aggiornare un plugin,
gli basterà cambiare la version del relativo plugin nel campo dependency.
3. L’elemento build definisce la struttura di un progetto Maven, come
precedentemente esposto.
4. La versione 2.0.9 di Maven è quella specificata di default, nel caso in cui
un utente non ne specificasse una nel suo progetto.
Grazie ai suddetti quattro punti, un POM “figlio” risulta essere molto più snello e
comprensibile per lo sviluppatore:
Figura 13 – Identificatori di un progetto nel POM Maven
Dei quattro attributi contenuti nel POM appena mostrato [Figura 13],
modelVersion, groupId, artifactId, version, gli ultimi tre sono necessari per
Figura 12 - Particolare del POM principale di Maven
~ 33 ~
l’identificazione delle coordinate del progetto. Essi infatti identificano la
locazione dell’eventuale packaging del software nel repository di Maven. Ciò
permette ad un altro sviluppatore di poter integrare tale progetto nel proprio
codice, semplicemente dichiarando nelle dipendenze del suo POM la tripla
groupId, artifactId e version.
Naturalmente non tutti i POM sono così semplici, possono essere ulteriormente
arricchiti con indicazioni per il build del progetto, la dichiarazione delle
dipendenze (come già accennato prima) ma anche con l’inserimento di plugin
aggiuntivi rispetto a quelli di base.
1.2.5 Plugin
Un plugin Maven è una collezione di uno o più goal [Figura 14]. Esempi di plugin
Maven possono essere: il Jar plugin, adibito alla creazione dei file jar; il Compiler
plugin adibito alla compilazione del codice sorgente e delle unità di test; il Surfire
plugin che si occupa di eseguire il codice di test e di generare dei report. Oltre a
questi plugins di base, ne esistono molti altri molto più specializzati e che spesso
lavorano con librerie esterne come Hibernate. Inoltre è data la possibilità allo
sviluppatore di implementare il proprio plugin personalizzato, con i relativi goals.
Per svilupparlo si può scegliere non solo il linguaggio Java, ma anche altri
linguaggi come Ruby, Ant, Groovy, beanshell e molti altri. Questo garantisce
flessibilità al codice che si sta sviluppando, ma anche la possibilità di avere una
configurazione del progetto che non dipende dal linguaggio di sviluppo del
software.
Figura 14 – Struttura di un plugin
~ 34 ~
Un goal è un determinato lavoro che può essere compiuto in maniera a sé stante,
oppure insieme ad altri goals. Per lanciarne l’esecuzione in Maven, basta scrivere
nella shell: mvn pluginID:goalID.
Figura 15 – Esempio di comando Maven
La [Figura 15], mostra come lanciare il goal create appartenente al plugin
Archetype. Le tre dichiarazioni successive (-DgroupId=… , -DartifactId= … , -
DpackageName= … ) sono parametri che il goal create riceve in ingresso affinché
possa effettuare il suo lavoro. Esistono però anche plugins i cui goals non
richiedono parametri obbligatori, in questo caso possono non esserci parametri in
ingresso, o essere opzionali.
I plugin, insieme ai goals che contengono al loro interno, rappresentano il motore
di Maven. Infatti senza di essi, il framework non saprebbe fare altro che gestire i
classpath di progetto, controllare il file POM e controllare la sintassi dei comandi
da shell. Dunque ciò che davvero fa funzionare Maven, sono i suoi plugins, con la
peculiarità che ogni sviluppatore può anche decidere di adottarne di propri al
posto di quelli già offerti di base.
1.2.6 Ciclo di vita di Maven
Maven supporta differenti cicli di vita, ma ve ne è uno, che è anche quello
maggiormente usato, di default. Questo ciclo, inizia con la fase di validazione del
progetto e finisce con la fase di rilascio del progetto. Ognuna delle fasi coinvolte
in questo ciclo di vita, sono lasciate intenzionalmente vaghe, per consentire la
massima flessibilità in base alla tipologia di progetto che si sta sviluppando. Se ad
esempio si sta sviluppando un’applicazione desktop, il progetto può essere
rilasciato sotto forma di jar, ma se invece si sta sviluppando un’applicazione
software, allora preferiremmo un war. [21]
In [Figura 16] è riportato il ciclo di vita di un progetto Maven, comprendente
anche i vari goals associati ad ogni fase. Tali goals non appartengono però alle
~ 35 ~
fasi del ciclo di vita, ma bensì a plugins che possono essere usati nella
compilazione del progetto. Infatti ogni plugins agisce in una determinata fase, e
rispettando un preciso ordine (da sottolineare come ogni fase possa avere zero o
più di un goals):
resources:resources: copia ogni risorsa contenuta in
src/main/resources e qualsiasi altri directory di resource specificata,
nella directory di output.
compiler:compile: compila tutti i file contenuti in src/main/java e li
sposta nella directory di output.
resources:testResources: copia tutte le risorse contenute in
src/test/resources nella directory test di output.
compiler:testCo
mpiler: compila i test-
case in src/test/java
e li sposta nella directory
di output.
surefire:test:
esegue tutti i test e
costruisce i files di
output.
jar:jar: trasforma
la directory di output in
un jar file (se invece si ha
a che fare con
un’applicazione per il
web, invece di usare il
plugin Jar, si può usare
quello che produce in
output un file war).
Figura 16 – Ciclo di vita di Maven
~ 36 ~
Ma come è possibile far partire questo ciclo di vita? Basta semplicemente scrivere
a shell il comando mvn install (trovandosi naturalmente nella cartella in cui è
situato il POM del progetto). Ma vi è anche un'altra alternativa, più macchinosa,
ma che permette di specificare a mano che tipo di plugins e goals eseguire durante
la compilazione del progetto [Figura 17], anche se questa opzione risulta essere
molto più onerosa per lo sviluppatore che è costretto a specificare i plugins per
ogni fase.
1.2.7 Sistema di gestione delle dipendenze
Affinché Maven possa usare librerie esterne o progetti sviluppati da terzi,
necessita delle cosiddette dipendenze da aggiungere al file POM. Tali dipendenze
come già accennato in precedenza permettono di individuare in maniera univoca
una libreria all’interno del repository grazie alla terna groupId, artifactId e
version. Ma avendo solo dipendenze e non un sistema che sia in grado di gestirle,
non è possibile importare codice esterno nel proprio progetto automaticamente
Figura 18 – Dipendenze transitive in Maven
Figura 17 - Comando per la specifica manuale dei goal di una compilazione
~ 37 ~
(soprattutto se si hanno dipendenze transitive da dover gestire).
Una delle peculiarità che rende Maven tanto potente è proprio nella gestione delle
dipendenze transitive, ovvero quelle dipendenze che non servono direttamente al
nostro progetto, ma che occorrono ad una libreria che si sta usando per poter
eseguire il nostro codice. In parole povere se un Progetto A contiene nelle sue
dipendenze il Porgetto B, e il Progetto B necessita del Progetto C per poter essere
eseguito, allora Maven si occuperà di andare a prendere il Progetto C
automaticamente. [Figura 18] Tramite questo procedimento si evita al
programmatore di scrivere in maniera esplicita ogni dipendenza che serve al suo
codice, semplificando il POM ma evitando anche di inserire librerie superflue per
errore che andrebbero ad aumentare la grandezza del nostro progetto.
Inoltre Maven riesce anche a risolvere i conflitti dovuti alle dipendenze, in modo
tale da non causare blocchi dovuti a loop di dipendenze. Per capire come il
framework riesca a gestire le dipendenze transitive, basta dare un’occhiata ad una
libreria scaricata dal Central Maven Repository, come junit :
Come si può notare in [Figura 19], nella directory in cui è contenuto il jar di junit,
non è presente solo la libreria che sarà usata nel progetto, vi sono anche altri file,
di cui uno in particolare è molto importante: junit-version.pom. Tramite questo
file Maven riesce a ricostruire che tipo di dipendenze occorrono a junit, e ad
integrarle in qualsiasi progetto che usi junit. Quindi il repository non salva solo
bytecode, salva meta-informazioni contenente informazioni sugli artefatti
contenuti in Maven.
Inoltre, altra peculiarità e punto di forza del framework, le librerie dichiarate come
dipendenze transitive non vengono aggiunte al classpath del progetto, che così
non le incorpora direttamente.
Figura 19 - Contentuto directory junit in Maven
~ 38 ~
Sono disponibili anche diversi scope per le dipendenze contenute in un progetto,
tra cui quelle più frequenti sono:
test: specifica che quella determinata libreria non viene usata in fase di
compilazione ma solo durante l’esecuzione dei test del progetto. In questo
modo la libreria non viene aggiunta al jar risultante dal processo di
esecuzione del ciclo di vita del progetto. Se invece si vuole generare un
WAR, nel caso in cui si debba avere a che fare con un progetto web, allora
si può anche imporre di aggiungere tale libreria nel bundle, apportando le
opportune modifiche al POM del progetto.
provided: in questo caso invece la libreria serve in fase di compilazione,
ma anche qui essa non viene aggiunta nel package risultante (sia esso jar,
war, o altro) a meno che non sia specificato altrimenti nel POM.
1.2.8 Riepilogo
Nei precedenti paragrafi, si è visto cosa sia Maven e quali miglioramenti abbia
apportato nello sviluppo di un progetto, avente o meno più moduli, quali siano i
suoi punti di forza, ma soprattutto cosa abbia di diverso rispetto a framework
preesistenti e di successo come Ant o EJB. Inoltre si è effettuata una panoramica
per quanto generale e semplice possibile su tutti i concetti di base su cui si fonda
Maven, e come questi ne influiscano sulla propria struttura. Per saperne di più si
consiglia una lettura delle numerose guide che si possono trovare online o dei libri
dedicati.
1.3 Spring framework
Quando a metà degli anni ’90 Java fu presentato al mondo, quasi tutti gli esperti
del settore ne intravidero le grandi potenzialità grazie alla sua caratteristica di
poter scrivere applicazioni complesse in modo modulare. Ciò che però
maggiormente colpì i programmatori, fu la possibilità di sviluppare RIA (Rich
~ 39 ~
Internet Application) usando le applets messe a disposizione dal linguaggio di
programmazione.
Nel Dicembre del 1996 la Sun Microsystem pubblicò una specifica per quanto
riguarda i JavaBeans [22]. Tale specifica prevedeva la descrizione di un modello
per i componenti software, nonché un insieme di regole per poter rendere il
proprio codice modulare e riusabile in applicazioni più grandi. Nonostante queste
politiche però, gli sviluppatori usarono principalmente i JavaBeans come modello
per la costruzione di widgets nelle interfacce grafiche, non ritenendoli adatti per
essere utilizzati in applicazioni complesse. Per ovviare a queste problematiche, la
Sun pubblicò nel 1998 la versione 1.0 degli Enterprise JavaBeans (EJB), che
aveva lo scopo di venire incontro alle esigenze dei programmatori, ma al
contempo mantenere la semplicità che contraddistingueva i primi JavaBeans.
Purtroppo però pur andando a semplificare aspetti strutturali come le transazioni e
la sicurezza, gli EJB introdussero alcune complicazioni quali file di
configurazione e codice non necessario. Col passare del tempo, molti sviluppatori
abbandonarono gli EJB alla ricerca di soluzioni più facili con cui poter costruire i
loro software.
La risposta ai bisogni dei programmatori, arrivò alcuni anni dopo, quando fu
presentato lo Spring framework sotto licenza Apache. Esso garantiva un ritorno
alla semplicità dei JavaBeans, ma con l’introduzione di alcune nuove
caratteristiche che ne avrebbero fruttato il successo a discapito dei suoi
concorrenti più famosi (Struts per fare un esempio). Di seguito sono elencate
alcune delle caratteristiche che hanno convinto gli sviluppatori a passare a Spring
(caratteristiche approfondite in maggior dettaglio in seguito):
leggerezza: Spring è un framework “leggero” e grazie alla sua architettura
estremamente modulare è possibile utilizzarlo nella sua interezza o solo in
parte. L’adozione di Spring in un progetto è molto semplice, può avvenire
in maniera incrementale e non ne sconvolge l’architettura esistente. Questa
sua peculiarità ne permette anche una facile integrazione con altri
framework esistenti.
~ 40 ~
Spring è un lightweight container e si propone come
alternativa/complemento a J2EE. A differenza di quest’ultimo, Spring
propone un modello più semplice e leggero (soprattutto rispetto ad EJB)
per lo sviluppo di entità di business. Tale semplicità è rafforzata
dall’utilizzo di tecnologie come l’Inversion of Control e l’Aspect Oriented
che danno maggiore spessore al framework e favoriscono la focalizzazione
dello sviluppatore sulla logica applicativa essenziale.
A differenza di molti framework che si concentrano maggiormente nel
fornire soluzioni a problemi specifici, Spring mette a disposizione una
serie completa di strumenti atti a gestire l’intera complessità di un progetto
software. Si analizzerànno in dettaglio gli strumenti offerti, per ora è
sufficiente affermare che Spring fornisce un approccio semplificato alla
maggior parte dei problemi ricorrenti nello sviluppo software (accesso al
database, gestione delle dipendenze, testing, etc.).
Spring è un framework nato con la concezione che il codice di qualità
debba essere facilmente testato.
Le tematiche appena citate quali Inversion of Control, Dependency Injection,
architettura di Spring e Aspect Oriented Programming saranno approfondite nei
prossimi paragrafi, andando di volta in volta a capire come abbiano potuto
contribuire al successo del framework.
1.3.1 Architettura di Spring
Spring Framework è composto, come già accennato, da diversi moduli. Tali
moduli, all’incirca 20 jar, sono scaricati automaticamente in fase di installazione
del framework.
~ 41 ~
Naturalmente non tutti i jar con cui si lavora rientrano nella medesima area di
competenza, così come non tutti e 20 i jar riguardano ambiti diversi. In generale si
possono riconoscere circa 6 area in cui poter suddividere l’architettura di Spring,
ognuna delle quali collabora con le altre, senza però risultare indispensabile per
gli altri moduli. Grazie a questa peculiarità, lo sviluppatore è sia in grado di poter
costruire la propria applicazione enterprise in toto, sia in grado di scegliere di non
usare alcuni dei moduli messi a disposizione da Spring [1]. In [Figura 20] è
riportata la suddivisione dei 20 moduli nelle 6 tipologie, ognuna delle quali è
adibita ad un particolare ambito:
Core Spring Container: come già accennato dal nome costituisce il cuore
dell’intera architettura di Spring: esso gestisce, crea e configura i beans
creati in un’applicazione. All’interno di questo modulo si trova lo Spring
bean factory, ovvero la porzione di Spring dedita alla depency injection.
Tramite il factory, Spring rende disponibile diverse implementazioni
dell’application context (in parole povere ciò che consente all’applicazione
che si sta sviluppando di poter accedere ai beans che un programmatore ha
Figura 20 – Architettura del framework Spring
~ 42 ~
dichiarato in un suo file di configurazione), ognuno dei quali fornisce una
diversa configurazione di Spring.
Inoltre tale modulo fornisce servizi come email, accesso JNDI (Java
Naming and Directory Interface, un API Java che fornisce servizi per
directory sul server), integrazione con EJB, e schedulazione. Ognuno di
questi servizi è offerto in maniera trasparente all’utente, non
costringendolo ad usare file di configurazione o a scrivere codice in
eccesso per poterli usare nelle proprie applicazioni
Modulo AOP Spring: fornisce un ampio supporto per la programmazione
orientata agli aspetti. Come la dependncy injection, la programmazione ad
aspetti supporta il rilassamento dei vincoli che intercorrono tra le classi
adibite ad una funzione e la funzione in se (sicurezza, transazioni, ecc). In
questo modo si mira a far diventare un’applicazione più modulare, leggera
e che abbia codice riusabile.[23]
Data access and integration: dovendo lavorare con un database, spesso il
programmatore è costretto a scrivere alcune linee di codice superflue
necessarie alla creazione della connessione, lavorazione dei risultati e
rilascio della connessione col database. Tutto questo codice in eccesso può
rendere le nostre classi lunghe e difficili da interpretare da terze persone,
per non contare poi della possibilità di errori dovuta alla mancata chiusura
di una connessione. Per ovviare a questi inconvenienti, il modulo Data
access and integration offre due sotto moduli (JDBC e DAO) che
prendono in carico il problema della creazione, gestione e chiusura delle
connessioni del database, in modo che l’utente si occupi solo dei dati
necessari per la propria applicazione. Inoltre, viene offerto una
metodologia di gestione degli errori causati dal database, che non è
dipendente dal tipo di DBMS che si sta usando, ma viene offerta da questo
modulo. Così facendo l’utente non dovrà avere a che fare con diverse
diciture per il medesimo errore (ad esempio la colonna cercata non esiste
in questa tabella).
~ 43 ~
Inoltre per chi volesse usare l’Object-relational mapping (ORM) è messo a
disposizione un modulo apposito. Spring non implementa una propria
soluzione ORM, anzi fornisce un supporto per l’integrazione con altri
framework che ne dispongono: Hibernate, Java Persistence API, Java Data
Object, ecc.
Altre funzionalità offerte da questo modulo sono: Java Message Service
(JMS) per la comunicazione asincrona con altre applicazioni attraverso
messaggi; la trasformazione object-to-XML; servizi per la gestione delle
transazioni.
Web and remoting: nonostante Spring si integri con molti framework
MVC già popolari ed efficienti, Spring ne propone una sua versione che
aggiunge due varianti: un servlet-base framework per le applicazioni web
convenzionali ed un'applicazione portlet-based per lo sviluppo di
applicazioni da contrapporre al Java portlet API. In aggiunta all’aspetto
puramente grafico delle applicazioni sul web, tale modulo offre anche
molte opzioni per lo sviluppo di applicazioni che interagiscono con altri
programmi (RMI, Hessian, Burlap, JAX-WS e un HTTP invoker).
Testing: Spring riconosce gli sforzi di quei programmatori che scrivono
non solo la logica e la grafica della loro applicazione, ma che pensano
anche allo sviluppo dei test per le loro applicazioni. In quest’ottica, il
framework dispone di un modulo interamente dedicato al testing del
software, naturalmente con il relativo supporto per l’integrazione di tali
test nel progetto che si sta sviluppando (ciò è garantito dalla presenza di
classi mock per il testing di servlet, JNDI e portlet).
Dopo aver visto l’architettura modulare di Spring, ed aver capito come essa aiuti il
framework ad essere leggero e massimamente flessibile alle esigenze del
programmatore, si possono introdurre altri due concetti che hanno il medesimo
obiettivo: Dependency Injection e AOP.
~ 44 ~
1.3.2 Dependency Injection
La Dependency Injection fa la sua apparizione per la prima volta in un post di
Marti Fowler del 2004, in cui esso lo descrive come un’implementazione
dell’Inversion of Control [Figura 21]. Per IoC si intende un principio
architetturale che va a stravolgere il punto di vista circa il normale flusso di
controllo creato in un programma. Se nella logica tradizionale è lo sviluppatore a
decidere il flusso di controllo così come la creazione, inizializzazione e uso dei
metodi degli oggetti in un’applicazione, nella IoC sarà il framework a occuparsi di
tali aspetti. La DI rientra in tale ambito, concentrandosi però solo sulla risoluzione
delle dipendenze tra le classi di un’applicazione. Se prima era lo sviluppatore che
aveva il compito di creare in una classe un oggetto per poterne usare i suoi metodi
(e creando in questo modo una dipendenza della Classe A sulla Classe B), ora
invece si affida tutto ad un componente esterno alla logica applicativa, il
cosiddetto Assembler, che avrà il compito di creare ed istanziare gli oggetti nelle
classi in cui sono richiesti. [5]
In generale, esistono tre tipologie di Injection:
1. Constructor Injection, dove la dipendenza viene iniettata tramite
l’argomento del costruttore
2. Setter Injection, dove la dipendenza viene iniettata attraverso un metodo
“set”
Figura 21 – Diagramma che esprime il paradigma della dependency injection
~ 45 ~
3. Interface Injection che si basa sul mapping tra interfaccia e relativa
implementazione (non utilizzato in Spring)
Una sorta di Injection Dependency è il pattern factory, usato spesso per istanziare
un oggetto demandando però il compito ad una classe adibita al compito. Anche
se questo approccio sembra poter disaccoppiare i codici delle classi con cui si
lavora, in realtà non fa altro che spostare il problema nella classe factory che si è
creata. L’unico modo per poter davvero usufruire della DI, consiste nell’utilizzare
un IoC Container che dovrà prendersi carico della gestione di tutte le dipendenze
(in sostanza si tratterebbe di un container che segue le operazioni presenti in un
file di configurazione, in cui vi è scritto che la Classe A userà la Classe B). Tale
Container, va quindi a sostituirsi alla dichiarazione esplicita di una classe in
un'altra, rendendo il compito dello sviluppatore più semplice, ma anche lasciando
il codice più leggibile.
In Spring tale Container viene direttamente fornito dal framework, e i componenti
che esso gestisce vengono chiamati tecnicamente “bean”. A differenza di altri
framework che usano “heavyweight container”, ovvero container in cui gli oggetti
al loro interno devo estendere classi o interfacce, in Spring un bean può essere
rappresentato da qualsiasi classe. Si ha così un “lightweight container”, ovvero un
container in cui non vi sono vincoli di nessun tipo.
Lo IoC Container è realizzato da due interfacce:
BeanFactory, che definisce le funzionalità di base per la gestione dei bean
ApplicationContext, che estende queste funzionalità basilari
aggiungendone altre tipicamente enterprise come ad esempio la gestione
degli eventi, l’internazionalizzazione e l’integrazione con AOP
L’interfaccia BeanFactory è il più semplice IoC Container in Spring. Esso si
assume il compito di: creare i bean necessari all’applicazione, inizializzare le loro
dipendenze attraverso l’uso dell’injection, gestirne l’intero ciclo di vita. Per
svolgere questi compiti, il container si appoggia a configurazioni impostate
dall’utente che, riflettendo lo scenario applicativo, specificano i beans che
~ 46 ~
dovranno essere gestiti dal container, le dipendenze che intercorrono tra questi
oltre alle varie configurazioni specifiche. Si riporta di seguito una delle numerose
implementazioni di BeanFactory messe a disposizione da Spring [Figura 22]: l’
XMLBeanFactory, in cui attraverso uno o più file xml l’utente è in grado di
specificare le dipendenze che intercorrono tra le varie classi. [4]
Dopo l’intestazione del file XML, racchiuse tra i tag <beans>, si trovano le
definizioni dei bean e, per ognuno di questi, le eventuali proprietà che ne
descriveranno la struttura e il comportamento:
Id: identifica univocamente un bean che così può essere usato tramite
invocazione al container.
Class: indica il nome della classe che si vuole costruire.
Risoluzione di dipendenze: mediante metodi getter o setter o attraverso il
costruttore, si indica se una classe dipenda da un'altra (un esempio può
essere l’uso di una classe A nel costruttore).
Proprietà comportamentali: definiscono il modo in cui il bean deve essere
gestito dal container (scope, ciclo di vita, ecc).
Una volta configurato l’ XMLBeanFactory, basterà usare l’ApplicationContext
all’interno delle classi in cui voler usare uno dei nostri bean.
Figura 22 - Dichiarazione di alcuni bean in Spring
~ 47 ~
Con questo procedimento, qualora si volesse cambiare qualcosa
dell’implementazione del bean, o se ne volessero creare altri, basterà
semplicemente andare nel relativo file di configurazione ed apportare le nostre
modifiche. E come è visibile dall’esempio in [Figura 23], l’impatto per il codice
in un’applicazione è minimo, dato che consiste semplicemente nell’inserire due
righe di codice per usare i metodi di una classe “esterna” al nostro codice.
1.3.3 Spring AOP (Aspect Oriented Programming)
Presente fin dalla prima versione del framework, l’Aspect Oriented Programming
rappresenta, insieme all’Inversion of Control, una delle caratteristiche principali
alle quali Spring deve la sua popolarità. Il suo supporto è garantito da Spring
AOP, un modulo nativo del framework che nasce con lo scopo di fornire una
completa integrazione con il container di IoC. Grazie a questa caratteristica,
Spring AOP pur non essendo una delle soluzioni più complete sul mercato risulta
essere estremamente flessibile e versatile. A partire dalla versione 2.x Spring AOP
è stato esteso con il supporto alle annotation AspectJ (@AspectJ), portando di
fatto un forte cambiamento nel modo di concepire AOP in Spring.
Prima di iniziare a scoprire le potenzialità AOP di Spring, bisogna introdurre
alcuni aspetti fondamentali della programmazione orientata agli Aspetti:
Figura 23 - Esempio sulla creazione di bean in Spring
~ 48 ~
Crosscutting concern: comportamento trasversale all’applicazione che si
ripercuote in più punti dell’object model. Ne sono un esempio il caching,
il logging, l’autenticazione o il Transaction Management. AOP nasce per
cercare di isolare i crosscutting concern in moduli ben precisi.
Aspect: è l’equivalente di una classe in OOP ed è utilizzata nella
programmazione ad aspetti per modularizzare i crosscutting concern.
Grazie agli aspect è possibile aggiungere dinamicamente comportamenti
agli oggetti di dominio senza che questi ne siano a conoscenza. All’interno
di un Aspect, si possono trovare un insieme di Advice, che ne
implementano le logiche applicative, ed alcuni pointcut, che ne regolano il
flusso.
Join Point: rappresenta un punto preciso nell’esecuzione del programma,
come l’invocazione di un metodo o il verificarsi di un’eccezione.
Advice: descrive l’operazione da compiere da parte di un aspetto ad un
determinato Join Point. Un advice, a differenza di un metodo che deve
essere invocato esplicitamente, viene eseguito automaticamente ogni volta
che ad un determinato evento (Join Point) si verifica una particolare
condizione (Pointcut). [Figura 24]
Figura 24 - Tabella inerente agli Advice in AOP
~ 49 ~
Pointcut: descrive le regole con cui associare l’esecuzione di un Advice ad
un determinato Join Point. Sostanzialmente attraverso un predicato viene
specificato che al verificarsi di un determinato Join Point sia applicato un
determinato Advice (es: Before).
Target: anche chiamato Advised Object rappresenta l’oggetto sul quale
agisce l’Advice.
I punti appena elencati, costituiscono le fondamenta della programmazione
orientata agli Aspetti, che naturalmente sono ripresi, con qualche cambiamento, in
Spring.
Per modellare un Advice in Spring è abbastanza semplice e si riduce
all’implementazione di apposite interfacce con le quali definire le azioni da
intraprendere [Figura 25].
Figura 25 - Tabella rappresentante gli Advice in Spring
~ 50 ~
Come si può notare dall’immagine sopra riportata [Figura 26], per la gestione di
un Advice, basta far implementare una delle interfacce riportate in tabella ad una
classe ed implementare il metodo richiesto dall’interfaccia (nel caso
dell’interfaccia MethodBeforeAdvice il metodo è before).
Rimane però ora da occuparsi della gestione del target, ma anche in questo caso
Spring limita al minimo le azioni che lo sviluppatore deve compiere. Per poter
associare un Advice ad un target object, basta semplicemente andare nel file XML
dell’IoC Container e specificare tramite i bean chi deve invocare cosa [Figura 27]:
Figura 26 - Esempio d’uso dell’interfaccia MethodBeforeAdvice
Figura 27 – Esempio di gestione degli Advice in Spring tramite xml
~ 51 ~
Come si può vedere in [Figura 27], il bean avente id “bookDaoProxy”, tramite il
property name “interceptorNames”, riesce a gestire gli advices dichiarati a fine
immagine. In questo modo i riferimenti non sono specificati come codice
all’interno dell’applicazione, lasciando così le classi leggibili e senza codice
superfluo.
Anche se il metodo di gestione e costruzione degli Aspetti precedentemente
mostrato sembra abbastanza lineare e di facile utilizzo, Spring dalla versione 2.x
ha offerto una metodologia molto più flessibile e potente, basata sull’uso delle
annotazioni di AspectJ.
Ora un Aspect viene trattato come un POJO (Plain Old Java Object, ovvero un
semplice oggetto Java) e al suo interno quando viene dichiarato un metodo, deve
essere annotato con un tipo di Advice e il conseguente Pointcut avente la lista dei
metodi che lo invocano. Così facendo si riducono anche le linee di codice nell’IoC
Container, dato che ora dovrà solo contenere il nome della classe che farà da
Aspect:
Figura 28 - Esempio sull’uso delle annotation in Spring
~ 52 ~
L’ultimo bean dichiarato è l’Aspect con la relativa logica che si è scelto di
implementare. Una peculiarità che aiuta a semplificare il lavoro dello sviluppatore
è data dall’auto-proxy [Figura 29] [Figura 28]che Spring 2.x mette a
disposizione. Come si è potuto notare, rispetto alla versione precedente che era
anche più classica come approccio, l’ultima modalità ha ridotto di non poco il
codice, facendo anche risparmiare tempo.
Dopo aver dato un’occhiata alle strutture principali usate dal progetto Apromore,
si passa ora ad una disamina sull’architettura che esso utilizza e su come tali
software entrino in gioco. Partendo dal background che ha portato allo sviluppo di
Apromore, si passerà ad una breve approfondimento sull’architettura su cui esso si
fonda, passando poi per esaminare alcuni dei moduli principali che lo
costituiscono nonché alcuni dei tools, utilizzabili anche stand-alone, che ne fanno
parte.
1.4 Architettura
Apromore è stato implementato con un’architettura a tre livelli [Figura 30]: livello
presentation, livello logic e livello data. [17]
Figura 29 - Esempio sull’uso delle annotation in Spring
~ 53 ~
Il livello Presentation costituisce l’interfaccia del repository, esso contiene il
Portal, la vera e propria interfaccia grafica in cui l’utente lavora con i suoi
modelli, che si interfaccia al Repository Manager, il servizio adibito ad esporre i
metodi forniti da Apromore (importare/esportare modelli in differenti linguaggi,
effettuare query su un insieme di modelli, sicurezza, controllo delle versioni dei
modelli, ecc). Come si evince dalla [Figura 32], l’unico punto di accesso al resto
dell’architettura è costituito dal servizio Repository Manager. Attraverso
quest’ultimo si andranno a richiamare i servizi adibiti al salvataggio dei modelli
nel database, i vari algoritmi attualmente offerti da Apromore (Process Similarity,
Process Merger, Business Process Clone Detection, ecc) ed infine il salvataggio
dei modelli in un linguaggio particolare (il Canonical Process Format) di cui ci si
occuperà in seguito.
Nel livello Logic si hanno altre due entità cardine dell’architettura: il Canonizer e
il Toolbox, entrambi servizi con scopi differenti ma complementari. Il primo
opera sui modelli preoccupandosi di creare un’interfaccia per la logica sottostante.
Ivi si trova il salvataggio dei modelli, la creazione dei formati canonici, delle
Figura 30 - Architettura di Apromore
~ 54 ~
informazioni aggiuntive per ogni modello, ecc. La seconda entità cardine, il
Toolbox, si occupa invece di fornire gli algoritmi precedentemente elencati
all’utente. Grazie alla scelta di frapporre servizi tra i dati e la grafica, si ha così la
possibilità di mantenere l’architettura flessibile ed aperta all’aggiunta di nuovi
componenti, nonché di nuovi servizi.
Il livello Data incapsula i dati dell’architettura software, ivi si trovano un insieme
di servizi che operano sui dati e sul loro salvataggio in un DBMS. In particolare,
quando un utente crea un nuovo modello, entrano in gioco cinque entità principali,
ognuna delle quali svolge un determinato compito [11]:
Archivio modelli: i modelli di processo nel loro formato originale XML
sono contenuti in questo archivio.
Archivio modelli canonici: per ogni modello nel suo formato originale,
viene prodotto il relativo modello in formato canonica. Tale formato
supera le specifiche di ogni linguaggio di rappresentazione, permettere agli
algoritmi presenti in Apromore di lavorare su una specifica comune.
Archivio annotazioni: quando un modello canonico viene creato, ad esso si
aggiungono alcuni metadati relativi alla rappresentazione del modello, per
migliorare gli indici di ricerca ,ecc. Tali metadati sono salvati poi sotto
forma di annotazioni riferite a quel modello ed organizzate in profili.
Archivio patterns: librerie riusabili di definizioni di processi, ovvero
pattern che possono essere riusati in altri modelli. Capita spesso infatti che
alcuni processi abbaino elementi in comune, e tramite questi pattern si
evita di dover perdere tempo ogni volta nel modellare un blocco già fatto
in precedenza.
Archivio relazioni: le relazioni tra rappresentazioni canoniche di differenti
modelli di processo, oppure tra modelli di processo e le loro estensioni.
Alla fine del processo nel repository, saranno così salvate due versioni di ogni
modello. La prima versione è il modello così come l’utente lo ha creato (ovvero il
~ 55 ~
modello nel linguaggio scelto dall’utente, XPDL, BPMN, ecc). Tale versione non
è tuttavia usata direttamente da Apromore e dai suoi algoritmi. Ciò con cui
lavorano gli algoritmi è in realtà il Canonical Process Format, ovvero un formato
canonico creato ad hoc per permettere il confronto e l’elaborazione di modelli
costruititi attraverso diversi linguaggi. Esso viene creato in contemporanea al
modello originale, e viene salvato nell’apposito archivio, pronto ad essere usato
qualora l’utente voglia effettuare un operazione su qualche modello. Si vede ora in
cosa consiste il CPF e come grazie alla sua flessibilità è possibile lavorare con
un’ampia gamma di tipi di modello.
1.5 CPF (Canonical Process Format)
Il formato canonico fornisce una rappresentazione comune e non ambigua di un
processo scritto in un linguaggio particolare e con un determinato grado di
astrazione. L’idea di base è quella di rappresentare solo gli elementi strutturali di
un processo, tralasciando gli aspetti grafici propri del linguaggio di
rappresentazione da cui proviene il modello. Infatti i primi sono comuni alla
maggior parte dei linguaggi di rappresentazione, mentre i secondi variando,
rendono spesso impossibile la comparazione tra modelli scritti in linguaggi
differenti. Si ha così un linguaggio puramente strutturale, che non ha elementi di
rappresentazione come linee, forme o posizioni di oggetti. Tutte queste
informazioni superflue ai fini della descrizione di un processo, sono salavate in
via separata tramite le annotazioni.
Grazie a questa “struttura” si hanno dei benefici sostanziali rispetto a cinque
aspetti:
Standardizzazione: non vi è più la necessita di avere differenti algoritmi
che lavorano su diverse tipologie di modello. Avendo algoritmi che
possano lavorare su un CPF, si riuscirà in realtà lavorare
contemporaneamente su diversi modelli scritti in diversi linguaggi.
~ 56 ~
Efficienza: non avendo più linguaggi con cui lavorare, si eliminano
problemi dovuti alla conversione da un modello ad un altro, così come i
tempi di traduzione da un linguaggio ad un altro.
Intercambiabilità: grazie alle annotazioni che accompagno ogni modello,
si possono operare trasformazioni da un CPF al linguaggio originario, così
come da CPF ad un altro linguaggio diverso dall’originale.
Riusabilità: grazie a modelli standardizzati, è possibile individuare
porzioni comuni in differenti processi che possono essere eletti a pattern
riusabili all’occorrenza.
Flessibilità: gli elementi di un CPF sono definiti attraverso un meccanismo
di ereditarietà che fa in modo che un processo sia visto, a livello più alto di
astrazione, come un grafo diretto. Ciò permette di eseguire operazioni sia a
livello di grafo, come ad esempio trovare eventuali cicli, sia di poter
effettuare operazioni più specifiche, come ad esempio ricerca di porzioni
similari all’interno del processo.
Grazie al CPF si evita di dover implementare un algoritmo per ogni tipologia di
linguaggio supportato dal repository.
Per capire come tale risultato sia stato possibile, occorre analizzare il grafico
UML che modella un processo. In questo modo si può capire come sia possibile
trasformare un processo, per quanto complicato, in un’astrazione di un grafo
formato solo da archi e nodi. [16]
~ 57 ~
Dalla [Figura 31] si evince come un CanonicalProcess, il modello presente nel
repository, sia formato da un insieme di Net, ResourceType ed Object.
Una Net rappresenta un processo o un sotto-processo, ed è rappresentato da un
grafo orientato. Il processo padre, ovvero quello da cui seguono tutti gli altri
sotto-processi, è indicato come root. Ogni rete è formata naturalmente da Node e
da Edge, in cui questi ultimi devono avere necessariamente una sorgente e una
destinazione. Per quanto concerne i Node [Figura 32] essi si suddividono in:
Work e Routing. I primi hanno un contenuto informativo rilevante per quanto
riguarda il processo, hanno al più un arco entrante ed al più un arco uscente, e si
suddividono a loro volta in Event e Task. Un Event può essere:
Message: scambio di messaggi tra due entità appartenenti ad un processo
(ad esempio l’invio di una lettera, la ricezione di una mail, ecc)
Time: un’azione legata al tempo. Può essere una scadenza, un’azione
ripetuta per un certo lasso temporale oppure anche solo un tempo morto
(un esempio sono l’attesa nella ricezione di un pezzo di ricambio,
l’aggiornamento giornaliero di una DB, ecc)
Figura 31 - UML del Canonical Process Format
~ 58 ~
Un Task rappresenta invece un elemento in grado di compiere attivamente
un’azione nel processo (il processare una lettera, registrare un paziente, ecc).
Possono essere eventi atomici oppure composti (è il caso dei sottoprocessi).
Per quanto riguarda i nodi di Routing, rappresentano quei nodi in cui non si
svolgono azioni ma soltanto scelte. Per questo motivo essi possono avere più archi
entranti e più archi uscenti. Tali nodi si suddividono in:
Split: sono nodi avente un arco entrante e più archi uscenti e rappresentano
le scelte possibili conseguenti ad un Work. Essi si suddividono in: OrSplit,
XORSplit e ANDSplit.
Join: sono i nodi a valle di uno Split, e rappresentano la conseguenza di
una scelta. Essi hanno più nodi entranti, corrispondenti alla cardinalità di
scelte che si potevano effettuare, ed un solo arco uscente che permette al
processo di continuare. Dualmente come nei primi nodi, si ha: ORJoin,
XORJoin e ANDJoin.
State: per indicare o stato prima di una decisione event-driven oppure uno
stato subito dopo una merge.
Per quanto riguarda gli Object [Figura 33], essi rappresentano contenuto
informativo essenziale che viene adoperato nello svolgimento del processo. Esso
si suddivide in due categorie:
Hard: rappresenta artefatti fisici come lettere, immagini, ecc;
Figura 32 - Dettaglio dell’elemento Node in CPF
~ 59 ~
Soft: tutto ciò che è digitale come file, tuple in un DB, ecc;
Tali oggetti saranno usati dai ResourceType, ovvero dipendenti (identificati
dall’oggetto Human ResourceType) oppure da sistemi informatici (identificati
dall’oggetto Non-Human ResourceType).
Figura 33 – Dettagli degli elementi ResourceType e Object
Combinando insieme tali elementi di base, si riesce così a tradurre modelli
provenienti da differenti linguaggi, in un modello universale su cui APROMORE
potrà eseguire i suoi algoritmi. Tutto questo è stato possibile grazie all’analisi
effettuata su sei tipi di linguaggi di rappresentazione (EPC, BPMN 1.1, WF-Net,
Protos 8.0, YAWL 2.0, WS-BPEL 2.0), che ha portato a stabilire che in realtà
esistono elementi strutturali comuni (come può essere un ORSplit o un Task), che
però sono graficamente differenti.
~ 60 ~
1.6 Interfaccia Apromore
Figura 34 – Portale di Apromore
Ecco in [Figura 34] come si presenta il portale di Apromore, dopo che un utente
ha effettuato l’accesso tramite il sito (stesso discorso vale per la versione
standalone che un utente ha installato sul proprio pc). La grafica è stata
interamente sviluppata tramite il frameowork Zk e le sue funzionalità. Le tre zone
evidenziate, forniscono all’utente tutte le informazioni di cui ha bisogno per poter
trovare e scegliere il modello su cui deve lavorare. Mentre i menu a tendina,
Process, Filtering e Design hanno un elenco di opzioni che saranno esplicitate in
seguito. Nel box verde è racchiuso l’albero che permette all’utente di poter
navigare attraverso le sottocartelle contenute nel repository. Come radice
principale dell’albero vi è la cartella “Home”, sotto cui si trovano le altre
sottocartelle create dall’utente. La Home a differenze della altre cartelle, non può
essere rinominata o cancellata. Da notare come nell’albero siano presenti solo
cartelle e non anche processi. Se infatti un utente volesse visualizzare i modelli
contenuti in una cartella, basterà che egli selezioni la cartella a cui è interessato, e
il contenuto sarà poi visualizzato nel box blu. Qui l’utente visualizza informazioni
come:
Name: scelto liberamente dall’utente quando crea un modello o una
sottocartella.
~ 61 ~
Id: costituito da un numero, viene dato in modo automatico sia alle cartelle
che ai modelli. Per non assegnare medesimo identificativo a due oggetti
differenti (che siano cartelle o modelli), l’id è incrementale e parte dal
numero 0 associato alla cartella Home.
Original Language: esprime in che tipo di linguaggio è costruito il
modello.
Domain: indica se il processo è pubblico per tutti gli utenti oppure se
appartiene ad un dominio specificato dal creatore del modello.
Ranking: è una valutazione effettuata dagli utenti sul modello.
Latest Version: indica l’ultima versione disponibile per quel determinato
modello. Le precedenti versioni comunque, non vanno sono sovrascritte
dall’ultima, sono anch’esse conservate nel DB a meno che l’utente non le
distrugga.
Owner: indica il proprietario del modello.
Ognuna di queste colonne è poi fornito anche di ordinamento automatico, in modo
da aiutare l’utente nella ricerca di un particolare modello.
Come anticipato, di un modello vengono conservate tutte le versioni, ma come e
dove esse siano rese disponibili all’utente, è mostrato nel box rosso. In quel
riquadro al di sotto dell’albero, l’utente può scorrere tutte le precedenti versioni
disponibili di un modello, quando esse hanno subito l’ultima modifica, il
“Branch” a cui appartengono e il campo “Annotation” che specifica il linguaggio
in cui è stato editato il modello. Se l’utente volesse aprire una di queste versioni,
basterebbe selezionare dal campo annotazioni il linguaggio con cui si vuole
lavorare, fare doppio click sulla modello ed aspettare infine l’apertura dell’editor
grafico [Figura 37].
~ 62 ~
Figura 35 – Editor di Apromore
Come si può vedere in [Figura 35], l’editor è suddiviso in due zone principali. A
destra (riquadro rosso) si trovano tutti i componenti per la costruzione di un
modello, suddivisi in varie categorie: Activities, Gateways, Swimlanes, Artifacts,
Data Objects, Start Events, Cacthing Intermediate Events, Throwing Intermediate
Events, End Events. A sinistra (riquadro verde) vi è la zona in cui disegnare il
processo. Per costruire un modello, l’utente seleziona l’oggetto di cui ha bisogno
tra quelli a disposizione a sinistra, lo trasporta nell’area verde e lo posiziona dove
ne ha necessità. Naturalmente gli oggetti a disposizione dell’utente cambiano in
base al tipo di linguaggio che esso sceglie per costruire il suo modello. La barra
che compare sopra ai due riquadri evidenziati contiene operazioni di base come il
salvataggio del modello, copia, incolla e taglia, ma anche funzionalità più
avanzate come l’autoridimensionamento del modello, l’export/import da json il
verificatore di sintassi del modello.
Facendo riferimento alla schermata in [Figura 37], si spiegheranno ora il
contenuto dei menu a tendina Process, Filtering e Design.
~ 63 ~
Per quanto riguarda la voce Process [Figura 36], esso contiene la creazione,
l’import/export, la cancellazione, la modifica, ecc. Una volta selezionato il
modello, basta semplicemente selezionare l’opzione a cui si è interessati.
Figura 36 – Dettaglio del menu Process
In alcuni casi, vedasi “Create model”, si aprirà una finestra aggiuntiva in cui
l’utente può specificare alcune opzioni come [Figura 37]: nome del modello,
dominio, linguaggio, proprietario, ecc.
Figura 37 – Interfaccia per la creazione di un nuovo processo
Per quanto riguarda il menu Filtering [Figura 38], ivi si trovano due opzioni molto
utili nell’analisi dei modelli di processo: Similarity Search e Clone Detection.
~ 64 ~
Figura 38 – Dettaglio del menu Filtering
Il Similarity Search è un’opzione che dato un modello in input, e selezionata una
cartella del repository, è in grado di trovare i modelli simili a quello in input che
hanno una similarità maggiore o uguale a quella della soglia specificata.
Figura 39 – Interfaccia dell’algoritmo Similarity Search
La [Figura 39] compare una volta selezionato il modello e cliccato su “Similarity
Search”. Come si può notare vi è la possibilità di scegliere tra due tipi di algoritmi
(Greedy e Hungarian), nonché di poter variare alcuni parametri come soglia di
similarità delle label, oppure del modello, del contesto ecc. Inoltre si ha la
~ 65 ~
possibilità di ricercare solo tra le ultime versioni dei modelli contenuti nella
cartella selezionata a sinistra, oppure tra tutte le versioni disponibili. Il risultato
della ricerca sarà poi visualizzato nel portale, e riporterà per ogni modello lo
“score” di similarità rispetto al modello padre, nonché le altre informazioni di
base dei modelli [Figura 40].
Figura 40 – Risultato del Similarity Search
Per quanto riguarda il Clone Detection, è un algoritmo che lavora all’interno di un
modello e ne ricerca i frammenti uguali. Ad esempio se in un processo è presente
più volte un ordine di pagamento, probabilmente sarà composto dalle medesime
attività. L’algoritmo si occupa di trovare proprio queste ripetizioni, in modo che
chi gestisce il modello di processo possa creare dei pattern facili da riutilizzare (ad
esempio creando sottoprocessi).
Figura 41 – Interfaccia grafica per il clustering in Apromore
Basta scegliere il tipo di algoritmo (DBSCAN o HAC), la grandezza del clone da
identificare (ovvero il numero di nodi minimo per cui due frammenti si possono
dire uguali) ed infine selezionare “show Clusters” per visualizzare le componenti
uguali [Figura 41].
~ 66 ~
Figura 42 – Dettaglio del menu Design
Si analizza ora all’ultimo menù, Design, ed alle sue funzionalità [Figura 42]. Ivi si
trovano tre opzioni:
Merging: in grado di fondere insieme un certo numero di modelli scelto
dall’utente e di produrre il modello nella cartella da cui provengono i
processi sorgente.
Questionnaire-driven configuration:
Define model: questionnaire mapping:
1.7 Vantaggi nell’uso di Apromore
Dopo aver visionato l’architettura di Apromore, come riesca a gestire diversi
linguaggi di rappresentazione dei modelli di processo, ed avere avuto un piccolo
scorcio della grafica user-friendly di cui dispone, si può ora passare all’analizzare
i vari motivi per cui un ente (sia pubblico o privato) o un gruppo di ricercatori
nell’ambito del Business Process Management, possano trarre beneficio dall’uso
della piattaforma.
Innanzitutto il primo beneficio consiste nella distribuzione gratuita del software
Apromore. In questo modo chiunque abbia interesse nell’analisi di modelli tramite
tecniche avanzate e non commerciali si può tranquillamente avvicinare alla
piattaforma. Da questo punto di vista il guadagno è sia per l’utente che può usare
tutti i tool messi a disposizione nel repository, sia per il progetto che aumenta il
~ 67 ~
proprio bacino di utenza, rendendolo di rimando anche più famoso alla comunità
accademica e non.
Per quanto riguarda le aziende, la piattaforma mette a disposizione molte opzioni
di cui altri software attualmente in commercio non dispongono (un esempio è la
capacità di gestire più linguaggi nella stessa piattaforma, ed avere elementi di
analisi avanzati come il Similarity Search e il Clone Detection). In questo modo
l’uso di Apromore non viene limitato alle sole aziende che adottano un particolare
linguaggio nella modellazione dei loro processi. Inoltre tale flessibilità permette
ad un’azienda di poter avere i propri modelli in differenti linguaggi, senza che la
stessa debba adoperare del personale per la traduzione di un modello. Per quanto
concerne invece gli algoritmi presenti su Apromore, essi sono offerti in maniera
chiara e trasparente, aiutando così coloro i quali che non sanno nulla di
programmazione ad andare avanti nel loro lavoro e a produrre risultati
professionali. Da non dimenticare poi il fatto Apromore è disponibile sia online
che in versione standalone. Ciò permette di poter caricare i propri modelli sul
repository, non usando un server aziendale proprietario, e di poter creare, tramite
la creazione di un “Dominio”, gruppi di lavoro in seno all’azienda per determinati
modelli. Di contro, la versione standalone può essere utile per coloro i quali
vogliano ampliare il progetto immettendo una propria funzionalità, oppure
semplicemente avere tutto ciò di cui Apromore dispone sul proprio pc, senza
caricare necessariamente i modelli online.
Ultimo punto, ma non meno importante, è l’ambito di ricerca che ha dato vita al
progetto, e che da esso può continuare a trarne beneficio. Nato dalla
collaborazione di diverse università sparse per il mondo (Eindhoven University of
Technology, University of Grenoble, University of Tartu, Vienna University of
Business and Economics, Cooperative State University Karlsruhe, Bonn-Rhein-
Sieg University of Applied Sciences) e coordinate dal Queensland Universit of
Technology, il progetto ha stimolato, e continua a stimolare, la produzione di
software che va ad arricchire Apromore con nuove funzionalità. Questo è
garantito dall’architettura a servizi di cui dispone il software, che rende possibile
~ 68 ~
lo sviluppo di plugin per il web (di cui è presente una guida sul sito
Apromore.org) che possono essere integrati facilmente.
~ 69 ~
CAPITOLO 2: PQL: A Process Query Language
Grazie al grande successo avuto dal business process management in ambito
aziendale, il numero di modelli per la rappresentazione dei processi è aumentato
in maniera critica, arrivando a numeri di centinaia, ed a volte, migliaia di modelli
per un singolo ente. Naturalmente non tutti i modelli sono formati da pochi task o
hanno un flusso di facile comprensione, rendendo così il lavoro degli analisti
aziendali molto difficile e dispendioso.
Per ovviare a tali problemi, col passare degli anni si sono sviluppati linguaggi
d’interrogazione per collezioni di modelli di processo. Tramite questi linguaggi
(BPMN-Q o BP-QL per citarne alcuni) l’analista può confrontare e recuperare
informazioni da centinaia di modelli, senza avere la necessità di effettuare un
lavoro manuale.
Come si può vedere nell’esempio di [Figura 43], si nota che i due processi
Figura 43 – Esempio di due modelli simili in BPMN
~ 70 ~
esprimono la medesima operazione (l'apertura di un conto bancario da parte di un
utente). Entrambi hanno lo stesso numero di task, ma struttura leggermente
diversa. Infatti, il primo modello effettuerà sempre un “Analyse customer credit
history”, mentre nel secondo caso vi è la possibilità di terminare il processo in
“Open VIP account”. Tramite uno dei linguaggi d’interrogazione precedentemente
citati, un analista potrebbe estrarre questi due modelli da un insieme di centinaia,
tramite un’interrogazione del tipo “Ritorna tutti i modelli che iniziano con
“Receive customer request” e finiscono con “Analyse customer credit history” “.
Anche se quest’ approccio potrebbe sembrare adatto a lavori di questo tipo, ci si
rende conto che spesso non è del tutto fedele alle richieste dell’utente. Infatti se
nel primo modello “Analyse customer credit history” segue sempre il task
“Receive customer request”, nel secondo caso questo non avviene. Quindi non ci
si può solo servire della struttura del modello per poter effettuare interrogazioni, si
deve anche inserire una certa semantica che permetta di distinguere casi come il
precedente. Per questo motivo sono stati sviluppati in seguito linguaggi che
tenessero conto anche del tipo di operazioni che venivano effettuati in un modello,
in modo da poter permettere interrogazioni sempre più fedeli alle richieste fatte
dall’utente.
PQL (Process Query Language) è nato con il proposito di unire analisi sintattica e
analisi semantica di un modello per accrescere l’accuratezza di un’interrogazione.
Un esempio di come la semantica aumenti il numero di risultati coerenti con
l’interrogazione effettuata consiste nella disambiguazione delle etichette in un
modello. Molte volte capita di trovare in un modello etichette che hanno il
medesimo significato di quella immessa nell’interrogazione, costituita però da
parole diverse (per esempio le frasi “Acquista prodotto” e “Compra prodotto”
hanno lo stesso significato nonostante siano scritte in modo diverso). Inoltre con
l’aggiunta di un punteggio di similarità è data la possibilità di limitare la
somiglianza tra etichette ad una determinata soglia, in modo da poter filtrare tutti
quei risultati poco conformi ai criteri dell’utente.
~ 71 ~
Di seguito saranno spiegata in breve la sintassi e la semantica del linguaggio PQL,
riportando anche alcuni esempi per facilitarne la comprensione.
2.1 Sintassi
PQL è un linguaggio d’interrogazione che è indipendente dal modello di processo
e dal linguaggio in cui esso è rappresentato. Grazie a questa peculiarità è possibile
interrogare vari modelli scritti anche in linguaggi differenti, sicuri che PQL si
concentrerà solo sui “contenuti” del modello, tralasciando gli aspetti visivi.
Affinché sia possibile conseguire gli obiettivi sopra proposti, è necessario
individuare aspetti come le occorrenze dei task in un processo, così come le
relazioni che intercorrono tra i vari task in un modello. Una volta compiuta tale
operazione, si può passare a definire determinati predicati di base che li
rappresentano e che vanno a costituire il nucleo del linguaggio PQL. In seguito si
utilizzerà t per indicare un task all’interno di un modello r ed i per identificare una
traccia di esecuzione per un processo, e con essi si vedranno tutti i predicati di
base, scritti in una sintassi astratta, che compaiono all’interno del linguaggio:
1. posoccur(t,r): indica che il task t ricorre almeno una volta nell’esecuzione
del modello r;
2. alwoccur(t,r): indica che il task t ricorre in tutte le esecuzioni del processo
r;
3. exclusive(t1,t2,r): indica che il non è possibile avere nella medesima
esecuzione di un processo il task t1 e il task t2;
4. concur(t1,t2,r): indica che ambo i task, in una qualsiasi esecuzione del
processo r, devono verificarsi (non importa se prima t1 e dopo t2 o
viceversa);
5. successionany(t1,t2,i): nella traccia di esecuzione i, per quante siano le
ricorrenze di t1, almeno una di esse deve precedere t2;
6. successionevery(t1,t2,i): nella traccia di esecuzione i, per quante siano le
ricorrenze di t1, ognuna di esse deve precedere necessariamente t2;
~ 72 ~
7. precedenceany(t1,t2,i): nella traccia di esecuzione i, per quante siano le
ricorrenze di t1, almeno una di esse deve succedere t2;
8. precedenceevery(t1,t2,i): nella traccia di esecuzione i, per quante siano le
ricorrenze di t1, ognuna di esse deve succedere necessariamente t2;
9. isuccany(t1,t2,i): nella traccia di esecuzione i, t1 ricorre almeno una volta ed
è immediatamente succeduto da un istanza di t2;
10. isuccevery(t1,t2,i): nella traccia di esecuzione i, ogni istanza di t1 deve essere
susseguita da un istanze di t2;
11. ipredany(t1,t2,i): nella traccia di esecuzione i, esiste almeno un istanza di t1
ed essa è seguita immediatamente da t2;
12. ipredevery(t1,t2,i): nella traccia di esecuzione i, ogni istanza di t1 è seguita
immediatamente da t2;
Questi sono solo alcuni dei predicati di base, i restanti si formano utilizzando le
seguenti formule sui predicati dal numero 5 al numero 12 (il simbolo Φ indica uno
dei predicati in precedenza esposti): [9]
Φ∀(t1,t2,r): Φ(t1,t2,i) il predicato è vero per ogni esecuzione i del processo
r;
Φ∃(t1,t2,r): Φ(t1,t2,i) il predicato è vero per almeno un’esecuzione i del
processo r;
Dopo aver elencato gli elementi base del linguaggio, si può ora passare alla sua
formulazione astratta, soffermandoci ogni volta sugli elementi di maggior rilievo.
La grammatica che seguirà, segue la notazione descritta da B. Meyer nel suo
articolo “Introduction to the Teory of Programming Languages”. Essa consiste
semplicemente di una grammatica astratta, in cui i costrutti elencati hanno
funzione puramente logica ed esplicativa. Nessuno di essi può essere sostituito in
un’interrogazione usata dall’utente, perché le parole usate, non sono riconosciute
come parole chiavi del linguaggio. Come ultima peculiarità, questa notazione
permette di esprimere la grammatica come un insieme di “costrutti” ed un insieme
finito di “produzioni” (ognuna di esse è associata ad un “costrutto”). Ogni
~ 73 ~
costrutto a sua volta descrive un insieme di oggetti chiamati “campioni”, usando
produzioni di tre tipi: “choice”, “list” e “aggregate”.
Query ≡ vars: Variables; atts: Attributes; locs: Locations; pred: Predicates
La definizione sopra riportata è il costrutto di più alto livello nella grammatica di
PQL. Esso descrive infatti da quali elementi sia composta un’interrogazione nel
Process Query Language. Il costrutto Query è definito come una produzione
aggregata di quattro componenti (Variables, Attributes, Locations e Predicates,
ognuna preceduta dal suo tag). In questo momento l’ordine con cui sono elencati i
componenti di un’ interrogazione è irrilevante ai fini della definizione della
Query. La posizione che dovranno ricoprire all’interno di un’interrogazione sarà
specificata solo in ultima analisi, per permettere al programmatore di potersi
concentrare solo sulla struttura dei componenti.
Variables, Attributes e Locations sono definite attraverso una produzione list di
campioni facente riferimento ad un altro costrutto (una lista è essenzialmente una
sequenza di zero, uno o più elementi):
Variables ≡ Variable *
Attributes ≡ Attribute +
Locations ≡ Location +
Si ha così che in un’interrogazione PQL vi è la presenza di zero, una o più
variabili e la presenza di almeno un attributo, così come deve essere necessaria la
presenza di almeno una locazione. [10]
Una variabile è definita da due costrutti: il nome della variabile ed un insieme di
lavori appartenenti ad alcuni dei modelli che si hanno a disposizione.
Variable ≡ name:VariableName; tasks: SetOfTasks
Come si vedrà più avanti, le variabili serviranno nei predicati per sostituire
l’insieme dei lavori che rappresentano, col nome della variabile. Tramite questa
scelta, l’utente non è costretto a scrivere interrogazioni troppo complesse, piene di
nomi di lavori oltre che dei vari costrutti necessari, ma risulta tutto ordinato e
~ 74 ~
comprensibile. Quando poi l’interrogazione sarà processata, in fase di esecuzione
i nomi delle variabili presenti nei predicati saranno sostituiti dal loro contenuto.
L’Attribute è qualsiasi cosa che riesca ad identificare univocamente una
collezione di processi o anche solo un processo, sia esso un ID, un nome o un
altro identificativo. Per quanto riguarda un Attribute nel contesto di Apromore,
esso può assumere i valori di: Universe, ID, Name, Domain, Language, Owner,
Ranking e Version. Per quanto riguarda la prima parola chiave precedentemente
elencata, essa ha il medesimo valore della “*” nel linguaggio SQL, restituire
all’utente l’insieme di tutti gli attributi che costituiscono un modello. Per le altre
parole chiave esse hanno un significato auto-esplicativo e non richiedono un
ulteriore approfondimento. La cosa importante da sottolineare, è come tali parole
possano essere combinati a seconda delle necessità degli utenti. Qualora sia
necessario, si possono scegliere anche dei sottoinsiemi dalla lista precedentemente
fatta, ed effettuare interrogazione che restituiscano solo ID e Name dei processi,
piuttosto che Owner, Language e ID. Inoltre è anche data la possibilità di inserire
sempre, per quanto possa essere contro intuitivo, la parola chiave Universe. Se si
ha così un’interrogazione avente come attributi ID, Name e Universe, il risultato
sarà naturalmente tutti gli attributi caratterizzanti un processo (ovvero ID, Name,
Language, Version, Owner, ecc).
Attribute ≡ Universe | ID | Name | Language | Version | Owner | Ranking |
Domain
Occupandoci ora delle Location, esse identificano i modelli di processo da cui
voler estrarre le informazioni specificate negli Attributes. Per fare un paragone col
linguaggio SQL, una Location è il corrispettivo di una tabella nella clausola
FROM.
Location ≡ Universe | LocationID | LocationDirectory
Come riportato dalla definizione sopra citata, una Location può assumere quattro
valori.
~ 75 ~
Universe: permette ad un utente di selezionare tutti i modelli contenuti nel
repository in cui sta lavorando;
LocationID: sarebbe l’identificativo univoco che distingue un modello da
un altro;
LocationDirectory: si è già visto che Apromore raggruppa i modelli
tramite un sistema di cartelle e sotto-cartelle. Sfruttando questa struttura,
l’utente può decidere di selezionare tutti i modelli contenuti in una cartella.
Qualora la cartella contenesse sotto cartelle, i modelli appartenenti alle
sotto cartelle, sarebbero comunque considerate nel processamento
dell’interrogazione;
A differenza degli Attributes, in cui un utente poteva comporre più Attribute a suo
piacimento, nelle Locations l’utente è costretto a fare una “choice”. Dunque o si
sceglie di selezionare tutti i modelli di processo presenti nel repository (Universe),
o si usano solo LocationID dei modelli che ci interessano, oppure si possono
scegliere le LocationDirectory per selezionare i modelli appartenenti a quelle
cartelle. Pur rappresentando un vincolo un po’ scomodo per l’utente, tale scelta fa
solo parte della definizione della grammatica. In fase implementativa può essere
superata dando la possibilità all’utente di specificare Universe oppure un insieme
di LocationID e LocationDirectory. Sarà poi compito del programmatore tradurre
tali locazioni in un formato univoco da passare al motore che interpreterà
l’interrogazione. Si nota ancora una volta come la grammatica sia scevra
dall’implementazione postuma che effettuerà il programmatore.
L’ultimo costrutto rimasto, ovvero il Predicate, è formato da una produzione
choice di costanti e costrutti che elencati a breve. Per permettere al lettore di
capire intuitivamente a cosa servano i Predicates, si ricorre ancora una volta ad un
paragone con il linguaggio SQL. In quest’ultimo linguaggio è presente la clausola
WHERE, in cui un utente mediante delle condizioni può discriminare le tuple
appartenenti ad una tabella. I Predicates rappresentano quelle condizioni, e
permettono similmente di discernere i modelli di processo che rispettano i criteri
dell’utente.
~ 76 ~
Predicate ≡ UnaryPredicate | BinaryPredicate | LogicalTest |
UnaryPredicateMacro | BinaryPredicateMacro | SetPredicate | TruthValue |
Negation | Conjunction | Disjunction
Di seguito vengono analizzati uno ad uno ognuno dei suddetti tipi di predicato:
Negation, Conjunction e Disjunction: sono il corrispettivo degli operatori
logici negazione, congiunzione e disgiunzione.
TruthValue: indicano un valore di verità tra “true”, “false” e “unknown”.
UnaryPredicate: indicano un predicato che ha come argomento un tasks
appartenente a qualche modello.
BinaryPredicate: come il predicato sopra descritto ma che riceve in
ingresso due tasks anziché uno soltanto.
UnaryPredicateMacro: sostanzialmente svolge il medesimo ruolo di un
UnaryPredicate, con la differenza però che è applicato su un insieme di
lavori ed ha un quantificatore che determina se ai fini del valore di verità
del predicato influisce almeno un modello di quelli elencati o tutti.
UnaryPredicateMacro ≡ UnaryPredicateName; SetOfTasks; AnyAll
AnyAll ricopre la funzione di quantificatore (esistenziale o universale),
mentre SetOfTasks è l’insieme di lavori su cui applicare il predicato
specificato da UnaryPredicateName. Per come è definita la grammatica, e
per come è stato già anticipato, SetOfTasks può contenere sia una lista di
lavori specificati dall’utente, ma può essere sostituita anche da una
Variable.
BinaryPredicateMacro: svolge una funzione simile al BinaryPredicate,
solo che questa volta gli argomenti possono essere Task/SetOfTasks
oppure SetOfTasks/SetOfTasks.
LogicalTest: è usato come stimatore del valore di verità di un predicato.
Avendo a che fare con una logica con tre possibili valori di verità (vero,
falso e sconosciuto), si dà la possibilità all’utente di testare il valore di un
predicato tramite:
IsTrue | IsNotTrue | IsFalse | IsNotFalse | IsUnknown | IsNotUnknown
~ 77 ~
SetPredicate: è usato per verificare se un task appartiene ad un dato
insieme di tasks, oppure effettuare operazioni insiemistiche su due insiemi
di task (Identical, Different, OverlpsWith, SubsetOf, ProperSubsetOf).
A seguire sarà mostrata una possibile grammatica concreta del linguaggio PQL
scritta tramite ANTLR (ANother Tool for Language Recognition).
Una particolarità che non sarà sfuggita al lettore, riguarda la definizione di query
presente a riga 9 di [Figura 46]. Come si può vedere si adottano le medesime
parole chiavi usate nel linguaggio d’interrogazione SQL: “SELECT”, “FROM” e
“WHERE”. Tale scelta è motivata proprio dalla grande notorietà che SQL ha
acquisito col passare del tempo, ma anche dal fatto che è un linguaggio usato in
molti DMBS commerciali (se non per intero in qualche sua variante). Così
facendo si aiuta l’utente a familiarizzare prima con PQL, consentendo un facile
apprendimento della sua sintassi. A seguire sarà mostrata il resto della grammatica
[Figura 44].
~ 78 ~
~ 79 ~
2.2 Semantica di PQL
In questo paragrafo sarà spiegato, in modo formale, il significato di ogni costrutto
non terminale precedentemente esposto nella sintassi.
Come primo passo s’introdurranno alcune definizioni che permetteranno di capire
meglio in seguito il significato delle operazioni non terminali.
Definizione 1 (unione prevalente): l’unione prevalente di f : X -> Y da g : X ->
Y, denotata da f ⊕ g, è definita come f U g / {(x, f(x)) | x ∈ dom(f) ∩ dom(g)}.
Definizione 2 (lavoro non-silente): sia N un modello di processo e T l’insieme
dei lavori in esso contenuti, per ogni t1,t2 ∈ T e φ ∈BPb:
Formula 1 – Lavoro non silente
Figura 44 - Sintassi PQL
~ 80 ~
ovvero, la relazione dovrebbe essere era se ambo i lavori non sono silenti
contemporaneamente.
Per BPb si intende l’insieme dei predicati binari presenti nel linguaggio, per φ un
predicato appartenete al precedente insieme, per LN(t) la label associata al lavoro t.
Definizione 3: sia N un modello di processo e T l’insieme dei lavori in esso
contenuti, per t ∈ T e ψ ∈ BPU:
Formula 2 – Predicati unari
la suddetta relazione dovrebbe valere in N se t non è un lavoro silente.
Per BPU si intende l’insieme dei predicati unari presenti nel linguaggio, per ψ un
predicato appartenente al precedente insieme.
Definizione 4 (Binding): data la possibilità di definire variabili, che saranno poi
usate o meno in un interrogazione, bisogna definire un legame tra la variabile e
l’insieme di lavori ad essa associata:
Binding ≡ ProcessModel XVarname -> 2Task
Definizione 5 (Repository): per repository si intende l’insieme di tutti i modelli
di processo su cui un utente può effettuare un’interrogazione:
Repository ≡ 2ProcessModel
Definizione 6 (valutazione interrogazione): la funzione valutazione
interrogazione restituisce i modelli di processo che soddisfano l’interrogazione
effettuata:
MQuery : Query X Repository -> 2ProcessModel
La valutazione dell’interrogazione dipende naturalmente da che predicati sono
stati usati e dagli assegnamenti dati alle variabili, si parlerà così d’insieme di
assegnamenti. In tale insieme una variabile può comparire più volte e può
~ 81 ~
incamerare differenti valori (da ricordare che un variabile non è altro che una
notazione ristretta che rappresenta un insieme di lavori). Naturalmente ai fini della
valutazione dell’interrogazione varrà solo l’ultimo assegnamento.
MAssignements : Assignamets X Repository X Binding -> Binding
Formula 3 - Assegnamenti
Definizione 7 (predicato): produce tutti i processi di modello sufficientemente
simili al task specificato nel predicato (in congruenza alla similarità delle label dei
tasks coinvolti):
MPredicate : Predicate X Repository X Binding -> 2ProcessModel
Formula 4 - Predicato
Definizione 8 (relazione tra Task): un predicato può produrre anche relazione tra
i task, ed in questo caso genera tutti i modelli di processo che soddisfano tale
relazione:
~ 82 ~
MTaskRel : TaskRel X Repository X Binding -> 2ProcessModel
Formula 5 – Relazione tra task
Definizione 8 (insieme di Task): di base indica una collezione di lavori
appartenenti ad un modello di processo, però può anche essere riferito all’insieme
di lavori assegnati ad una variabile dichiarata nell’interrogazione. In ultima analisi
un insieme di Task può essere anche “Costruito” (adoperando gli operatori
~ 83 ~
“UNION”, “INTERSECTION” e “DIFFERENCE”) oppure “Applicato” (definito
attraverso proprietà di base riferite ad alcuni predicati).
MTaskSet : TaskSet X Repository X Binding -> (ProcessModel -> 2Task
)
Formula 6 – Insieme di Task
2.3 Esempi di interrogazioni PQL
A seguire saranno mostrati alcuni esempi d’interrogazioni sviluppate con il
linguaggio PQL. Alcune delle primitive che saranno usate, apparterranno alla
sintassi astratta del linguaggio d’interrogazione (nel momento in cui tale tesi è
~ 84 ~
scritta, PQL non ha ancora concluso la sua traduzione da linguaggio astratto a
linguaggio concreto in toto). Le funzionalità mancanti saranno comunque
aggiunte in futuro, permettendo così all’utente di poter sfruttare appieno le
potenzialità del linguaggio.
La struttura degli esempi che si mostreranno seguirà lo schema:
Interrogazione in linguaggio naturale
Interrogazione in linguaggio PQL
Esempio di modello di processo che soddisfa l’interrogazione
Per quanto riguarda le lettere in maiuscolo all’interno delle interrogazioni, esse
rappresentano etichette che identificano lavori all’interno di un processo (tali
lettere vanno da A… L).
Q1: Seleziona tutti i modelli di processo in cui il lavoro A è presente in alcune
esecuzioni, mentre il lavoro B è presente in ogni traccia di esecuzione.
SELECT * FROM * WHERE TaskPos("A") AND TaskAlw("B");
Figura 45 - Esempio di processo
Figura 46 – Esempio di processo
Si sono riportati due esempi, [Figura 45] e [Figura 46], di modello di processo che
aiutano a capire meglio la formulazione dell’interrogazione Q1. Come si può
notare il primo esempio non è altro che una serie di lavori in cascata, mentre il
secondo modello ha una diramazione XOR (in cui è possibile intraprendere solo
una delle scelte a disposizione). In ambo i casi, il lavoro A è sempre effettuato, la
discriminate è però il lavoro B. Infatti se nel primo caso il lavoro B è sempre
~ 85 ~
effettuato, data la natura a cascata del processo, nel secondo esempio lo XOR fa sì
che il lavoro B abbia una possibilità di non essere eseguito. Dunque solo il primo
modello risponde ai fini dell’interrogazione, mentre il secondo risulta non adatto.
Q2: Seleziona tutti i modelli di processo dove è possibile che il lavoro A sia
svolto prima del lavoro D.
SELECT * FROM * WHERE AlwSuccAny("A","D");
Q3: Seleziona tutti i modelli di processo in cui il lavoro A è svolto sempre prima
dello svolgimento del lavoro D.
SELECT * FROM * WHERE AlwSuccEvery("A","D");
Figura 47 – Esempio di processo
Le interrogazioni Q1 e Q2 sono entrambe soddisfatte dal suddetto modello di
processo [Figura 47]. Infatti come si può notare il lavoro A è effettuato sempre
prima del lavoro D (grazie agli AND gate presenti dopo il lavoro A, i lavori B, C,
D, E ed F sono sempre effettuati, verificando così le due interrogazioni
precedenti). Inoltre l’esempio appena riportato, soddisfa anche le seguenti
interrogazioni:
Q4: Seleziona tutti i modelli di processo in cui può accadere che in qualche
esecuzione il lavoro A avvenga prima del lavoro B, e che il lavoro B avvenga
prima del lavoro K.
SELECT * FROM * WHERE PosSuccAny("A","B") AND
PosSuccAny("B","K");
Q5: Seleziona tutti i modelli di processo in cui il lavoro A avviene sempre prima
del lavoro B in qualche esecuzione.
SELECT * FROM * WHERE PosSuccEvery("A","B");
~ 86 ~
Q6: Seleziona tutti i modelli di processo in cui il lavoro B avviene il parallelo con
il lavoro C.
SELECT * FROM * WHERE Concur("B","C");
Figura 48 – Esempio di processo (d)
La [Figura 48] soddisfa le ultime due interrogazioni effettuate (Q5 e Q6), basta
notare i lavori nella zona evidenziata. A seguire verrà formulata un'altra
interrogazione verificata per il modello appena proposto.
Q7: Seleziona tutti i modelli di processo in cui il lavoro B avviene in parallelo con
il lavoro C, ed in cui il lavoro A avviene in parallelo con il lavoro H.
SELECT * FROM * WHERE Concur("B","C") AND Concur("A","H");
~ 87 ~
Progettazione modulo per PQL
Nel capitolo precedente si è ampiamente discusso del linguaggio d’interrogazione
PQL e della sua utilità per l’utente nell’ambito della ricerca di particolari modelli.
Tale linguaggio, solo mostrato nella sua sintassi nonché nella sua semantica, deve
ora essere tradotto in un pacchetto software che sarà integrato all’interno della
piattaforma APROMORE e dovrà essere fornito di un’interfaccia grafica che
guidi l’utente nella formulazione delle proprie interrogazioni.
Naturalmente per usufruire al meglio delle potenzialità offerte dal linguaggio
PQL, è necessario costruire un’interfaccia grafica il quanto più possibile semplice
per l’utente, ma che al contempo fornisca un valido supporto nella formulazione
di un’interrogazione. Si parlerà così di tutte le funzionalità presenti nel modulo
software che si è sviluppato, di come siano state organizzate le classi per
ottimizzare le prestazioni del modulo, nonché delle interrogazioni, il processo
attraverso il quale il modulo è stato integrato nella piattaforma APROMORE ed
infine si fornirà una piccola guida per l’utente in cui si vedranno, attraverso alcuni
esempi, le potenzialità che il software è in grado di offrire.
3.1 Specifiche
Come già spiegato nell’introduzione della tesi, il software che si è andato a
sviluppare consiste essenzialmente in un’interfaccia grafica, il quanto più
semplice possibile, che dovrà pilotare l’utente nella formulazione delle sue
interrogazioni PQL. Infine, bisognerà poi visualizzare, in maniera apposita, i
risultati di tali interrogazioni. La vera sfida per il conseguimento delle due
operazioni, riguarda la costruzione di un’interfaccia mediante il framework Zk,
con cui è costruito il portale web APROMORE. Infatti pur sembrando scontata e
di facile implementazione come scelta, nei prossimi paragrafi si vedrà invece
come essa risulta più ardua da conseguire, e richieda per tale motivo un
compromesso tecnico.
~ 88 ~
Dunque il primo requisito fondamentale deve essere necessariamente una grafica
in “stile APROMORE”, e per ottenere questo obiettivo bisogna ricorrere a ZK, il
framework tramite il quale è stata costruita l’interfaccia della piattaforma web.
Tale scelta è dettata soprattutto dal fatto che avere “diverse grafiche” nella stessa
piattaforma, oltre a non essere visivamente accattivante per l’utente, può
introdurre confusione e portarlo ad abbandonare la piattaforma. In secondo luogo,
usando le medesime classi già usate nell’interfaccia grafica di APROMORE, si
può riutilizzare del codice già esistente, abbattendo così i tempi necessari alla
costruzione del nuovo modulo. Naturalmente qualora si renda necessario si può
migliorare o variare in alcune sue parti un componente già definito, per adattarlo
alle proprie necessità. Avendo individuato così la scelta migliore con cui costruire
la grafica, si può passare a trattare la sua struttura.
Prima di azzardare una possibile struttura per la grafica, si devono innanzitutto
individuare le parti fondamentali del linguaggio a cui dovrà dare supporto (ovvero
il linguaggio PQL). Come si è visto nel secondo capitolo, tale linguaggio è
essenzialmente diviso in quattro parti fondamentali:
1. Enumerazioni delle variabili
2. Clausola SELECT, in cui l’utente specifica che informazioni ricavare dai
vari modelli (una lista di attributi quali ID, name, original language, ecc)
3. Clausola FROM, in cui si specificano i vari modelli o directory da cui
ricavare i risultati (si ha così una lista di modelli o directory separate da
virgole)
4. Clausola WHERE, in cui si enumerano le condizioni che i modelli devono
soddisfare affinché facciano parte del risultato (predicati unari o binari,
combinazioni d’insiemi di variabili o modelli, ecc)
Dall’elenco sopra riportato si notano due cose fondamentali: che l’elenco delle
variabili non ha una struttura rigida come il resto dell’interrogazione e che la parte
più complessa dell’interrogazione riguarda la clausola WHERE. Alla luce di
quanto esposto, si può decidere di separare le variabili dal resto
dell’interrogazione, separare le quattro parti o lasciarle tutte insieme. Si inizia con
~ 89 ~
l’esaminare l’ultima scelta, ovvero quella di tenere legate la parte delle variabili
con le tre clausole. In questo caso si ricorrerebbe al massimo ad un’area di testo
unica, in cui l’utente può scrivere prima le variabili e successivamente
l’interrogazione (in accordo a quanto specificato nella sintassi del linguaggio).
Nonostante il vincolo della sintassi, nessuno costringe l’utente a poter scrivere
prima l’interrogazione ed in seguito la lista delle variabili che vuole usare.
Naturalmente in quest’ultimo caso si restituirà un errore all’utente, ma invece di
costringere quest’ultimo ad effettuare in seguito tale correzione, sarebbe meglio
pilotarlo nella costruzione dell’interrogazione. Dunque l’idea di usare un'unica
area di testo è da accantonare in favore delle altre due opzioni precedentemente
esposte.
Ritornando ai punti in cui è divisa un’interrogazione, sarebbe allora ovvio
separare tutte e quattro le parti, usando per ognuna di esse un’area di testo. In
questo modo l’utente saprà cosa scrivere in ognuna delle aree, evitando una prima
fonte di errori. Naturalmente nessuno obbligherà l’utente a dover necessariamente
scrivere le variabili nell’apposito spazio, ma in questo caso egli saprà a priori che
l’interrogazione è malformata e che quindi riceverà un errore. Inoltre mettendo a
disposizione un’area di testo per ogni parte dell’interrogazione, si possono
adottare contromisure adeguate nel caso che l’utente commetta degli errori (ad
esempio andando ad evidenziare solo le parti contenenti gli errori, evitando così
che l’utente sia costretto ad interpretare tutto ciò che abbia scritto). Il lato negativo
risultante da tale approccio, come si può intuire, è una ridondanza eccessiva delle
aree di testo. Queste ultime danno l’impressione all’utente di dover riempire un
questionario online, piuttosto che un'interrogazione SQL-like. Come ultima
motivazione si riporta il confronto con i più famosi database commerciali
(MySQL, PostgreSQL), che come aree in cui poter scrivere un’interrogazione,
mettono a disposizione dell’utente un'unica area di testo ingrandibile
all’occorrenza. Una volta scartata anche quest’ultima scelta, si può passare a
descrivere quella più “logica”, che sarà anche poi quella adottata nel modulo
sviluppato, ovvero quella di due aree distinte, una in cui poter scrivere la lista
delle variabili e l’altra in cui poter scrivere il resto dell’interrogazione.
~ 90 ~
La scelta di suddividere l’interrogazione in due parti offre all’utente una maggiore
chiarezza su dove scrivere cosa, ed inoltre permette al contempo di poter trovare
in modo semplice gli errori commessi. Basti pensare che nella prima parte si
avranno solo liste di variabili, e che quindi se l’utente visualizzerà un messaggio
di variabile non dichiarata, dovrà aggiungerla in tale area. Naturalmente in tale
area è possibile aggiungere un meccanismo d’individuazione degli errori in
tempo reale, in modo da permettere le adeguate contromisure immediatamente, e
non dopo aver lanciato un’interrogazione (un approccio simile a molti IDE di
sviluppo come Eclipse e NetBeans, che sottolineano gli errori di sintassi
immediatamente). Per quanto concerne la seconda parte, quella relativa
all’interrogazione, si può usufruire degli stessi benefici precedentemente citati.
Infatti anche qui è possibile individuare in modo mirato gli errori che l’utente può
commettere, ma è anche possibile aiutarlo a ridurli al minimo. Tale scopo è infatti
raggiunto con l’introduzione di opzioni per l’auto-completamento delle parole
chiavi e/o dei processi che si vogliono ricercare, costruzione automatica delle
clausole SELECT e FROM ed evidenziando le parole chiavi, i valori delle
clausola SELECT e i processi con colori diversi. Le scelte appena esposte saranno
esaminate nel dettaglio, e per ognuna di esse si esporranno eventuali pregi e
difetti.
Si parte con l’esporre come poter offrire la funzionalità di auto-completamento
all’interno di un’area di testo. Anche qui le scelte possono essere molte, ma di
sicuro quella forse più intuitiva, e maggiormente utilizzata da molti DBMS e
software per la programmazione, consiste nell’adottare una piccola schermata
vicino alla parola correntemente scritta. In questo modo l’utente può visionare le
possibili scelte, e semplicemente cliccando sulla parola ricercata inserirla dove ne
ha bisogno. Un possibile problema riguarda naturalmente il numero di
suggerimenti disponibili. Nel caso precedentemente riportato si nota come la
parola “whe” scritta dall’utente abbia soltanto due parole chiavi simili: when e
where. Entrambe possono essere visualizzate dato che lo spazio occupato da
queste due parole sia molto ristretto. Nel caso in cui però, le parole dovessero
essere in numero maggiore, per esempio 10 o 15, visualizzarle tutte potrebbe
~ 91 ~
essere un
problema, dato
che la schermata
dei suggerimenti
si andrebbe ad
ingrandire
notevolmente.
Come è visibile
in [Figura 49]
riportato qui a
lato (ripreso da
MySQL
Workbench), per
ovviare alla
mancanza di spazio derivante dal grande numero di parole, si ricorre ad una
finestra con barra scrollevole a lato. In questo modo si possono visualizzare le
prime parole, lasciando poi all’utente la possibilità di ricercare la parola desiderata
tramite l’uso della barra. Come osservazione banale, si può evincere come le
parole elencate nella finestra dei suggerimenti, siano in ordine alfabetico ed
abbiano a lato un’icona che le identifica (“k” per keyword e “f()” ad indicare una
funzione) per aiutare l’utente nella sua ricerca. Inoltre qualora l’utente volesse
raffinare la sua ricerca, potrebbe digitare una lettera, per esempio “a”, e la finestra
dei suggerimenti si aggiornerebbe mostrando solo parole la cui iniziale sia “a”,
come ad esempio “abs”, “acos”, ecc [Figura 50].
Figura 49 – Particolare della grafica MySql
Figura 50 – Particolare del menu di suggerimento in MySql
~ 92 ~
Per agganciarci al secondo punto precedentemente citato, l’uso di colori diversi
per evidenziare e distinguere le parole, si possa fare ancora riferimento agli
esempi mostrati. Come si può notare le parole “select” e “from” sono colorate in
celeste, mentre le parole appartenenti alla clausola “select” non hanno una
particolare colorazione [Figura 50]. Un ulteriore esempio [Figura 51] aiuta a
capire come l’uso di colorazioni diverse riesca a distinguere in un sol colpo le
varie parti dell’interrogazione (parole chiavi, commenti, nomi di tabelle, attributi).
Purtroppo però, pur essendo un vantaggio, l’uso di colori per distinguere le
diverse tipologie di parole, può trasformarsi in un’arma a doppio taglio. Se infatti
i colori sono troppo vivaci, come può esser il giallo, o le tipologie di parole sono
in gran numero, l’utente potrebbe confondersi. L’uso di colori troppo forti infatti,
può dare fastidio all’utente che si appresta a scrivere un’interrogazione anche
molto complessa, causando non poche fonti di distrazione. Per quanto riguarda la
molteplicità di colori dovuta alla varietà di tipologie di parole, esso rappresenta
più una cacofonia agli occhi dell’utente che in questo modo può non riuscire a
distinguere in maniera adeguata le varie parti dell’interrogazione. In sostanza
bisogna riuscire a trovare un giusto compromesso tra tipi di colori, e il loro
numero.
L’ultimo punto da esporre, la composizione automatica delle clausole SELECT e
FROM, è un’ aggiunta pensata apposta per venire incontro alle necessità degli
utenti “più pigri”. In nessun DBMS commerciale infatti è prevista l’opzione di
Figura 51 – Colorazione delle parole chiavi in MySql
~ 93 ~
poter generare in automatico parte della interrogazione mediante valori
predefiniti. Per esplicitare meglio il concetto, si pensi a cosa debba fare l’utente
per scrivere un’interrogazione in MySQL: scrivere select, scrivere gli attributi a
cui esso è interessato, scrivere from, scrivere la/le tabelle da cui estrarre le
informazioni, scrivere where, scrivere le condizioni relative al risultato voluto.
Come si può notare dalla catena di azioni precedentemente scritta, vi sono i primi
quattro punti che sono obbligatori e al contempo molto semplici, mentre gli ultimi
due sono opzionali. Dunque si potrebbe permettere di generare in automatico i
primi quattro punti mentre gli ultimi due sarebbero da far scrivere a mano
all’utente qualora egli lo ritenga necessario. Naturalmente adottando per la
clausola “select” dei valori di default, mentre per la clausola “from” trovare un
modo per specificare quali processi voler analizzare con l’interrogazione. In
questo modo l’utente può produrre molto velocemente le interrogazioni più banali
(per intenderci quelle in cui non esiste la clausola “where”), e può comunque
risparmiare tempo nella stesura delle interrogazioni più complesse, non dovendosi
preoccupare delle prime parti (infatti in queste ultime interrogazioni la parte più
corposa è rappresentata dalla clausola “where”, in cui la presenza di più predicati
rende impegnativa la formulazione del risultato che si vuole raggiungere). Con
quest’ultimo punto si è conclusa la trattazione riguardante le aree adibite alla
composizione dell’interrogazione da parte dall’utente, spiegando di volta in volta
le scelte che si sono effettuate. Rimane però un ultimo aspetto fondamentale,
quello della continuità con il portale di Apromore.
~ 94 ~
Per conseguire questo aspetto bisogna innanzitutto individuare quali sono le parti
fondamentali nella grafica della piattaforma online.
Come si può notare dalla [Figura 52], si hanno tre componenti fondamentali che
risaltano subito all’occhio dell’utente: l’albero dei processi, la tabella delle
versioni di un singolo processo ed infine l’elenco dei modelli di processo
contenuti in una cartella. Nel nostro
modulo sarebbe preferibile ricostruire
una schermata di questo tipo, trovando
spazio per l’inserimento delle aree di
testo, ed eliminando le parti non
necessarie. Riflettendo sull’obiettivo che
si vuole conseguire, ovvero una grafica
per la formulazione di interrogazioni
SQL-like, si può eliminare in prima
battuta l’elenco dei modelli nel riquadro
azzurro. Esso infatti, oltre ad occupare un
consistente spazio, può essere integrato
Figura 53 – Aggiunta dei modelli di processo nell’albero di Apromore
Figura 52 – Portale Apromore
~ 95 ~
nell’albero a lato recuperando spazio e mantenendo al contempo una forma
ordinata. Inoltre, eliminando la parte relativa alla cornice azzurra, si è ricavato lo
spazio necessario all’inserimento delle aree di testo nonché di altre funzionalità.
Per quanto riguarda invece l’albero delle cartelle e la tabella nel riquadro rosso,
esse possono rimanere, al massimo apportando qualche piccolo cambiamento
funzionale al nostro modulo.
L’albero, come si può notare [Figura 53], può essere migliorato con l’introduzione
dei modelli di processo nella gerarchia delle cartelle. In questo modo non occorre
un ulteriore lista in cui far vedere all’utente il contenuto di una cartella. Inoltre,
l’albero può essere usato come fonte di scelta da cui l’utente seleziona i modelli
da inserire nella clausola FROM della sua interrogazione. Anche in questo caso vi
sono diverse scelte, tra cui spiccano due metodologie di fondo: permettere
all’utente di selezionare solo modelli di processo oppure permettere di selezionare
cartelle e modelli di processo (ed aggiungere in ambo i casi, nella clausola
FROM, ciò che si è selezionato). Il primo approccio è sicuramente da sconsigliare
dato che costringerebbe l’utente a dover aprire le cartelle contenenti sotto-cartelle,
e selezionare da esse i modelli contenuti. Sarebbe fattibile se le gerarchie delle
cartelle non fossero profonde, ma sarebbero comunque “scomode” per quanto
riguarda la praticità. La seconda scelta risulta sicuramente la migliore, dato che
l’utente potrebbe selezionare una cartella, contenente cartelle e modelli di
processo, ed aggiungere il tutto con pochi click all’interrogazione. Solo quando
l’utente ha selezionato le sorgenti, l’utente può trascinare le selezioni nell’area di
testo, ed esse compariranno nella clausola FROM (ovviamente evitando di
inserire modelli di processo già esistenti). Un'ultima accortezza è data dalla scelta
di non mescolare identificativi di modelli di processo e cartelle, onde evitare di
creare confusione nell’utente, ma soprattutto per seguire un discorso incentrato
esclusivamente sui modelli di processo. Infine, quando l’utente seleziona una
cartella e la trascina sull’area di testo dell’interrogazione, si selezioneranno tutti i
modelli di processo contenuti in essa. Così facendo sarà possibile inserire nella
clausola FROM dell’interrogazione, solo gli identificativi dei processi, in modo da
poter disambiguare processi con medesimo nome contenuti in cartelle differenti.
~ 96 ~
Per quanto riguarda la
tabella al di sotto
dell’albero, quella
bordata di rosso in
[Figura 52], può essere
usata per apportare
delle migliorie
funzionali ai nostri scopi. Dall’ingrandimento di [Figura 54], si possono notare i
quattro campi di cui essa è costituita: Branch (o ramo della versione), Version
(indica appunto le release di un processo, dato che quest’ultimo può averne anche
più di una), Last Update (o ultimo aggiornamento) e Annotations (ovvero i
linguaggi di rappresentazione disponibili per quel modello di processo). Ai fini
della nostra applicazione, non tutti i suddetti campi sono necessari. Infatti si deve
riuscire ad identificare i modelli di processo che saranno poi inseriti nella FROM
della nostra interrogazione, in che formato siano essi scritti è irrilevante. Dunque
si può eliminare l’ultima colonna e trasformare la tabella nel seguente formato.
Come si può notare il modello di processo che l’utente ha selezionato ha solo due
versioni disponibili. Nel caso in cui le versioni fossero molte di più, si potrebbe
pensare di far comparire a lato della tabella una barra scrollabile per aiutare
l’utente nella sua ricerca della versione da selezionare, ed una volta trovata
codesta versione, trascinarla nell’area di
testo dell’interrogazione. Come ultimo
accorgimento si potrebbe addirittura pensare
di effettuare un ordinamento delle versioni
del modello di processo mediante il click su
uno dei titoli della tabella (naturalmente
l’ordinamento sarà effettuato
lessicograficamente se si clicca sul titolo BRANCH e numericamente nel caso di
click su VERSION o LAST UPDATE) [Figura 55].
Ricapitolando, si sono esposte tre specifiche essenziali che il modulo software a
supporto del linguaggio PQL deve offrire (campi in cui inserire variabili e
Figura 504 – Tabella versioni Apromore
Figura 55 – Trasformazione della tabella delle versioni
~ 97 ~
interrogazione, un albero dei modelli di processo da cui selezionare le locazioni e
una tabella per poter selezionare le versioni relative ai modelli). Non si sono
considerate però eventuali implicazioni e/o rapporti che tali componenti possono
generare. Come citato nei precedenti capoversi, sia l’albero che la tabella delle
versioni, permetterebbero all’utente di poter selezionare un modello da inserire
nella clausola FROM. E’ possibile quindi far in modo che una dipenda dall’altra,
e che tutte e due insieme forniscano un miglior supporto all’utente. In poche
parole, si potrebbe pensare di far selezionare all’utente un modello dall’albero, e
consentirgli poi di selezionare tutte le versioni, la versione più recente o solo
alcune versioni dalla tabella posta sotto l’albero [Figura 56].
Figura 56 – Esempio della grafica che dovrà avere il tool per la stesura delle interrogazioni (a)
Purtroppo, pur potendo scegliere la versione che più ci interessa dalla tabella,
sarebbe scomodo ricercare ogni volta l’ultima versione o selezionare tutte le
versioni disponibili di un modello. Pur avendo a disposizione l’ordinamento delle
colonne nella tabella, bisogna fornire all’utente un metodo più facile e veloce per
scegliere i modelli di cui necessita. Avendo elencato prima le tre possibili azioni
che l’utente può intraprendere (ultima versione, tutte le versioni, un sotto insieme
~ 98 ~
delle versioni di un modello), si deve offrire il modo migliore per la selezione del
modello di processo per l’utente. La natura intrinseca del problema ci porta a
scegliere un approccio basato o su un menù a tendina o su una serie di checkbox,
con cui l’utente può cambiare ogni volta la “modalità” di selezione dei modelli di
processo.
Si espongono vantaggi e svantaggi del menu a tendina per constatare se esso vada
bene o no. Innanzitutto il menù non occupa molto spazio ed è facilmente
sistemabile all’interno della grafica. Di contro però non permette all’utente di
avere in vista le possibili opzioni a disposizione, e risulterebbe macchinoso nel
caso in cui l’utente debba fare più selezioni magari diverse tra loro. Passando
invece ai checkbox, essi costituiscono componenti forse “ingombranti” ai fini
della grafica, ma permettono all’utente di avere tutte le opzioni a disposizione e
non risultano troppo ardui nel caso si voglia cambiare spesso modalità. Dunque è
molto meglio scegliere i checkbox, perdendo dal punto di vista dello spazio nella
grafica, ma guadagnando in funzionalità per l’utente. Inoltre si potrebbe anche
pensare di far in modo che le versioni nella tabella apparissero e scomparissero a
seconda della modalità di seleziona scelta dall’utente. In sostanza quando l’utente
attiva la modalità “ultima versione” o “tutte le versioni”, i processi si possono
direttamente scegliere dall’albero dei processi. Di contro nella modalità “seleziona
versioni”, selezionando un processo dall’albero, immediatamente apparirebbero le
sue versioni nella tabella, permettendo all’utente di scegliere quelle a cui è
interessato. Da notare un problema la cui risoluzione è pressoché immediata: se
l’utente è in modalità “seleziona versioni”, l’albero deve consentire la scelta
multipla? E negli altri due casi (ultima versione e tutte le versioni) si deve
permettere la selezione multipla dall’albero? La risposta alla prima domanda è
necessariamente no. Sia perché è poco logica come azione (si tratterebbe di
mescolare versioni appartenete a due processi diversi) sia perché l’utente ha
comunque la possibilità in pochi click di scegliere posteriormente le versioni dal
secondo modello. Per quanto concerne invece la seconda domanda che si è
formulata, la risposta è dipende. Infatti garantire la multi-selezione dei modelli
~ 99 ~
dall’albero, favorisce naturalmente il compito dell’utente, però non è detto che
debba essere un obbligo.
Si è dunque introdotta una nuova funzionalità che porterà a cambiare la grafica nel
modo seguente [Figura 57]:
Figura 57 - Esempio della grafica che dovrà avere il tool per la stesura delle interrogazioni (b)
Si sono così snocciolate tutte le possibili azioni per assistere l’utente nella
formazione della propria interrogazione (dalla finestra dei suggerimenti, all’auto-
completamento, al drag and drop dei modelli nell’area di testo). E’ il caso però di
concentrarsi anche su aspetti quali il salvataggio di dati o finestre di riepilogo
aggiuntive che forniscono maggiori informazioni sulle interrogazioni che sta
effettuando l’utente. Ragionando a fondo, si può affermare che di dati da salvare
nella nostra applicazione non ve ne sono. Vi sono però variabili e interrogazioni
che potrebbero servire in futuro all’utente e che quindi quest’ultimo sarebbe
costretto a riscrivere ogni volta salvo che non gli si fornisca un metodo per il loro
salvataggio/caricamento. Dove queste informazioni siano salvate, o da dove esse
siano caricate lascia però un interrogativo. Infatti si può dare la possibilità di
salvare le interrogazioni sia in locale, ma anche di poter fornire una cartella online
qualora non si voglia occupare dello spazio sul proprio pc, o per permetterne la
~ 100 ~
condivisione con altri utenti. Inoltre è una funzionalità che anche i maggiori
DBMS ormai hanno (vedasi MySql), ed è di facile implementazione qualora si
voglia mettere a disposizione. Non è una problematica nemmeno trovare uno
spazio in cui inserire tale comando, dato che basta aggiungere un menù.
Per quanto riguarda invece l’introduzione di componenti riepilogativi (come
tabelle o aree di testo non editabili), si può prendere spunto dai software per
l’interrogazione delle basi di dati. In essi, oltre allo spazio in cui scrivere
l’interrogazione, compare anche una finestra in cui si visualizzano gli errori
sintattici, e i motivi per cui un’interrogazione è fallita. Funzionalità questa molto
utile per l’individuazione degli errori da parte dell’utente, e che ci motiva ad
inserire un suo omologo all’interno del nostro modulo. Le modalità con cui si
andranno ad inserire saranno probabilmente le stesse presenti nei DBMS, ovvero
tramite un’area di testo non editabile, e non rappresenterebbe uno sforzo eccessivo
data la natura di semplice cattura dell’errore che si propaga dall’interrogazione.
Altra funzionalità che si potrebbe aggiungere, a quelle finora fornite, è una tabella
riepilogativa riguardo ai modelli inseriti nell’interrogazione. Se ad esempio
l’utente prende in esami molti modelli, ognuno dei quali ha molte versioni, la
clausola FROM tende a crescere molto velocemente, non permettendo all’utente
di poter avere una visione d’insieme sulle operazioni che sta facendo. Onde
evitare questi problemi, si mette a disposizione tale tabella in cui si fornirà
all’utente: ID, nome, owner, language, version, ed altri campi qualora sia
necessario. Il vero problema però consiste nel non poter inserire questa tabella
nella grafica principale, ma nel doverla far apparire pop-up (problema di per se
non enorme e che risulta elegante come soluzione).
Elencate di seguito, vi sono tutte le principali funzionalità che il nostro software
deve avere:
Area di testo per le variabili
Area di testo per l’interrogazione
Albero per la scelta dei modelli di processo
Tabella riassuntiva per le versioni dei modelli
~ 101 ~
Tre diverse modalità di scelta dei modelli di processo (ALL, LAST e
CHOOSE)
Tabella riepilogativa dei modelli presenti nella FROM
Area di testo per gli errori di sintassi
Salva/Carica dei modelli sul filesystem o sul web
Conclusa così l’analisi delle specifiche del software da implementare, si focalizza
l’attenzione sulla fase di integrazione della logica PQL all’interno della
piattaforma APROMORE. Dato che tale procedura consta di più passi, ed è legata
intimamente con le tecnologie Maven ed OSGI, si riporteranno varie screenshot
per una migliore comprensione del processo nonché i risultati ottenuti.
3.2 Integrazione modulo PQL nell’architettura
Apromore
Nel capitolo due della tesi è stato spiegato
approfonditamente la sintassi e la semantica del linguaggio
PQL, ivi si spiegherà invece come la libreria adibita
all’interpretazione del linguaggio sia stata integrata
nell’architettura di APROMORE. Particolare enfasi sarà
rivolta alla traduzione di tale libreria in modulo Maven
spiegandone, in prima analisi, le motivazioni e le difficoltà
del procedimento, e solo in seguito si passerà a spiegare la
parte prettamente tecnica mediante opportune screenshot.
Come esposto nel primo capitolo, la piattaforma
APROMORE si poggia fortemente su tecnologie quali
Maven, per la generazione dei moduli di cui è composto
nonché per esplicitare le dipendenze tra moduli, e Spring, Figura 58 – Contenuto del jar org.jbpt
~ 102 ~
come contenitori dei moduli derivanti da Maven e la loro gestione. Dunque
qualsiasi libreria che si trova all’interno della piattaforma è già in formato Maven
e non crea alcun problema laddove, nell’implementazione di un nuovo modulo, si
volesse usare un jar appartenente al repository. Il problema ivi sorge a causa della
mancanza di qualcosa adibito alla gestione del linguaggio PQL. Tale libreria
infatti, sviluppata dal ricercatore del QUT Artem Polyvyanyy, è esterna
all’architettura di APROMORE e di base non è in formato Maven. L’obiettivo
sarà quindi quello di trovare una soluzione a questo primo problema, cercando di
trovare laddove possibile una soluzione elegante.
Innanzitutto, come fa la libreria PQL a lavorare con modelli di processo scritti in
diversi linguaggi? [Figura 58] Ebbene essa non è in grado di riconoscere BPMN,
EPML o YAWL, ma solo reti di Petri, con l’ulteriore vincolo che esse siano sound
ed appartengano alla categoria delle workflow-net (ovvero una rete avente unico
input ed unico output). Come sia stata possibile la traduzione di tutte le reti
presenti nel repository di Apromore, nel formato precedentemente esposto verrà
spiegato più avanti nel paragrafo della progettazione, qui si sposta l’attenzione sui
passi che la libreria esegue nella valutazione di una rete passata in input. Si inizia
quindi col verificare se un determinato modello di processo sia effettivamente una
workflow net. Per farlo si deve prima tradurre ogni elemento del modello di
processo in un oggetto java usando la libreria open source disponibile all’indirizzo
https://code.google.com/p/jbpt/. Effettuata questa prima operazione, si può,
tramite le classi contenute in org.pql.mc, verificare la soundness della rete così
costruita. Per la verifica si ricorre ad un tool esterno chiamato Lola model
checker, che è uno dei maggiori software per la verifica strutturale sulle reti di
Petri. Naturalmente, sia la libreria jbpt che Lola dovranno essere inserite in
APROMORE ricorrendo anche qui a Maven. Una volta appurata la soundness
della rete, il prossimo passo consiste nel salvataggio delle informazioni relative
alla rete (posti, transizioni, etichette, token presenti nella rete, ecc). Le
informazioni, salvate in due basi di dati differenti (MySql e PostgreSQL), sono
gestite dalle classi presenti in org.jbpt, org.pql.label e org.pql.persist (l’uso dei
due DB è stato reso necessario soprattutto per alcune funzionalità che PostgreSQL
~ 103 ~
offre a dispetto di MySql). Si sta cercando però di integrare le tabelle salvate in
PostgreSQL nel DB MySql, in modo da non costringere gli utenti a dover
installare due basi di dati sullo stesso computer, qualora vogliano sviluppare
codice standalone sulla propria macchina. Per quanto riguarda le informazioni
salvate, bisogna porre maggior attenzione su come siano salvate le etichette
relative ai lavori presenti nei processi. Esse non sono semplicemente estratte dai
modelli e salvate, ma subiscono un processo di stemming (un processo mediante
cui si ha la riduzione di una parola in forma flessa nella sua forma radice). Tale
processo è reso necessario affinché l’utente possa effettuare interrogazioni sui
modelli, pur non sapendo con esattezza le etichette contenute in essi (si ricorda
che PQL offre la possibilità di inserire una soglia si somiglianza riguardo le
etichette, proprio per facilitare il compito nelle interrogazioni dell’utente). Una
volta effettuata l’indicizzazione del modello, ovvero il processo di salvataggio nei
database delle informazioni, l’utente può effettuare le sue interrogazioni sul
modello (e qui intervengono le classi in org.pql.query).
Il prossimo passo da compiere, dopo aver visto la struttura della libreria, consiste
nel trasformarla da semplice libreria java, a modulo Maven da importare
all’interno di APROMORE. Per compiere tale operazione, bisognerà però
controllare prima le varie librerie da cui PQL dipende, e verificare che anche
queste ultime siano in formato Maven. Alcune dipendenze sono già state
esplicitate precedentemente (MySql e PostgreSQL), di seguito si elencano ora le
altre librerie utilizzate da PQL:
Antlr4: è un potente generatore di parser per la lettura, l'elaborazione,
l'esecuzione, o la traduzione di testo strutturato o file binari. E
'ampiamente utilizzato per creare linguaggi, strumenti e framework. Da
una grammatica, ANTLR genera un parser in grado di costruire e navigare
alberi di analisi
Ini4j: un API Java per gestire la configurazione dei file Windows .ini
Themis: è una libreria adibita all’Information Retrival.
~ 104 ~
Jbpt-petri: utile per la costruzione e gestione di reti di Petri
Json: una libreria adibita allo scambio di dati. Essa adotta un formato di
testo completamente indipendente dal linguaggio di programmazione
Si è ora pronti a spiegare i passaggi necessari affinché le librerie sopra esposte
diventino in formato Maven, e permettano così l’integrazione di PQL nella
piattaforma web. Il processo che si andrà a spiegare rappresenta una delle
maggiori barriere incontrate durante il processo di sviluppo del modulo software.
Basti pensare che senza tutti i software descritti precedentemente, lo sviluppo di
una grafica per la formulazione d'interrogazioni sarebbe stata fine a se stessa, non
potendo interagire con la logica per cui era stata implementata.
Una delle prime operazioni compiute riguardo le librerie citate, è stata una ricerca
online nel repository di Maven. Ivi si possono ricercare le librerie che sono già in
formato Maven, e che quindi non necessitano una traduzione ma soltanto
l’aggiunta della relativa dipendenza nel pom destinazione. Tale ricerca ha portato
a constatare che le librerie Maven-ready, erano: MySql, PostgreSQL, Ini4j, Json e
Antlr4. Le uniche librerie così che rimanevano da tradurre erano Jbpt-Petri, PQL e
Themis. Per effettuare tale operazione bisognava scaricare il codice sorgente di
ognuna delle precedenti librerie, costruire un progetto Maven che ripercorresse il
loro packaging ed infine scrivere il file pom.xml per ognuno di essi. Pur
sembrando un processo semplice e lineare, bisognava solo dare un nome al
progetto Maven e inserire le giuste dipendenze all’interno del file pom, in realtà
ha presentato non pochi problemi. Infatti una volta compilati ed inseriti all’interno
di APROMORE come librerie Maven, tali librerie generavano conflitti che non
permettevano l’esecuzione del modulo Apromore-Manager. Dopo svariati
tentativi, in cui si è variata la struttura del pom dei vari progetti, si è arrivati infine
ad adottare un approccio basato su OSGI. Quest’ultima tecnologia, spiegata in
parole semplici, non fa altro che prendere un file .jar e trasformarlo in un bundle,
mediante l’opportuna riscrittura del file MANIFEST.MF. Sostanzialmente, una
libreria jar standard contiene al suo interno due oggetti: la cartella META-INF (in
~ 105 ~
cui è contenuto il MANIFEST.MF) e la cartella contenente i file .class (nel caso
sotto riportato la cartella org) [Figura 59].
Figura 59 – Contenuto di un file .jar
Qualora alcune delle classi presenti nel jar avessero bisogno di librerie, queste
ultime potrebbero essere assegnate ad una terza cartella, dal nome lib, proprio
all’interno del jar. Ciò che trasforma un jar in bundle, non è la struttura del jar, per
quanto diversa da quella del bundle, ma il file MANIFEST.MF [Figura 60].
Figura 60 – MANIFEST.MF di un file .jar
~ 106 ~
Figura 61 – MANIFEST.MF di un bundle
Come si nota dalle immagini [Figura 60] e [Figura 61] , il manifest di un bundle
contiene molte più informazioni rispetto a quello di un jar (da notare che
l’esempio di bundle manifest riportato, non è nemmeno completo. In rosso sono
inoltre evidenziate le parti salienti del manifest). Non si spiegherà a cosa servano i
numerosi campi del bundle manifest perché esula dai compiti di questo paragrafo,
però si porrà particolare enfasi su “Export-Package”. Quest’ultimo attributo
elenca le varie cartelle, contenenti .class, di cui è composto il nostro bundle, e per
ognuna di tali cartelle elenca le dipendenze. Per rendere al meglio il concetto,
basta notare la prima riga di “Export-Package”. Si vede infatti che le classi
contenute in org.postgresql usano le classi contenute in org.postgresql.copy,
org.postgresql.fastpath, ecc. Come si può intuire questo approccio è molto simile
alla tecnologia Maven, con la differenza però che OSGI elenca per ogni package
le sue “dipendenze”. Bisogna dunque trasformare i jar generati dalla compilazione
Maven in bundle OSGI. Ma come si possono trasformare i manifest all’interno
delle librerie PQL, Jbpt-Petri e Themis evitando di svolgere tutto il lavoro a
mano? Ad aiutarci in questa operazione è un plugin Maven, “maven-bundle-
~ 107 ~
plugin”. Tramite questo plugin è possibile analizzare la struttura del packagin
della libreria che si vuole trasformare in bundle, e per ogni package al suo interno
è possibile ricavare da quali altre librerie esso dipenda. Un esempio ripreso dal
pom di jpbt-petri, chiarirà meglio quanto spiegato a parole poc’anzi:
Figura 62 – File POM del progetto jbpt-petri OSGI
Nell’esempio di [Figura 62] si evince innanzitutto il goal che si intende
raggiungere al termine della compilazione con Maven (punto numero uno,
packaging bundle). Appurato il prodotto che si vuole conseguire, bisogna però
specificare cosa inserire al suo interno. A tale scopo servono le istruzioni
dichiarate a seguito del punto numero 2 “configuration”. Come si può notare ogni
campo dichiarato sotto il precedente tag, ha un corrispettivo anonimo nel
~ 108 ~
MANIFEST.MF del bundle che si verrà a generare a seguito della compilazione.
Unica eccezione riguarda il tag “Import-Package”, che non avendo figli o testo,
non verrà poi riportato nel file manifest. Inoltre ciò che manca nel pom, è la parte
relativa alle librerie che ogni package usa (quell’elenco di package a seguito della
parola uses presente nel MANIFEST.MF). Quest’ultima parte è generata in
automatico dal plugin ed inserita senza che l’utente si debba adoperare per
controllare a mano ogni dipendenza, facendo risparmiare così tempo. Un tag
particolare su cui prima non ci si è soffermati a lungo, è “Embed-Dependency”. In
questo tag l’utente può inserire una lista di librerie che si vuole importare nel
proprio bundle, e che quindi verrà inserita nella cartella specificata da “Embed-
Directory”. Se si pone maggiore attenzione a quanto dichiarato nel pom file sopra
riportato, si noterà come nella lista delle librerie embedded, si trovi jbpt-petri, che
è la libreria che bisognerà trasformare in OSGI. Quindi ora rimane il dubbio, jbpt-
petri, è stata o no trasformata in libreria OSGI? Ebbene la risposta è no. Infatti si è
deciso di non trasformare direttamente la libreria, soggetta a cambiamenti, in
formato OSGI, bensì si è pensato di creare un wrapper di tale libreria, e con esso
costruire una libreria OSGI che ne ripercorresse fedelmente la struttura. In questo
modo, se la libreria sorgente (jbpt per intenderci), subisse variazioni sulla
struttura, basterebbe cambiare la parte relativa a “Export-Package” nel pom e si
risolverebbero i problemi relativi. Stesso approccio è stato usato anche per le altre
due librerie, PQL e Themis, permettendo al contempo di guadagnare altro tempo.
Infine, per completezza, si è deciso di trasformare in librerie OSGI anche
PostgreSQL, Ini4j, Antlr4, Json e Jbpt-Core. Una precisazione, tutte le librerie
precedentemente citate hanno subito una trasformazione che non è disponibile
online, ma che comunque serve a far funzionare APROMORE in locale. Dunque
se un utente dovesse scaricare APROMORE e non cambiasse tali librerie,
riscontrerebbe numerosi problemi all’atto dell’installazione della piattaforma. Per
risolvere quest’ultimo problema si è deciso di aggiungere ad APROMORE un
modulo speciale che contenesse i pom relativi ad ogni libreria trasformata in
OSGI (il modulo in questione è stato rinominato “Apromore-OSGI-Bundle”). Si
sono poi inseriti ognuno di questi moduli nel pom padre relativo al progetto
APROMORE, per evitare all’utente di doverli compilare uno ad uno. [Figura 63]
~ 109 ~
Come ultima osservazione a conclusione del paragrafo, si riporta un’immagine
raffigurante una delle suddette librerie nel repository Maven [Figura 64]. Come si
può notare vi sono due cartelle facenti riferimento alla medesima libreria. In
sostanza la prima, “pql”, è quella originale non in formato OSGI, ed è quella che
qualsiasi utente può scaricare da internet. La seconda invece, “pql-osgi”, è la
libreria OSGI ottenuta tramite il progetto Maven. In questo modo, se ci dovessero
essere cambiamenti in una libreria, o se uscisse una nuova release, basterà
cambiare qualcosa nel pom della libreria associata, compilare, e il tutto verrà
generato in automatico.
Si conclude così la nostra panoramica
sull’integrazione di librerie esterne in
APROMORE, facendo vedere come
trasformare una libreria esterna in libreria
OSGI tramite l’aiuto di Maven. Spiegato quali benefici questi cambiamenti hanno
apportato al progetto, e cosa bisogna fare qualora ci fossero delle variazioni nelle
librerie sorgenti.
Dopo aver visto come la logica del linguaggio PQL sia stata integrata, si sposta
ora l’attenzione sulla progettazione del modulo software che dovrà garantire le
specifiche espresse nel primo paragrafo del capitolo. Ricorrendo a diagrammi
UML, sequence diagram e casi d’uso, si cercherà di esprimere al meglio tutte le
relazioni tra le classi e laddove necessario si useranno screenshot per far
Figura 64 – Contenuto della cartella pql contentua in .m2
Figura 63 – Moduli trasformati in OSGi
~ 110 ~
comprendere meglio le relazioni tra le classi e le funzionalità messe a
disposizione.
3.3 Progettazione
Prima di iniziare con i vari diagrammi UML e quindi a parlare della progettazione
del modulo software, è necessario effettuare un piccolo riepilogo delle principali
funzionalità del modulo che si andrà a descrivere:
Auto-completamento delle parole chiavi e dei nomi dei modelli di
processo
Evidenziazione degli errori di sintassi
Drag and drop dei modelli di processo da un albero
Tabella delle versioni dei modelli di processo
Tabella riassuntiva dei modelli inseriti nella FROM
Salvataggio/caricamento delle interrogazioni
Tre modalità di scelta dei modelli di processo (ultima versione, tutte le
versioni, versioni a scelta)
L’elenco fa riferimento alle principali caratteristiche che il mostro modulo deve
supportare, ma questo non vieta di inserire altre funzionalità qualora lo si renda
necessario.
Innanzitutto occorre effettuare un’analisi riguardante il contesto a cui si fa
riferimento, per permettere di capire meglio le scelte che si sono effettuate in fase
progettuale. Innanzitutto è noto si dovrà sviluppare un modulo adibito al
riconoscimento e all’elaborazione dei interrogazioni PQL. Tale modulo fa però
parte della piattaforma web Apromore, che come già visto nei precedenti capitoli,
offre una serie di servizi all’utente. E’ lecito dunque immaginare che tali servizi
saranno necessari anche in fase di sviluppo del modulo per poter accedere a
informazioni quali modelli di processo, costruzione dell’albero delle cartelle,
versioni dei modelli, ecc. Inoltre, si dovrà usufruire anche del portale per poter
~ 111 ~
visualizzare i risultati derivanti dalle interrogazioni effettuate dall’utente. Si
individuano così due parti ben distinte ancor prima di passare a progettare
l’architettura delle classi: la prima riguardante la costruzione dell’interrogazione
da parte dell’utente (ed è la parte che sarà interamente sviluppata nel modulo che
si è costruito), mentre la seconda è la parte che deve fornire informazioni sui
modelli di processo al modulo e deve visualizzare i risultati dell’interrogazione
all’utente (la parte che interagisce con i vari moduli di APROMORE). Si
spezzerà così la spiegazione della progettazione in queste due parti, cercando di
favorire il più possibile la comprensione del lettore.
3.4 Progettazione modulo PQL
Per quanto concerna la progettazione del modulo PQL, ovvero della parte adibita
alla formulazione dell’interrogazione, esso si basa sul pattern Model-View-
Controller (MVC). Per analizzare al meglio ognuna delle tre parti, si è deciso di
descriverne le peculiarità prima singolarmente e successivamente mostrare le
connessioni che intercorrono tra loro. In questo modo si spera di generare un’
analisi approfondita ma anche abbastanza chiara.
Il primo componente che si andrà ad analizzare riguarda la grafica, che è anche la
parte maggiormente corposa. Come esposto ad inizio capitolo, si richiede che la
grafica segua lo stile della piattaforma APROMORE, e che quindi possibilmente
sia sviluppata in Zk. Si è così deciso all’inizio di sviluppare una grafica sfruttando
i tag messi a disposizione dalla versione base di Zk. Purtroppo però dopo poco
tempo ci si è accorti che tale strada era impraticabile a causa della mancanza di
componenti adatti a fornire all’utente figure quali auto-completamento ed
evidenziazione degli errori. Si è passati allora a cercare librerie, magari sviluppate
in Zk, che fossero gratuite e di facile comprensione, onde evitare di perdere troppo
tempo. Vedendo però che molte ricerche portavano alle versioni a pagamento di
Zk, si è deciso così di abbandonare l’approccio Zk puro. Per avere comunque una
sorta di continuità con il portale APROMORE, si è allora deciso di provare un
~ 112 ~
approccio misto Zk/Java (albero e tabelle sviluppati tramite componenti Zk,
mentre aree di testo e altre funzionalità sviluppate con la Swing di Java).
Purtroppo però anche questo tentativo non è andato a buon fine a causa
dell’architettura di Zk. Infatti il framework genera un’area virtuale per far
costruire i suoi componenti e generare il codice javascript che costituisce la
pagina html sviluppata. Quest'area virtuale però entra in contrasto con la seconda
area virtuale generata dal codice Java, non rendendo possibile lo scambio di
informazioni tra componenti sviluppati in Zk e componenti sviluppati in Java (non
era possibile eseguire il drag and drop dei processi dall’albero alla textarea). Un
modo per far comunicare le due parti in teoria c’è, e sarebbe quello di creare una
sorta di ponte tra le aree virtuali in RMI o tramite socket, purtroppo però tali
approcci sono abbastanza complessi e possono creare problemi non facilmente
risolvibili. Dunque anche questo secondo approccio è stato abbandonato ed ha
portato alla scelta di una grafica puramente in Java. Il problema ora però da
risolvere è come poter inserire un’applicazione Java in una pagina scritta in Zk (si
ricorda che tutto ciò che si trova nel portal di APROMORE è scritto in Zk, e
quindi il nostro modulo deve essere ospitato in una di queste pagine). La
soluzione è direttamente offerta dal framework tramite il tag “applet”, che
permette così di poter scrivere un’applicazione interamente in Java. Unico
problema rimane l’aspetto della grafica palesemente diverso rispetto a quello del
portale APROMORE. C’è da dire però che Java mette a disposizione un’ampia
gamma di look and feel, nonché la possibilità di crearne altri, che ci permettono
cosi di avere un effetto quanto più simile possibile alla grafica della piattaforma
web.
~ 113 ~
In [Figura 65] è riportata la pagina Zk che ospiterà la nostra applet Java (Come si
può notare dal tab, il nome della pagina è apqlFilter.zul, è tornerà utile a breve). Il
tag principale della pagina è “window” che come si può vedere ha alcuni attributi
(id, width, height, closable, position) di facile comprensione. Quello che però non
è chiaro, è che tipo di finestra sarà la nostra pagina html (si ritaglierà uno spazio
nel portale, sarà una finestra a scomparsa, aprirà un'altra pagina nel browser, ecc).
Le informazioni sul tipo di pagina in costruzione, sono codificate all’interno di
una classe Java adibita al raccoglimento di alcune informazioni [Figura 66]. Come
si vede in [Figura 65], la finestra è creata usando la pagina “apqlFiler.zul”, ed è
assegnata alla variabile “queryWindow”. D’ora in poi tutte le operazioni che si
faranno sulla variabile “queryWindow”, si ripercuoteranno sulla finestra
visualizzata all’utente (vedasi l’assegnazione del titolo alla finestra alla riga
successiva). Come ultimo comando, si nota che sulla finestra è richiamata
Figura 66 – Costruzione del contenuto dell’applet tramite file Java
Figura 65 - Dichiarazione dell’applet all’interno del file .zul
~ 114 ~
un’operazione “doModal”. Ebbene con questo comando si trasforma la nostra
finestra in una sorta di popup che non permette all’utente di interagire con
nient’altro che non sia la finestra stessa. In questo modo l’utente potrà solo
costruire la sua interrogazione, oppure, nell’eventualità che abbia finito di
effettuare le sue operazioni, abbandonare la nostra finestra e ritornare al portale
APROMORE. Un’ultima osservazione da fare sulle poche righe di codice
visualizzate, consiste nella definizione della nostra applet. Come si può vedere
anch’essa viene “prelevata” dalla pagina zul precedentemente mostrata affinché
sia possibile aggiungere alcuni parametri che saranno chiari più tardi. Rimane
però il problema di come identificare la nostra applet, o meglio come fare ad
imporre alla pagina zul il caricamento di una determinata applet. La soluzione è
fornita dagli attributi presenti nel tag “applet”. Infatti come si può notare dalla
prima immagine, il codice da usare è identificato dall’attributo “archive”, in cui si
deve specificare il jar contenente il codice dell’applet che si vuol far girare.
Inoltre, come avviene per ogni applet, bisogna specificare la classe che fa girare
l’applicazione, ovvero segnalare la classe java che estende la superclasse Applet
(da notare in questo caso l’attributo “code”). Con queste semplici operazioni si è
in grado di inserire l’applet, di cui per adesso non si conosce il contenuto,
all’interno della pagina zul che verrà visualizzata all’utente.
Si espone adesso l’organizzazione dei contenuti nell’applet. Tramite un class-
diagram si esporranno, per adesso, le classi principali rinviando in seguito una
discussione più approfondita sui singoli elementi [Figura 67].
~ 115 ~
Come si può vedere dall’immagine [Figura 67], i componenti principali
costituenti l’applicazione sono pochi (si porta il lettore ad osservare che
nell’immagine sono riportati solo componenti grafici, altre classi attinenti al lato
Controller, saranno mostrate in seguito onde evitare confusione). Si parte
naturalmente dalla classe base che è l’Applet, che a sua volta usa un JPanel (la
classe Main) per poter ospitare altri componenti. Affinché tali componenti si
possano disporre nella nostra applet, si deveò specificare il tipo di layout che si
vuole usare. Nella nostra applicazione si è deciso di optare, dopo un’attenta analisi
benefici/svantaggi, di uno SpringLayout capace di garantire una certa flessibilità
nella disposizione dei componenti, nonché di adattarsi automaticamente ai
cambiamenti di dimensionamento del contenitore. Unico aspetto negativo, a mio
avviso, riguarda la prolissità del codice [Figura 68], dato che il programmatore è
costretto ad individuare per ogni componente almeno due punti di riferimento che
consentono al componente di adattarsi in maniera automatica ad un cambiamento
nella grafica. L’immagine riportata qui a lato aiuta il lettore a capire meglio il
discorso appena fatto in merito ai punti di riferimento per un componente.
Figura 67 - Class diagram del tool
~ 116 ~
Avendo visto come sistemare gli elementi della grafica, si può ora passare ad
analizzarli uno ad uno per capire le loro peculiarità.
3.4.1 FolderProcessTree: l’albero dei modelli di processo
Figura 69 – Class diagram della classe FolderProcessTree
Innanzitutto si analizza l’albero dei modelli di processo [Figura 69]. Come si può
constatare il fulcro è la classe FolderProcessTree, erede di JTree (la classe grafica
fornita da Java per la costruzione di alberi), che usa come nodi oggetti di tipo
DraggableNodeTree, erede di DefaultMutableTreeNode. A sua volta quest’ultima
classe si specializza in due versioni: DraggableNodeFolder (per rappresentare le
Figura 68 – Esempio d’uso dello SpringLayout Java
~ 117 ~
cartelle del repository) e DraggableNodeProcess (per rappresentare i modelli di
processo). Tramite questa distinzione si può associare ad ogni nodo dell’albero
un’immagine che la identifica in quanto cartella o modello, ma si consente anche
di intraprendere azioni differenti a seconda del tipo di nodo selezionato
dall’utente. Infatti, come è facile intuire dal nome, qualsiasi nodo dell’albero gode
della funzione drag and drop, che permette all’utente di trascinare uno o più nodi
all’interno dell’area adibita all’interrogazione (si ricorda che nelle specifiche si è
parlato ampiamente di questa funzionalità). Tale funzionalità è offerta mediante le
interfacce DragGestureListener (mediante la quale si riconosce il movimento di
trascinamento di un nodo) e DragSourceListener (che identifica l’albero come
sorgente del movimento di drag & drop). Quello che ancora non è chiaro è la
modalità di selezione degli elementi appartenenti all’albero. Infatti consente
all’utente di selezionare un solo nodo per volta, di avere una selezione multipla
ma contigua oppure una seleziona multipla ma discontinua. Tale politica è scelta
mediante un metodo della classe JTree che permette di cambiare la modalità di
selezione a seconda dello stato in cui l’applicazione si trova (se si ricorda, nelle
specifiche, si è parlato della possibilità di far scegliere all’utente se: selezionare
l’ultima versione di un modello, selezionare tutte le versioni del modello oppure
selezionare solo alcune versioni del modello). Più in particolare, se l’utente
volesse scegliere solo l’ultima versione o tutte le versioni appartenenti ad un
modello, si potrebbe consentire una selezione multipla e discontinua (scelta
effettuabile direttamente dall’albero selezionando solo i modelli a cui si è
interessati). Viceversa, se l’utente volesse selezionare solo alcune versioni di un
modello, questo necessiterebbe una selezione singola dall’albero (relativa al
modello in questione) e il divieto di poter effettuare il drag&drop (dato che la
scelta delle versioni di un modello deve avvenire tramite la tabella mostrata nel
secondo paragrafo). Bisogna inoltre riuscire a distinguere tra nodi “folder” e nodi
“process”. Infatti nel primo caso, trovandoci nella modalità “ogni versioni” o
“ultima versione”, bisogna selezionare anche tutte le sotto-cartelle e i modelli in
esse contenute, mentre in modalità “scegli versione” le cartelle non devono
produrre alcuna selezione, ma solo mostrare il loro contenuto. Nel secondo caso,
quello relativo ai processi, la selezione è sempre permessa in ogni modalità,
~ 118 ~
naturalmente con le dovute restrizioni. Le politiche di selezione appena descritte,
inducono a pensare l’esistenza di uno “stato” associato all’applicazione. Tale stato
è naturalmente scelto dall’utente, e può variare nel corso della stesura di
un’interrogazione (nelle specifiche si è parlato di una serie di JRadioButton che
servivano allo scopo). Si avrà dunque bisogno di una variabile che contenga
questa informazione, nonché di una classe adibita alla sua gestione ed a cui il
nostro FolderProcessTree si rivolge quando l’utente seleziona un modello di
processo. Tale classe è QueryController, che ha al suo interno tre costanti
(CHOOSEVERSION, LATESTVERSION e ALLVERSION) ed una variabile
stato in accordo alla modalità voluta dall’utente. Naturalmente la classe non è
adibita al solo controllo dello stato dell’applicazione, ma per una trattazione più
approfondita si rimanda alla parte dedicata ai controller dell’applicazione (cosi
come per ServiceController e ViewController).
Un’osservazione in merito a come sono ricavate le informazioni contenute
nell’albero è d’obbligo. Come già ampiamente spiegato nel capitolo dedicato ad
APROMORE, ogni utente ha una determinata visibilità su un insieme di processi.
Così, un utente riesce a vedere un modello di processo che invece altri utenti non
sono in grado di poter vedere (si ricorda che uno dei target per la piattaforma web,
sono le aziende, che naturalmente non vogliono far sapere a terze persone i loro
processi). Dunque quando si vuole scrivere un’interrogazione, ma più in generale
quando si accede ad APROMORE, bisogna riconoscere l’utente e sfruttare tale
informazione per ricavare i modelli che bisogna visualizzare. Quest’operazione è
fatta tramite i servizi web messi a disposizione dal modulo Apromore-Manager,
che naturalmente contiene tutta una serie di servizi per effettuare anche operazioni
di ricerca di un determinato processo o di creazione/cancellazione/aggiornamento
di un processo. Per quanto riguarda il nostro modulo per le interrogazioni, l’unica
operazione che ci interessa è la ricerca di quei modelli che l’utente può visionare,
e più in particolare dei modelli che sono sound (condizione necessaria affinché sia
possibile effettuare un’interrogazione su tali modelli). Per quanto concerne la
soundness dei modelli, essa non è verificata all’interno delle classi del modulo
PQL, ma è trattata in un servizio web apposito per le interrogazioni PQL di cui si
~ 119 ~
parlerà più avanti. La ricerca dei modelli di processo è ad opera del
ServiceController, che prelevando le informazioni utente salvate in
ViewController, accede al servizio APROMORE adibito alla gestione del
repository. Il ViewController è il custode delle credenziali utente a causa dell’uso
dell’applet in una piattaforma sviluppata in Zk. Come si è visto ad inizio
paragrafo, l’applet è inserita in una pagina .zul tramite un tag apposito. In questo
modo, anche se il portale conosce l’utente correntemente loggato, l’applet,
essendo una area virtuale a parte, non ha modo di accedere a tali informazioni se
non attraverso l’uso di parametri specifici. Esattamente il caso a cui la nostra
applicazione fa riferimento. I due parametri necessari ad identificare un utente
sono così “USERID” e “SESSIONID”, che passati tramite il ServiceController al
manager di APROMORE, permettono di avere i processi dell’utente.
Rimane aperta un’ultima questione relativa al caricamento di tutti i processi da
visualizzare nell’albero. Nel caso la cardinalità dell’insieme dei processi
appartenenti ad un utente fosse piccola, ad esempio un centinaio di modelli, allora
non si creerebbero problemi nel visualizzare subito l’albero dei processi. Se
invece la cardinalità aumenta, allora potrebbero volerci alcuni secondi, o
addirittura minuti, a causa della mole di informazioni che devono pervenire
all’applicazione tramite internet (una discriminante in questo caso potrebbe essere
anche la lentezza della connessione). Onde evitare tali problemi un possibile
approccio potrebbe essere quello di richiedere solo modelli di processo nei primi
due o tre livelli dell’albero, richiedendo i modelli nei livelli più bassi solo quando
l’utente ne faccia davvero uso. Naturalmente quest’approccio può andare bene
qualora vi siano molti modelli in diverse cartelle annidate, ma non va bene
qualora i modelli si trovino in poche cartelle e tutte al medesimo livello. Inoltre vi
è il problema legato al drag&drop dei modelli nell’area di testo relativa alle
interrogazioni. Qualora l’utente selezionasse una cartella con vari livelli di sotto
cartelle, l’operazione di visualizzare i modelli di processo annidati richiederebbe
comunque qualche secondo, facendo perdere il guadagno in merito alla
posticipazione del caricamento dell’albero. Si è deciso così di caricare l’intero
albero quando l’applet è lanciata dall’utente, in modo che il drag&drop sia
~ 120 ~
immediato, e non costringa l’utente ad aspettare alcuni secondi prima di veder
visualizzati i suoi processi all’interno dell’area di testo.
3.4.2 VariableText: dichiarazione delle variabili
Come si può notare
dall’immagine di [Figura 70],
la struttura relativa alle classi
attinenti alla gestione delle
variabili per
un’interrogazione PQL è
molto semplice. Infatti esse
non devono fare altro che
individuare un determinato
pattern per la dichiarazione di
variabili, e qualora tale
pattern non sia rispettato da
una dichiarazione di
variabile, sottolinearla in
modo da far capire all’utente che è in presenza di un errore. Descrivendo in
dettaglio questa architettura, si nota che l’area di testo, VariableText, usufruisce di
QueryController (solo per passare a quest’ultimo un suo riferimento) e di
VariableListener (adibito proprio all’individuazione degli errori da parte
dell’utente). Dunque la parte principale è proprio quest’ultima classe che ha la
duplice funzione di individuazione degli errori tramite un determinato pattern e la
segnalazione nei confronti dell’utente sottolineando l’errore [Figura 71].
Figura 70 – Class Diagram VariableText
Figura 71 – Pattern riconoscimento variabili
~ 121 ~
Per una migliore comprensione del pattern di riconoscimento delle variabili,
nonché della lista ad esso associata, si riporta un estratto del codice della classe
VariableListener [Figura 79]. Come si può vedere il nome di una variabile PQL,
può essere costituita da numeri (0…9) lettere minuscole (a…z) e dal carattere
underscore ( _ ). Inoltre una variabile può iniziare con una lettera oppure con un
underscore, ma mai con un numero. Tutto ciò che segue il primo carattere
obbligatorio è naturalmente a discrezione dell’utente, entro i canoni prima
stabiliti. Per quanto riguarda invece le etichette contenute in ogni lista, esse
devono contenere solo numeri o lettere, ed inoltre, ogni etichetta deve essere
racchiusa tra apici. Riporto alcuni semplici esempi per una migliore
comprensione:
a_0e = {“Aa0”}; dichiarazione corretta
_a0 = {“aA9”, “Bb2”}; dichiarazione corretta
0_a = {“a”}; dichiarazione errata (il nome della variabile inizia con un
numero)
Naturalmente qualora una dichiarazione fosse errata, nell’area di testo apparirebbe
la segnalazione grafica sotto riportata [Figura 72]:
Figura 72 – Particolare della textarea adibita alla dichiarazione delle variabili
~ 122 ~
3.4.3 QueryText: definizioni dell’interrogazione
Figura 73 – Class diagram della classe QueryText
Nel grafico in [Figura 73] è evidente come la classe QueryText necessiti di un
buon numero di classi affinchè sia in grado di offrire all’utente tutte le
funzionalità enunciate nel paragrafo relativo alle specifiche (auto-completamento,
suggerimenti, parole chiave evidenziate, ecc). I primi due elementi su cui bisogna
soffermarsi sono: DropTargetListener e JTextPane. Il primo elemento,
un’interfaccia per il drag&drop, va a completare il duo sorgente/destinazione
precedentemente visto nel paragrafo dedicato all’albero dei processi. Infatti i
modelli di processo sono trascinati dall’albero, e una volta rilasciati nel
QueryText vengono inseriti nella clausola FROM dell’interrogazione. Qualora
l’utente non abbia scritto nulla, o abbia solo accennato la parola SELECT, allora il
trascinamento di alcuni elementi causa il completamento dell’interrogazione in
automatico. Inoltre, si hanno comportamenti diversi a seconda che l’elemento
trascinato nell’area dell’interrogazione sia un modello o una cartella. Nel primo
caso verrà inserita nella clausola FROM il nome relativo al modello, o ai modelli
trascinati; nel secondo caso, non verrà visualizzato il nome della cartella, ma bensì
tutti i modelli contenuti in essa ed anche i modelli contenuti all’interno delle sotto
cartelle. Unico problema in questo caso sarebbe come disambiguare due modelli
~ 123 ~
aventi medesimo nome ma cartella differente. La soluzione che si è proposta,
riguarda il percorso da seguire all’interno dell’albero per arrivare ad un
determinato processo. Infatti non è possibile trovare due modelli aventi medesimo
nome all’interno della stessa cartella. Così quando l’utente inserisce un modello
all’interno dell’area di testo, esso comparirà come un path, alla cui fine si troverà
il modello di nostro interesse:
Home/cartella1/…/modello
Vi è però un altro problema legato alle tre modalità di selezione delle versioni
relative ad un modello di processo. Infatti, come già spiegato, l’utente può
selezionare ultima versione, tutte le versioni o solo alcune versioni di un
determinato modello. Tali informazioni, se si decidesse di inserire in QueryText
solo i path dei modelli, farebbero dimenticare all’utente che tipo di versioni si
selezionano per ogni modello. Onde questo tipo di problemi, si è deciso di inserire
alla fine del path appartenente ad un modello, un identificativo fittizio (non è una
parola chiave facente parte del linguaggio PQL) del tipo: LATESTVERSION,
ALLVERSIONS o CHOOSEVERSION. In questo modo l’utente riesce a
riconoscere con che tipo di versioni sta scrivendo la sua interrogazione.
Home/cartella1/…/modello{ALLVERSIONS}
Nel caso però della terza modalità, quella in cui un utente sceglie un sottoinsieme
di versioni con cui eseguire l’interrogazione, una sola parola chiave come
CHOOSEVERSION può non andare bene (ad esempio per inserire due versioni di
uno stesso modello i nomi verrebbero a collidere). La soluzione è riportare allora
una tripla che identifica la versione del modello con cui si sta trattando. Dato che,
come accennato nei precedenti paragrafi, la versione di un modello è scelta
trascinandola dalla tabella posta sotto l’albero dei modelli, e che tale tabella è
formata da id, branch e version, bisogna cambiare il path del processo in:
Home/cartella1/…/modello{id/branch/version}
In questo modo si ha la sicurezza di quali modelli sono stati inseriti nella nostra
interrogazione.
~ 124 ~
Una peculiarità molto interessante legata all’approccio appena descritto, riguarda i
ripensamenti che l’utente ha nel scrivere un’interrogazione. Infatti ammettendo
che il nostro utente selezioni determinati modelli con la modalità
ALLVERSIONS, ma che in seguito ci ripensi e voglia prendere in esame le ultime
versioni di determinati modelli, si può pensare di non costringere l’utente a
cancellare a mano quei modelli “ormai inutili”, ma forzare l’utente a cancellare a
mano i modelli che sono “inutili”. Il modo più semplice ed elegante per dare
soluzione a questo problema, riguarda la sostituzione dei path appartenenti ai
modelli che si vogliono eliminare dall’interrogazione con quelli che invece si
vogliono inserire. In questo modo se nella clausola from avessimo:
Home/cartella/modello{ALLVERSIONS} diventa
Home/cartella/modello{LATESTVERSION}
Medesima soluzione nel caso in cui un utente volesse cambiare il primo modello,
con un insieme di modelli scelti dalla tabella delle versioni.
Si passa ora ad analizzare cosa permette di fare il JTextPane nell’area di testo.
Innanzitutto è bene partire da cosa ci permette di fare una semplice area di testo,
per capire meglio il perché non sia stata scelta come alternativa nello sviluppo
dell’area adibita all’interrogazione. Le uniche operazioni ammesse da una
JTextArea, riguardano solamente il font dei caratteri, la loro formattazione e il
colore che si vuole dare al testo. In particolare mi soffermo sull’ultima
caratteristica enunciata, il colore che si può dare all’intero testo, perché risulta il
vero limite per cui si è scartata tale scelta. Infatti, è vero che si vuole fornire
all’utente un modo semplice ed efficace per evidenziare le parole chiave del
linguaggio PQL, ma attraverso una JTextArea questo non risulta possibile dato
che dovremmo colorare interamente il testo. Possibilmente bisogna trovare un
oggetto capace di comportarsi come una pagina HTML, ovvero che abbia la
capacità di strutturare in qualche modo il testo, e ci permetta di concentrarci solo
su determinate parole dell’interrogazione. Il JTextPane ci permette di evidenziare
solo determinate parola del testo agendo sullo StyleDocument associato. Cosi
facendo si è in grado di evidenziare le parole chiave di PQL, cambiare colore al
~ 125 ~
contenuto della clausola SELECT, cambiare font per i modelli di processo o
locazioni presenti nella clausola FROM. Insomma è possibile creare a piacimento
l’intera interrogazione.
Ora se il JTextPane ci permette di costruire l’interrogazione con diversi colori e
font, si deve avere una classe che, presa l’interrogazione correntemente scritta
dall’utente, l’analizzi e individui le parole da evidenziare. Guardando il nostro
grafico UML, si capisce che tale classe può essere solamente Highlight. Quando
l’utente digita una lettera nell’area di testo, l’azione è catturata tramite il
WordListener (ascoltatore erede di KeyListener), e tutto ciò che è presente nel
JTextPane viene inviato alla classe Highlight per essere evidenziata. Naturalmente
se vi sono già parole evidenziate, esse vengono ignorate, si andranno solo a
colorare le ultime parti aggiunte. Per capire meglio tale meccanismo, basti pensare
che un’interrogazione non è altro che un insieme di parole del tipo: parole chiavi,
locazioni, attributi ed etichette. Le parole chiavi sono un insieme fisso (o poco
variabile dato che probabilmente le parole al suo interno verranno estese in un
prossimo futuro), le locazioni seguono una determinata struttura, gli attributi
(appartenenti alla SELECT) sono anch’essi limitati e scelti all’inizio, e le etichette
si possono solo trovare dopo la WHERE. Seguendo queste semplici regole, è
possibile individuare facilmente ogni tipo di elemento contenuto
nell’interrogazione, ed evidenziarlo nella maniera più opportuna. Ad esempio,
nella prima implementazione del modulo, si è scelto di evidenziare le parole
chiave in rosso, gli attributi in blu mentre locazioni ed etichette non sono state
evidenziate. Naturalmente qualora si volessero cambiare colori o font o entrambe
le cose, basterebbe cambiare solo alcune variabili all’interno della classe per avere
l’effetto voluto. Ora che si è spiegato come si evidenziano le parole, si possono
spendere due parole su come riconoscerle. Banalmente, basta avere una lista delle
parole chiavi, degli attributi, un pattern per riconoscere le locazioni e un altro per
riconoscere le etichette. Inoltre si può sfruttare la divisione dell’interrogazione in
tre macro parti, corrispondenti naturalmente alle clausole presenti in essa:
SELECT, FROM, WHERE. Infatti, facendo un piccolo ragionamento, si avrà che:
~ 126 ~
La parola SELECT non è preceduta da nessun’altra parola (altrimenti
l’interrogazione sarebbe malformata) ed a valle può avere solo attributi
(ID, name, version, owner, ecc) prima di incontrare la parola chiave
FROM;
La parola FROM è preceduta da attributi ed è seguita da locazioni. Dato
che la parola WHERE è opzionale e non obbligatoria, si deve avere che
l’interrogazione finisca dopo l’elenco delle locazioni, oppure prosegue con
la clausola WHERE;
La parola WHERE è preceduta da locazioni e termina con il “;”, indicante
appunto che l’interrogazione è conclusa.
Grazie a questa suddivisione è possibile sia evidenziare correttamente le parole,
ma anche di fornire adeguati suggerimenti all’utente nella stesura della query.
Intatti WordListener, non solo fornisce aiuto alla classe Highlight, ma offre anche
le funzionalità di auto-completamento e suggerimenti.
Si espone innanzitutto cosa si intende per auto-completamente e per suggerimento
all’interno della nostra interrogazione. La prima funzionalità consiste
essenzialmente nel completare una parola scritta a metà con un insieme di parole
aventi medesima radice (esempio se si ha “pro”, risulteranno opzioni come:
“prodotto”, “produzione”, ecc). Inoltre sarebbe auspicabile che se la parola di cui
vorremmo l’auto-completamento si trovasse nella clausola FROM, allora
l’applicazione ci deve fornire solo le parole attinenti a quella clausola (ovvero
solo locazioni e la parola chiave WHERE, e non attributi o etichette). Per ottenere
questo risultato si devono quindi individuare determinati contesti all’interno
dell’interrogazione, lavoro non semplicissimo date le numerose possibilità offerte
dal problema:
Interrogazione vuota: ovvero non si è scritta alcuna parola all’interno
dell’area di testo
E’ presente solo la parola SELECT
~ 127 ~
E’ presenta la parola SELECT e un attributo
E’ presente la parola chiave FROM, senza locazioni, anticipata dalla
clausola SELECT
Sono presenti le clausole SELECT e FROM, entrambe con elementi al
loro interno
Vi è la parola WHERE, ma senza predicati al suo interno
Vi è la parola WHERE con alcuni predicati al suo interno
Interrogazione Malformata
A queste casistiche vanno poi aggiunti i casi in cui una locazione non è completa,
lasciando così la possibilità all’utente di adoperare le opzioni di auto-
completamento e suggerimento (basti pensare ad un caso del tipo
“Home/cartella1/” nel quale è necessario restituire all’utente tutti i modelli
contenuti nella cartella1 più tutte le sotto-cartelle). Insomma un lavoro del genere,
affinché sia il codice reso il più chiaro possibile, non può essere messo nella
classe WordListener, adibita al riconoscimento delle parole. Si è così scelto di
creare un controller apposito per la gestione dei contesti all’interno
dell’interrogazione, che collaborando con WordListener, Highlight e QueryText,
fosse in grado di effettuare tutte le distinzioni precedentemente elencate. Inoltre,
in base al contesto, essa è in grado di fornire i giusti suggerimenti tramite l’uso di
una finestra popup che l’utente può visualizzare tramite la combinazione di tasti
CTRL+SPACE [Figura 82].
~ 128 ~
Figura 74 – Particolare del menu per il suggerimento delle parole chiavi nel tool
Come si può vedere [Figura 74] la finestra è anche fornita di barra scrollabile in
modo che se le parole sono in gran numero, vengono visualizzate solo le prime
cinque opzioni, mentre tutte le altre saranno raggiungibili tramite barra. Inoltre,
qualora l’utente volesse raffinare la ricerca aggiungendo caratteri alla parola di cui
vuole il suggerimento, il contenuto della finestra viene aggiornato
automaticamente ed inoltre la finestra si allinea sempre con l’ultimo carattere
della parola. Naturalmente, la logica che è alla base di tale finestra, segue le
medesime dinamiche di individuazione della parola e ricerca delle parole simili.
Un caso particolare, per quanto riguarda auto-completamento e suggerimento, è
quello delle locazioni. Infatti qualora un utente volesse un aiuto per completare il
nome di una locazione, o anche solo per ricercarlo, si è costretti a scorrere l’albero
per individuare il giusto modello o la giusta cartella. Inoltre, se l’utente sbagliasse
nel selezionare un modello o una cartella, cancellando l’ultima parola o anche solo
un carattere di essa, bisognerebbe aggiornare il contenuto della finestra popup dei
suggerimenti.
~ 129 ~
3.4.4 ButtonAction: shortcut per aiutare l’utente
Come mostrato in [Figura 83], la nostra applicazione dispone di alcune shortcut
utili per
velocizzare
la stesura
delle
interrogazio
ni da parte
dell’utente.
La funzione
di alcune di
esse è già
abbastanza
nota, dato
che la
maggior parte dei programmi commerciali usa le medesime combinazioni di tasti.
Altre invece sono state create appositamente per poter svolgere funzioni di aiuto
nell’ambito della formulazione di interrogazioni PQL:
CTRL+X (taglia): elimina una parte di testo dall’area;
CTRL+V (incolla): incolla una porzione di testo precedentemente copiata
o incollata;
CTRL+C (copia): copia una porzione di testo;
CTRL+A (seleziona tutto): seleziona tutto il testo presente nell’area di
testo;
CTRL+Z (undo): elimina le conseguenze dell’ultima operazione
effettuata;
CTRL+Y (redo): operazione contraria dell’undo;
CTRL+SPACE (suggerimento): visualizza la finestra popup contenente i
suggerimenti;
Figura 75 – Class diagram della classe TextAction
~ 130 ~
CTRL+BACK (collassamento/rilassamento): collassa/rilassa la clausola
FROM per visualizzare/nascondere le locazioni;
Ognuna delle suddette operazioni deve innanzitutto interagire con l’area di testo
in cui è racchiusa l’interrogazione (si pensi alle operazioni di copia, incolla e
taglia) e naturalmente si deve appoggiare sui controller ViewController e
QueryController (JTextPane, VIewController e QueryCOntroller, non sono stati
inseriti nel diagramma UML per motivi di chiarezza). In sostanza i controllori
terranno, tramite variabili, una copia dell’interrogazione correntemente salvata
nell’area di testo, e in dipendenza delle shortcut usate dall’utente tale stringa verrà
modificata o usata così com’è. Si hanno però delle eccezioni che riguardano
questa regola: suggerimento, redo e undo. Infatti, per quanto riguarda il
“suggerimento”, esso modifica solo una parte dell’interrogazione (ad esempio
quando si selezionia un modello di processo da aggiungere nella clausola FROM)
oppure visualizza solo una finestra popup di cui l’utente può anche non
selezionare nulla. Per quanto concerne invece undo/redo esso servono ad avere
una cronologia di ciò che l’utente ha scritto nell’interrogazione. Così facendo
l’utente non è costretto a riscrivere a mano una parte dell’interrogazione che
aveva cancellato erroneamente. Affinché tale meccanismo sia reso disponibile
all’utente, occorre implementare una piccola lista in cui ogni posizione contiene
una copia dell’interrogazione in diversi momenti. Ogni volta che l’utente userà
CTRL+Z o CTRL+Y, un indice scorrerà la lista prelevando man mano
l’interrogazione in quella locazione (se l’utente dovesse premere CTRL+Z e il
cursore è in posizione zero, allora l’interrogazione nell’area di testo rimarrà
invariata. Stessa cosa in caso di ultima posizione quando l’utente userà CTRL+Y).
3.4.5 TableProcess: la tabella delle versioni
Si sposta ora l’attenzione sulla tabella da cui l’utente può selezionare le versioni
da inserire nella clausola FROM della propria interrogazione. Come si evince dal
diagramma UML [Figura 76], la tabella non è altro che un semplice pannello che
sarà poi integrato nella grafica del modulo. TableHeader e TableRow, come si
~ 131 ~
intuisce dal nome, rappresentano l’intestazione della tabella (dove vengono
inseriti i titoli delle colonne) e le righe della stessa (quelle in cui sono elencate le
versioni del processo selezionato nell’albero). Una particolarità riguardante la
tabella, è quella di non avere un numero di colonne fisse, bensì variabile. In
questo modo, se in futuro si volesse aggiungere un campo alla tabella, l’utente
dovrà soltanto aumentare il numero di colonne, come esse saranno gestite sarò
compito della tabella. L’interfaccia Observable è stata pensata appositamente
seguendo il pattern Observer (da notare anche la presenza dell’interfaccia
Observable legata al TableHeader), per far aggiornare il contenuto della tabella a
seconda delle operazioni effettuate dall’utente sul TableHeader. Infatti, se l’utente
clicca o trascina una delle colonne avrà due effetti differenti: cliccando su un
titolo, vedrà un ordinamento alfabetico della tabella in dipendenza del campo in
cui si è cliccato (ad esempio, cliccando sull’ID si avrà un ordinamento in base
all’ID, se si clicca sul branch si vedrà un ordinamento in base al branch, e cosi
via), se invece si trascina una colonna, essa si ridimensionerà secondo le necessità
dell’utente (naturalmente si è previsto una grandezza minima e massima, in modo
Figura 76 - Class diagram della classe TableProcess
~ 132 ~
da non costringere la tabella ad ingrandirsi in maniera sproporzionata). Un’ultima
osservazione in merito al TableHeader, è l’uso della classe HeaderLabel che
rappresenta il titolo di ogni colonna. Tramite esso si è potuto aggiungere
un’animazione nel caso in cui l’utente compia un ordinamento su una colonna (un
click e compare una freccia che segue il tipo di ordinamento,
discendente/ascendente, della colonna).
Per quanto riguarda invece TableRow, la seconda classe usata da TableHeader,
essa racchiude una versione di un modello, ed è anch’essa formata da più label
che racchiudono a loro interno le informazioni di ogni colonna (ID, version e
branch). Come si può notare dal diagramma UML, essa sfrutta quattro interfacce,
ognuna che ricopre un preciso compito:
MouseListener: è possibile selezionare più righe della tabella che saranno
poi inserite nell’area dell’interrogazione. Quando l’utente clicca su una
riga, essa cambia colore, notificando così di essere stata selezionata
dall’utente. Una volta che le righe sono state inserite nell’area di testo,
esse tornano al loro colore originale.
Comparable: tale interfaccia serve solo nel caso in cui un utente, cliccando
su un titolo della tabella, volesse ordinare le righe. A seconda del titolo
cliccato, le righe sono comparate tra loro, inserite in maniera ordinata in
una lista e a seguito visualizzate nella tabella.
DragSourceListener, DragGestureListener: sono le due interfacce adibite
al trascinamento delle righe nell’area dell’interrogazione. Così facendo, le
informazioni contenute in una riga sono visualizzate nell’interrogazione
nell’apposita clausola FROM.
Per quanto riguarda la trattazione della tabella delle versioni non vi è più nulla di
aggiungere. Vi è però un'altra tabella simile a quella descritta finora, ma che ha
tutt’altro scopo. Come anticipato nelle specifiche ad inizio capitolo, vorremmo
anche avere una tabella che potesse riassumere tutti i modelli inseriti nella
clausola FROM. Ebbene tale tabella è costruita con le medesime classi adoperate
~ 133 ~
per la tabella delle versioni, ma contiene più colonne (ID, name, version, branch,
owner, latest release, ecc). Naturalmente tale tabella non offre un sistema di
trascinamento delle colonne, dato che serve solo come fonte di riepilogo, ma ha il
ridimensionamento delle colonne. Per poter accedere a questa tabella di riepilogo,
è messo a disposizione un apposito tasto nella schermata principale della grafica,
che aprirà una finestra che rimarrà in primo piano a meno che non venga chiusa.
3.4.6 Salva/Carica: memorizzazione e riuso vecchie interrogazioni
Come nella maggior parte delle applicazioni attuali, si offre all’utente la
possibilità di salvare e caricare i propri dati sul disco locale, o in casi molto
particolari anche in rete. Per quanto riguarda la nostra applicazione, essa al
momento offre la possibilità di salvare/caricare interrogazioni dal disco locale. In
futuro si mira ad espandere questa funzionalità incorporando il
salvataggio/caricamento
d’interrogazioni dal WebDav
(un’estensione del protocollo http
che permette all’utente di accedere
a risorse remote con la praticità
della navigazione sul disco
locale). Ritornando a ciò che il modulo offre, il salvataggio di un’interrogazione
su file è effetuata mediante un semplice xml avente la seguente struttura:
Come si può vedere in [Figura 77] i tag presenti nella struttura sono due:
variablesText, in cui sono salvate le dichiarazioni delle variabili, e queryText, in
cui è presente il testo dell’interrogazione. Tale struttura è stata scelta per due
motivi principali:
1. Nel dichiarare le variabili, l’utente può inserire, tra una variabile ed
un'altra, un qualsivoglia numero di spazi bianchi e/o andate a capo,
rendendo il compito di separare variabili e interrogazione arduo.
Figura 77 - Struttura di un generico file per il salvamento delle interrogazioni
~ 134 ~
2. Avendo nell’interfaccia grafica due sezioni in cui l’utente può scrivere, si
è deciso di far corrispondere nel file una sezione dedicata alle variabile ed
un'altra dedicata all’interrogazione.
Seguendo le suddette considerazioni risulta molto più comodo elaborare un
semplice xml, che analizzare riga per riga il contenuto del file. Inoltre grazie
all’xml non è necessario preoccuparsi di simboli dalla duplice funzione (ad
esempio un medesimo simbolo usato come separatore tra variabili e
interrogazione, che può comparire anche in una delle due sezioni). Ciò che si
andrà a prelevare è solo il testo scritto dall’utente.
Si è così analizzata tutta la grafica sviluppata ai fini della stesura
dell’interrogazione, vedendo in modo approfondito ogni singola funzionalità e
come essa è stata implementata, nonché le scelte chi si sono fatte e le motivazioni
che ci hanno spinto a quelle scelte. Prima di passare a vedere come i risultati
prodotti dalle interrogazioni effettuate dall’utente vengano visualizzate sul portale,
si riporta di seguito una sreenshot riepilogativa del tool che si è sviluppato [Figura
78].
~ 135 ~
Figura 78 – Grafica del tool completa
3.5 Portal-client: l’applet comunica col portale
Come già accennato più volte nei capitoli precedenti, far comunicare direttamente
applet e Apromore non è possibile a causa della formazione di due aree virtuali
(quella adibita a Zk con cui è costruito Apromore, e quella creata dalla nostra
applet). Si è accennato anche ad alcune possibili soluzioni quali utilizzo di RMI o
socket, che costituiscono canali di comunicazione tra due diverse aree virtuali.
Purtroppo tali idee, seppur fattibili anche se complesse da implementare,
risultavano essere troppo poco flessibili, soprattutto considerando che bisognava
trovare un modo per inserire informazioni generate in qualsivoglia modo (da
applet o tramite un modulo appartenente ad Apromore) sul portale. In sostanza
quello che si cercava di implementare, riguardava una sorta di servizio espandibile
secondo le esigenze degli sviluppatori, capace di trasformare la grafica del portale,
senza ovviamente snaturarla, per inserire nuove informazioni. Dopo molti
tentativi, si è così arrivati alla progettazione di un servizio web interamente
dedicato al portale.
~ 136 ~
Una volta trovata “la soluzione” bisognava però risolvere alcuni problemi
riguardanti: lo spazio in cui visualizzare i risultati (come si vede dalla fotografia
in basso, il portale di Apromore è interamente dedicato alla lista dei modelli
contenuti in una cartella) e come rendere tale servizio il più generale possibile.
Dopo un’attenta analisi dello spazio a disposizione, ed avendo preso spunto dalla
grafica presente in numerosi programmi, si è deciso di optare per la costruzione di
un sistema di tab da aggiungere alla grafica principale. Le motivazioni che hanno
condotto ad abbracciare tale scelta sono principalmente legate alla gestione dello
spazio offerto dal portale (non si poteva stravolgere la grafica esistente, e non
potendo aggiungere nulla bisognava creare altro spazio), ma anche dalla
flessibilità che offriva. Infatti aggiungendo una tab, oltre ad avere un’intera pagina
in cui poter inserire una lista di risultati, si è in grado di creare diverse strutture,
ognuna diversa in dipendenza di cosa si vuole mostrare all’utente. In questo
modo, se per esempio, si visualizzano i risultati dell’interrogazione tramite una
lista, un altro sviluppatore potrà costruire una tabella per i suoi scopi ed inserirla
in un tab senza alcuno sforzo (naturalmente dovrà modellare la struttura della sua
tab, ma il “contenitore” è lo stesso. Non cambia).
Figura 79 – Flusso delle informazioni per la visualizzazione dei risultati
Come si può vedere dall’immagine in [Figura 79], i moduli coinvolti nella
creazione del servizio attraverso cui viaggiano le informazioni sono tre: PQL (il
~ 137 ~
modulo in cui si è sviluppata l’applet), portal-client (la sede del nostro servizio
web), e Apromore-Portal (in cui è contenuta tutta la grafica della piattaforma
Apromore). Naturalmente le informazioni che sono scambiate tra questi moduli,
non possono certo essere come semplici oggetti. Avendo costruito un web-service
bisogna codificare i messaggi attraverso xml-schema, e solo allora essi potranno
viaggiare attraverso la rete e raggiungere il destinatario (in questo caso il portale
Apromore) [Figura 80]:
Figura 80 – Dichiarazione dei messagi scambiati tra il tool e il servizio adibito alla visualizzazione dei risultati
Il primo complexType ad essere dichiarato è il contenitore di tutto ciò che verrà
visualizzato lato portale. Come si può vedere esso è composto da più campi:
userID: contenente l’identificativo dell’utente che ha effettuato
l’interrogazione;
query: contenente l’interrogazione e le variabili usate in essa;
~ 138 ~
nameQuery: è il nome che si è voluto dare all’interrogazione e che sarà
visualizzato sulla tab che ospiterà i risultati;
results: i modelli che corrispondono all’interrogazione effettuata;
details: ovvero eventuali dettagli aggiuntivi che provengono dalla logica
del linguaggio PQL;
Di questi cinque campi, uno ha una valenza particolare senza cui non sarebbe
stato possibile implementare la soluzione dei tab: userID. Infatti, tramite esso è
possibile identificare l’utente che ha scritto un’interrogazione, e tramite esso far
visualizzare ad un utente solo i suoi tab e non quelli di altri utenti. Infatti
trovandoci di fronte ad un servizio web che è usato da ogni utente, bisognava
trovare un modo per associare una lista di tab ad un utente e viceversa, ed inoltre
bisognava assicurare che ogni utente facesse riferimento a tale “supervisore”. Il
metodo più elegante, ma anche quello più semplice, consisteva nel creare una
classe singleton [Figura 81] che attraverso una mappa memorizzasse al suo
interno utenti e rispettive liste di tab:
Figura 81 – Classe SessionTab
~ 139 ~
Come si può vedere il costruttore è naturalmente dichiarato “private” affinché la
classe possa essere singleton. Per quanto riguarda gli ultimi due metodi,
setTabsSession e getTabsSession, essi garantiscono che ogni utente acceda alla
propria lista dei tab. Quello che però non è chiaro, è come i tab siano aggiunti alla
lista appartenente ad ogni utente, poiché tramite questa classe si può solo
prelevare o cambiare la lista di un utente. Ebbene questa parte è affidata alla
classe MainController, ovvero la classe adibita alla gestione del portale
Apromore. Di seguito è riportato il metodo adibito alla creazione e all’aggiunta
del nuovo tab nella lista di un utente [Figura 82], e come il lettore potrà
constatare, vi saranno tutte le informazioni che si sono trasmesse grazie a
TabAPQLInputMessageType (ovvero il messaggio mostrato in precedenza).
Figura 82 – Metodo per la visualizzazione dei tab nel portale Apromore
3.5.1 TabQuery: come vengono rappresentati i risultati
Nel precedente paragrafo l’attenzione è stata focalizzata su come le informazioni,
dall’applet, arrivino al portale. Il tema di questo capitolo sarà invece i contenitori
di queste informazioni, i TabQuery. Il punto di partenza consiste nello specificare
che tipo di classe essi siano, o meglio dire che tipo di oggetti siano. Infatti, come
visto nel capitolo Apromore, la grafica della piattaforma è interamente sviluppata
in Zk, che risulta quindi la fonte di ogni classe che si sviluppa nella grafica. Nel
nostro caso, si è estesa la classe Tab disponibile in varie versioni e di cui sono
presenti diversi tutorial online. L’obiettivo a cui si è voluti giungere però, non
consisteva nel costruire un semplice tab come quelli presenti online, ma creare
~ 140 ~
qualcosa di nuovo e particolare. Di seguito sono elencate le peculiarità del nostro
TabQuery [Figura 83]:
ogni utente ha a disposizione una lista di tab, ognuno facente riferimento
ad un’interrogazione. Onde evitare di saturare lo schermo di tab, si è
deciso di dare la possibilità all’utente di chiudere quelli di cui non ha più
bisogno. Attenzione, il primo tab sarà riservato alla lista dei modelli
contenuti in una cartella del repository, e tale tab non dovrà mai chiudersi
[Figura 83].
Figura 83 – Portale Apromore con la presenza dei tab per i risultati
Quando una tab viene chiusa dall’utente, essa dovrà rimuoversi
automaticamente dalla lista associata all’utente. Analizzando il codice di
gestione della lista dei tab, si è potuto vedere che in nessun modo è
possibile eliminare la tab, infatti sarà essa stessa a rimuoversi senza
affidare il compito ad altre classi.
Ogni tab contiene un nome tramite cui l’utente può identificare la
rispettiva interrogazione. In sostanza nell’applet, vi è un campo editabile
in cui l’utente può scrivere il nome da associare all’interrogazione [Figura
84]. Qualora l’utente non assegnasse nessun nome, allora il nome del tab
sarà costituito da un indice incrementale.
~ 141 ~
Figura 84 – Se un utente sceglie un nome per la sua interrogazione, lo stesso nome comparirà nel tab risultato
Cliccando sul nome del tab, sarà visualizzata una tabella contenente
ulteriori informazioni sull’interrogazione. In pratica sarà visualizzata una
tabella e un’area di testo non editabile, nella prima saranno visualizzate le
etichette usate nell’interrogazione e le parole ad esse simili contenute nel
database, mentre nella seconda sarà visualizzata l’interrogazione associata
a quel tab. In questo modo l’utente potrà confrontare i risultati e
interrogazione, per confermarne l’esatta formulazione.
La lista dei risultati [Figura 85], che come si è visto è simile alla lista dei
modelli di processo, avrà l’ordinamento in base alla colonna selezionata.
In pratica se l’utente clicca su una colonna, i risultati si disporranno in
ordine alfabetico (crescente/decrescente a seconda del numero di click su
quella colonna).
Si è così completata la panoramica riguardante l’implementazione dei tab,
spiegando anche in linea generale le funzioni disponibili per l’utente.
Figura 85 – Visione generale del portale Apromore avente un risultato
~ 142 ~
3.6 Indicizzazione dei modelli di processo
Nei paragrafi esposti in precedenza, è stato mostrato come utilizzare il linguaggio
PQL per effettuare interrogazioni su un insieme di modelli di processo, ma
soprattutto come effettuare interrogazioni attraverso l’apposito tool analizzato in
questo capitolo. Si è visto inoltre come un modello, affinché possa essere
interrogato, debba essere rappresentato attraverso una workflow net e
contemporaneamente essere sound. Dato che Apromore lavora con più tipi di
linguaggi di rappresentazione (YAWL, BPMN, EPML, ecc.) mentre PQL lavora
solo con reti di Petri, si è accennato ad una traduzione dei modelli dal formato
originale in workflow net (di cui successivamente si analizza la soundness).
Infine, se il modello soddisfa tutti i requisiti, si passa alla memorizzazione delle
sue informazioni per consentire all’utente di poter effettuare le proprie
interrogazioni.
Pur sembrando una sequenza di operazioni “banali”, in realtà costituiscono una
criticità per quanto concerne il salvataggio dei modelli in Apromore. Infatti, ogni
qual volta un utente crea, modifica o importa un modello nel repository
Apromore, si deve necessariamente analizzare quest’ultimo per aggiornare i due
database presenti al momento in Apromore: MySql, adibito al salvataggio delle
informazioni dei modelli nel loro formato originale, e PostgreSQL, adibito
all’indicizzazione del modello (ovvero a salvare le informazioni utili al linguaggio
PQL). Per aiutare il lettore a seguire la sequenza di passi necessari affinché un
modello sia salvato ed indicizzato, si riporta di seguito [Figura 94]:
~ 143 ~
Figura 86 – Flusso di informazioni per l’indicizzazione di un modello
Da [Figura 86] è evidente come il processo di salvataggio di un modello sia in
realtà diviso in tre macro-sezioni. La prima, riguardante l’utente e la sua
interazione con l’editor di Apromore per creare un modello. Successivamente,
qualora l’utente voglia salvare il suo lavoro nel repository, interviene il
ProcessServiceImpl, ovvero il servizio presente in Apromore adibito al
salvataggio dei modelli nel database. Infine vi è PQLServiceImpl, che come
intuibile dal nome, svolge attività prettamente legate alla libreria PQL (quella
adibita ad effettuare interrogazioni sui modelli). Quello che è stato tralasciato di
proposito nel modello, è un’indicazione circa la modalità con cui avviene tale
processo. Quello che è sicuro, oltre alle funzioni ricoperte da ogni parte mostrata,
è che vi sarà uno scambio di messaggi tra le varie componenti con lo scopo di
pilotare le funzioni di ogni parte.
Quando si parla di scambio di messaggi tra due o più parti, è naturale chiedersi se
le comunicazioni tra le parti coinvolte sia sincrona o asincrona. Con la prima
s’intende una comunicazione in cui il mittente, per poter proseguire nelle sue
operazioni, deve attendere che il ricevente risponda al messaggio appena inviato.
La seconda tipologia invece, in antitesi con la precedente, permette al mittente di
inviare un messaggio e proseguire nelle sue operazioni fintanto che il destinatario
~ 144 ~
del messaggio non risponde. Nel caso di [Figura 94], quello del salvataggio di un
modello, è lecito chiedersi quale approccio sia migliore per garantire prestazioni
elevate all’utente, o riformulando, per evitare che l’utente sia costretto ad
aspettare a lungo prima di poter riprendere a lavorare in Apromore. Come è facile
intuire, la prima parte del modello, quella in cui l’utente interagisce con l’editor di
Apromore per la creazione del modello, è ininfluente riguardo la scelta operazioni
sincrone/asincrone. Infatti, per creare un modello l’utente impiega tutto il tempo
di cui ha bisogno ed è sicuramente un’operazione sincrona, dato che non può
effettuare altre operazioni. Non appena l’utente avrà finito di rappresentare il suo
modello di processo, e sceglierà di voler salvare tale modello nel repository, allora
lì si avrà una prima “criticità”. Come detto nei paragrafi precedenti, affinché un
modello sia univoco, e possa quindi essere salvato correttamente, è indispensabile
che abbia la tripla: id, version e branch. Questi tre attributi costituiscono la chiave
per individuare in maniera univoca un modello nel database di Apromore. Così,
quando il ProcessServiceImpl deve verificare se poter salvare o meno un modello
nel repository (tralasciando eventuali errori di sintassi del modello verificati in
altra sede), fa una semplice richiesta al database con i tre valori passati
dall’utente: id, version e branch. Qualora la tripla non sia presente, il modello è
salvato ed una notifica di salvataggio avvenuto correttamente è visualizzata
all’utente. Di contro, se il modello che si vuole salvare ha la medesima tripla di un
modello già presente nel database di Apromore, all’utente sarà richiesto di
cambiare version o branch (si ricorda che l’id è assegnato in maniera automatica
ad ogni modello).
Dunque, analizzando il riquadro relativo al ProcessServiceImpl in [Figura 94], si
nota che prima il modello è “mappato” su un ProcessModelVersion (l’oggetto che
in Apromore fa le veci del modello rappresentato nell’editor). Tramite esso,
s’inoltra una richiesta al database (entra in gioco Spring grazie alle sue Injection)
per verificare se l’identificativo del modello è già presente ed in caso di risposta
affermativa si passa ad indicizzare il modello grazie al PQLServiceImpl. Si
capisce dunque come tali operazioni debbano essere necessariamente sincrone
dato che bisogna cambiare l’identificativo del modello che si vuole salvare (e
~ 145 ~
dunque o branch o version), l’utente lo deve sapere immediatamente e non quando
magari sta effettuando un'altra operazione. Infatti, qualora il salvataggio del
modello in Apromore fosse effettuato asincronamente, l’utente potrebbe anche
avviare un’operazione di Similarity Search che abbia come obiettivo il modello
appena costruito. Purtroppo però il modello potrebbe ancora non essere presente
nel database, magari perché contiene troppe informazioni da salvare o perché ha
un identificativo già presente nel database. Questa prima comunicazione (editor –
ProcessServiceImpl) sarà sicuramente sincrona e non potrà influire più di tanto sui
tempi in merito al salvataggio dei modelli.
Per quanto riguarda invece l’indicizzazione
di un modello, è possibile adottare un
approccio asincrono per il salvataggio delle
informazioni relative al linguaggio PQL?
Ebbene, prima di accennare ad una risposta,
si può analizzare PQLServiceImpl e le sue
operazioni, per constatare se abbia o meno
elementi importanti ai fini del salvataggio di
un modello. Come si evince dalla [Figura
87], le operazioni compiute dal servizio sono
essenzialmente tre:
1. Transform Model in WF – Model:
traduce il modello che si vuole salvare in una
WF-net per permettere alla libreria jbpt-petri (usata da PQL) di verificarne
la soundness;
2. Check soundness: analizzando la struttura della rete e usando un
algoritmo particolare, si stabilisce se la rete è sound. Se il controllo ha
esito positivo, allora si passa all’ultimo stadio (index model) altrimenti il
modello non è indicizzabile;
Figura 87 – Operazioni svolte dal PQLServiceImpl
~ 146 ~
3. Index Model: le informazioni relative al modello sono salvate sul
database PostgreSQL (ad esempio viene effettuato lo stemming delle label,
vengono salvate le connessioni tra i lavori, ecc.)
Non appena tutti e tre i passi sono effettuati, l’indicizzazione del modello è
conclusa.
Come si è potuto costatare, in nessuna delle precedenti tre operazioni ha la
necessità di operare col database di Apromore (salvo per prelevare il modello che
si vuole indicizzare, ma anche in questo caso si tratta di una “lettura”
d’informazioni già presenti nel database, informazioni peraltro che non saranno
modificate dal processo). Dunque essendo completamente scevro dalla logica di
salvataggio di un modello, l’indicizzazione può essere effettuata in maniera
asincrona rispetto alle precedenti operazioni (disegnare il modello e salvarlo).
Cronologicamente, dovrà anche essere l’ultima operazione che si dovrà effettuare
proprio perché necessita di un modello che è già presente nel database, ma che
non ha nessun vincolo per quanto concerne il tempo di indicizzazione (ovvero il
tempo necessario a salvare tutte le informazioni utili alla libreria PQL).
Ricapitolando, quando un utente crea un modello, quest’ultimo è salvato nel
database di Apromore in modo sincrono, mentre per quanto riguarda
l’indicizzazione, tale operazione è compiuta asincronamente. Naturalmente il
processo di [Figura 94] può essere esteso ai casi di aggiornamento/importazione
di un modello, dato che a cambiare sarà solo la prima parte, mentre le restanti due
saranno praticamente identiche. Attenzione però, salvataggio/aggiornamento e
importazione di modelli pur essendo operazioni simili, differiscono però in un
caso. Infatti nel caso in cui si sceglie se salvare o aggiornare un modello, si lavora
appunto con un solo “file”, nel caso dell’importazione si può avere a che fare con
più modelli contemporaneamente. Questa differenza non cambia il modo in cui un
modello viene indicizzato, ma costituisce un eventuale collo di bottiglia nel
salvataggio delle informazioni per PQL. Altra insidia si nasconde dietro le
tempistiche inerenti all’indicizzazione del modello (basti pensare che verificare la
soundness di modelli molto complessi è un’operazione alquanto lunga è
~ 147 ~
complicata, e di rimando salvare così tante informazioni riguardo un modello
complesso impiega non poco tempo). Inoltre se si pensa che per adesso i modelli
vengono importati ed indicizzati uno per volta, e soprattutto che Apromore è una
piattaforma web virtualmente utilizzabile da centinaia di persone
contemporaneamente, si capisce come risolvere i due problemi esposti in
precedenza sia un fattore determinante per le prestazioni di Apromore.
3.6.1 Multi-threading: indicizzazione più efficiente
Avendo individuato il problema nel precedente paragrafo, si può ora passare ad
analizzare la soluzione. Come già accennato, l’unica parte in cui è possibile
operare riguarda il servizio PQLServiceImpl e le sue operazioni. Inoltre avendo a
che fare con operazioni asincrone, è lecito pensare all’utilizzo di thread che
possano prendere in carico il lavoro di analisi e memorizzazione dei modelli di
processo. Quello che rimane da stabilire però, è il numero di thread adibiti a tale
compito (dato che almeno
uno dovrà servire le
operazioni di salvataggio
del modello nel repository
o comunque altre
operazioni legato ad
Apromore) e soprattutto
dove usarli.
Per quanto riguarda
l’ultimo punto, ovvero
dove usare i thread, la
[Figura 88] può aiutarci.
Come si vede, essa rappresenta il pattern Observer, generalmente utilizzato per
notificare ad una o più classi che l’oggetto che “si sta osservando” è variato. Nel
nostro caso, il ProcessServiceImpl è la classe che è osservata, mentre il
Figura 88 – Class Diagram per lo scambio di informazioni tra ProcessServiceImpl e PQLServiceImpl
~ 148 ~
PQLServiceImpl è la classe che osserva. Grazie a questa implementazione, non
appena un utente salvo/importa/aggiorna un modello, può iniziare l’indicizzazione
dello stesso. Dunque è lecito, e dovuto, supporre che proprio in quest’ultimo
servizio si debba inserire una logica di multiprogrammazione. In [Figure 89] è
riportato il metodo appartenente alla classe PQLServiceImpl, che rientra
nell’ottica del
pattern Observer.
Tramite questo
metodo, ogni volta
che si deve
salvare/aggiornare/i
mportare un
modello, viene
creato un thread che
prende in carico il
lavoro di
indicizzazione del
modello corrente.
Ma è giusto creare
indiscriminatamente così tanti thread? La risposta ovviamente è no, soprattutto
perché i modelli che possono essere elaborati in parallelo, dipendono dai core
posseduti dal server (con un processore ad 8 core, si possono effettuare al
massimo altre 7 operazioni in parallelo, dato che almeno un core è riservato al
flusso principale del programma). Partendo da questa considerazione, è chiaro
dunque come dimensionare il numero di thread affinché il processore riesca
sempre a lavorare al 100% (o quasi). Naturalmente non è pensabile di adottare un
numero fisso nel codice, proprio perché è slegato dalle caratteristiche fisiche del
processore (basti pensare che se in futuro si cambiasse server, il numero di core a
disposizione potrebbe variare. Così facendo si correrebbe il rischio di non
sfruttare appieno la nostra architettura hardware). In base a queste osservazioni,
per poter dimensionare il numero di thread capaci di lavorare in parallelo, si
ricorre al metodo java ( Runtime.getRuntime().availableProcessors() ).
Figura 89 – Metodo di avvio thread
~ 149 ~
Naturalmente, quando si ha a che fare con la multi-programmazione, si deve
prestare attenzione all’ordine con cui finiscono i lavori assegnati ai thread, ma
anche all’ordine in cui tali lavori sono iniziati (non è detto infatti che il lavoro che
inizia prima finisce anche prima nella multi-programmazione). In realtà, il vero
problema non è costituito dall’ordine in cui i lavori sono finiti (infatti il nostro
obiettivo e di indicizzare i modelli nel più breve tempo possibile), ma piuttosto
quello in cui sono iniziati. In altri termini, il modello che cronologicamente è
arrivato prima, deve anche essere sottomesso ad un thread prima (politica First
Input First Output). Questa scelta non crea alcuna differenza per quanto riguarda
la multi-importazione di modelli, ma è importante se si pensa che Apromore può
essere usato da più utenti contemporaneamente. Infatti, nel caso in cui il server sia
oberato di richieste, non è pensabile far aspettare un utente che ha deciso di
salvare il suo modello con largo anticipo rispetto ad altri utenti che invece si
accingono solo ora a farlo. Proprio per queste ragioni si è deciso di usare la classe
Semaphore come fonte di regolamentazione dei thread. Infatti essa non solo
garantisce che solo un numero limitato di processi possa essere sottomesso, ma è
anche in grado di gestire la propria coda di attesa con la politica FIFO.
Un’ultima considerazione prima di dedicarci a qualche statistica, anche se
introducendo i thread (con tutte le soluzioni sopra esposte) si sono ottenuti
sicuramente risultati positivi, c’è un problema subdolo che non è stato trattato.
Come accennato qualche paragrafo fa, alcuni modelli, data la loro natura molto
complessa, richiedono molto tempo per essere valutati e/o indicizzati.
Considerando il caso peggiore possibile, si può avere che i core sono tutti
impegnati a processare modelli la cui indicizzazione richiede delle ore, andando
così a danneggiare gli altri utenti che magari vogliono salvare solo modelli
“semplici”. Onde evitare che possa accadere un blocco così lungo, si è pensato di
affiancare ad ogni thread un timer, in modo tale da garantire che un’indicizzazione
(qualunque sia il suo risultato) finisca entro un determinato timeout.
Naturalmente, qualora questo timeout scada, il modello non verrà indicizzato, o
qualche informazione relativa a quel modello era già stata salvata in qualche
tabella, si provvederà a fare pulizia di queste informazioni. Questa politica però
~ 150 ~
introduce una problematica: cosa accade ai modelli che pur essendo sound, non
vengono indicizzati per motivi di tempo? Ebbene tali modelli non saranno presi in
considerazioni qualora un utente effettui un’interrogazione. Anche se forse può
risultare controproducente, al momento risulta essere l’unica scelta possibile
affinché la maggior parte dei modelli venga indicizzata e resa disponibile per
interrogazioni (al momento il timeout è dimensionato per scadere dopo 1 ora
dall’avvio del thread). Naturalmente in futuro si cercherà di trovare una soluzione
migliore, che sia in grado anche di gestire casi pesanti come quest’ultimo appena
espresso.
Per quanto riguarda le prestazioni avute con l’uso del multi-threading, basti
pensare che su una collezione di 500 modelli (in cui si avevano modelli contenenti
da 10 a 100 lavori circa), i miglioramenti sono stati tangibili. Infatti rispetto
all’approccio sequenziale, ovvero senza thread, che impiegava all’incirca tre ore
nel finire l’indicizzazione dei modelli, si è passati a circa 30-40 minuti della
versione multi-thread. Per completezza di informazione, non tutti i modelli sono
stati indicizzati con successo, a causa di modelli non sound o perché un
particolare modello produceva timeout. Inoltre, con l’introduzione della nuova
versione di LoLA model checker (ovvero il tool adibito alla verifica della
soundness dei modelli), ci si aspettano prestazioni ancora più elevate.
~ 151 ~
Conclusioni
Nella presente tesi si è discusso diffusamente della piattaforma web Apromore,
adibita al salvataggio e all’analisi dei modelli di processo, del linguaggio
d’interrogazione PQL, avente come fine l’innalzamento degli standard di analisi
di modelli, ed infine del modulo software, espressamente sviluppato in questo
lavoro, che consente ad un utente di poter effettuare le proprie interrogazioni
Apromore e PQL.
In particolare, nel primo capitolo si è discusso a lungo sull’architettura di base di
Apromore e sui software che esso utilizza. Si è cosi sottolineato che la struttura a
servizi permette alla piattaforma di potersi espandere secondo le necessità di un
utente senza introdurre particolari complicazioni. Inoltre si è visto come grazie a
Spring, i programmatori hanno “vita facile” per quanto riguarda
l’implementazione e la gestione dei servizi e dei Java beans. Per quanto riguarda
la grafica adottata, grazie al framework Zk si è riusciti ad evitare
l’implementazione a mano di molti componenti (la maggior parte dei componenti
usati nell’interfaccia grafica sono stati solo parzialmente modificati, ma per il
resto sono componenti disponibili sul web tramite tutorial o sul sito ufficiale di
Zk). In ultimo, si è visto come legare le diverse librerie utilizzate nei vari moduli
di Apromore, attraverso il progetto Apache Maven (si sono discussi i goal e alcuni
dei comandi di base).
Per quanto riguarda il secondo capitolo, si è introdotto il linguaggio PQL e le sue
caratteristiche, la sintassi che, come è risultato evidente, è molto simile al
linguaggio SQL (in modo che un utente qualsiasi non abbia troppe difficoltà ad
usarlo, anche se alle prime armi con linguaggi d’interrogazione). L’attenzione è
stata posta anche sulla sua caratteristica principale, ovvero la capacità di
riconoscere le etichette simili, grazie alla quale si è in grado di analizzare con
maggior dettagli i vari modelli di processo. Infine si è mostrato alcuni esempi di
come tale linguaggio analizzi un modello spiegando man mano alcune delle sue
parole chiavi (AlwaysOccur, CanOccur, ecc).
~ 152 ~
Infine si è proposta la progettazione e l’implementazione del modulo software che
ospita il linguaggio PQL e permette all’utente di interrogare il repository
Apromore. In particolare si è discussa l’architettura e le relative nonché le
problematiche di integrazione tra i vari strumenti utilizzati. L’attenzione è stata
rivolta sulle principali funzionalità che un utente vorrebbe in un software
d’interrogazione come: evidenziazione degli errori (affinché saltino
immediatamente all’occhio dell’utente), metodi per suggerire parole chiavi o nomi
di modelli, possibilità di trascinare i modelli che si vogliono interrogare
direttamente dall’albero di Apromore, possibilità di espandere/comprimere la
query per motivi di spazio, ecc. Si è analizzato per ogni soluzione citata vantaggi
e svantaggi, motivando ampiamente per ognuna, il perché di quella scelta. Infine
si è spiegato cosa ha comportato l’integrazione del modulo sviluppato all’interno
del progetto Apromore, nonché come alcune delle librerie usate siano state
convertite in OSGi per eliminare alcuni problemi.
Il lavoro sviluppato, che allo stato attuale rappresenta uno strumento efficace e
robusto per supportare query PQL in Apromore, sarà ulteriormente ampliato sia
nella componentistica grafica ma soprattutto per quanto riguarda le prestazioni,
considerando sempre come obiettivo principale la facilità d’uso del modulo
sviluppato (in modo che l’utente ne possa beneficiare appieno).
~ 153 ~
Bibliografia
[1] C. Walls, “Spring in Action, 3th Edition”, Manning Pubblication Co., 2011.
[2] ZK: The Developer’s Guide, http://www.zkoss.org/doc/devguide/, consultato
ad Ottobre 2014
[3] M. Stäuble, H.J. Schumacher, “ZK Developer’s Guide”, Packt Publishing,
Marzo 2008.
[4] IoC Container, http://www.html.it/pag/18719/ioc-container/, consultato ad
Ottobre 2014
[5] Inversion of Control Containers and the Dependency Injection pattern,
http://martinfowler.com/articles/injection.html, consultato ad Ottobre 2014
[6] ZK Documentation,
http://books.zkoss.org/wiki/ZK_Developer's_Guide/Fundamental_ZK/Basic_Con
cepts/Architecture_Overview, consultato ad Ottobre 2014
[7]“ZK vs JSF based Solutions Performance Report”,
http://www.zkoss.org/whyzk/zk_vs_jsf, consultato ad Ottobre 2014
[8] T. O’Brien, J. Casey, B. Fox, B. Snyder, J. Van Zyl, E. Redmond, “Maven:
The Definitive Guide”, O'Reilly Media
[9] A. H. M. ter Hofstede, C. Ouyang, M. La Rosa, L. Song, J. Wang, A,
Polyvyanyy, “APQL: A Process-Model Query Language”. Lecture Notes in
Business Information Processing, 159, pp. 23-38; 2013
[10] A. Polyvyanyy, A. H. M. ter Hofstede, M. La Rosa, C. Ouyang, J. Recker,
“Process Query Language: Design, Implementation and Evaluation”, report QUT
2014
[11] M. La Rosa, H.A. Reijers, W. M. P. van der Aalst, R. M. Dijkman, J.
Mendling, M. Dumas, L. Garcìa-Bueñelos, “APROMORE: An Advanced Process
Model Repository.” Expert Systems with Applications, Vol. 38 Issue 6, 2011.”
[12] Apromore, http://Apromore.org/, consultato ad Ottobre 2014
[13] Apromore Canonical format, http://Apromore.org/platform/canonical-format,
consultato ad Ottobre 2014
~ 154 ~
[14] EL Expressions,
http://books.zkoss.org/wiki/ZK_Developer%27s_Reference/UI_Composing/ZUM
L/EL_Expressions, consultato ad Ottobre 2014
[15] Zk ID Space,
http://books.zkoss.org/wiki/ZK_Developer's_Reference/UI_Composing/ID_Space
, consultato ad Ottobre 2014
[16] M. Dumas, M. La Rosa, J. Mending, H. A. Reijers, Fundamentals of
Business Process Management, Springer, 2013
[17] Apromore Architecture, http://Apromore.org/platform/architecture consultato
ad Ottobre 2014
[18] Maven Directories,
http://www2.mokabyte.it/cms/article.run?articleId=GMH-MCQ-RBI-
MBI_7f000001_28844331_dc5e1dbf consultato ad Ottobre 2014
[19] Maven, Avaible Plugins, http://maven.apache.org/plugins/ consultato ad
Ottobre 2014
[20] Maven, POM Reference, http://maven.apache.org/pom.html consultato ad
Ottobre 2014
[21] Maven, Introduction to build lifecycle,
http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
consultato ad Ottobre 2014
[22] R. Monson-Haefel, “Enterprise JavaBeans, Sviluppo di componenti Java
enterprise, 4 edizione”, O’Reilly, 2005
[23] L’architettura di Spring, http://www.html.it/pag/18715/larchitettura-di-
spring/ consultato ad Ottobre 2014
top related