POLITECNICO DI BARI
FACOLTÀ DI INGEGNERIA
CORSO DI LAUREA IN INGEGNERIA INFORMATICA
DIPARTIMENTO DI ELETTROTECNICA ED ELETTRONICA
TESI DI LAUREAIN
SISTEMI INFORMATIVI
Un mercato elettronico di conoscenza basato su HR-XML Staffing Exchange
Protocol
Relatore:
Chiar.mo Prof. Ing. E. Di Sciascio
Correlatore:
Ing. S. Colucci
Laureando:
Andrea Martelli
ANNO ACCADEMICO 2006– 2007
Ai miei genitori
Indice
1 Introduzione................................................................1
2 Description Logics e OWL.........................................6
3 Staffing Exchange Protocol......................................29
4 L'interfaccia grafica..................................................51
5 Struttura e funzioni del server...................................76
6 Comportamento del sistema.....................................98
7 Conclusioni.............................................................107
Appendice.......................................................................111
1 Introduzione
La gestione delle competenze (Skill Management) rientra nel campo più
generale ed esteso della gestione della conoscenza (Knowledge Management), un
campo che ha recentemente guadagnato attenzione sia in ambito accademico che
industriale, in particolare presso le organizzazioni ad alta “intensità di conoscenza”,
come società di consulenza o di collocamento. Il Knowledge Management mira a
migliorare l’utilizzazione della conoscenza posseduta dalla forza lavoro, cercando di
assegnare la giusta persona al giusto compito con il minimo dispendio di tempo e,
soprattutto, svincolandosi da criteri soggettivi, rendendo più simile la gestione delle
risorse umane a una scienza esatta. Le competenze della forza lavoro infatti, sono da
tempo riconosciute come un aspetto strategico di primaria importanza nella gestione
aziendale, e sono viste come un efficace mezzo per il raggiungimento, da parte
dell’organizzazione, di un vantaggio competitivo.
Alcuni studi mostrano come la spesa per il miglioramento e l’arricchimento dei sistemi
di gestione della conoscenza, e in particolare per quelli dotati di componenti per la
gestione delle competenze (Skill Management Systems, SMS), usati dalle compagnie,
sia largamente compensata dai vantaggi che ne derivano.
La creazione e il mantenimento delle conoscenze, quindi, è una delle attività più
importanti in un’organizzazione. Un sistema di gestione delle competenze dovrebbe
fornire diverse opportunità: quando si presenterà un compito da svolgere, innanzitutto
un’organizzazione cercherà le competenze richieste all’interno del proprio personale, e
1
questo è un compito la cui complessità cresce al crescere delle dimensioni
dell’organizzazione stessa. Se il processo di ricerca dovesse evidenziare la mancanza
delle competenze richieste, l’azienda potrebbe intraprendere due strade alternative:
ricorrere a personale esterno, o incoraggiare il personale interno ad acquisire nuove
competenze, soluzione di facile realizzazione vista la grande disponibilità di moduli di
e-learning.
Il sistema sviluppato in questa tesi si occupa della ricerca di forza-lavoro.
Pertanto potrebbe in futuro essere esteso o integrato in un sistema più ampio in modo da
sfruttare i risultati già ottenuti per formulare automaticamente percorsi di e-learning.
L’obiettivo è quindi la creazione di un portale web bilaterale, dedicato cioè sia
ad aziende che a personale qualificato in cerca di lavoro, con lo scopo finale di trovare il
miglior candidato (tra quelli disponibili in un database) allo svolgimento di un compito
stabilito dall’azienda, dove sia le competenze posseduta dall’individuo che quelle
richieste dall’azienda sono descritte semanticamente. Il processo di selezione si svolge
in due fasi: da una scrematura iniziale coi metodi tradizionali dei database, si passa a
una selezione basata sull’inferenza.
La prima fase del lavoro, quindi, è stata incentrata sullo studio della teoria che
sta alla base delle Logiche Descrittive e del linguaggio OWL, necessari alla
formulazione della descrizione dei profili individuali e dei task, richiesta dall'inferenza.
In particolare si sono studiati gli algoritmi di inferenza non standard implementati dal
gruppo di ricerca del Laboratorio di Sistemi Informativi del Politecnico di Bari.
2
Successivamente è stato approfondito lo “Staffing Exchange Protocol” − e nel
dettaglio la parte denominata “Position Matching Type” − uno standard messo a punto
dal consorzio HR-XML, un’organizzazione indipendente e no-profit dedicata allo
sviluppo e alla promozione di un set di specifiche XML miranti ad automatizzare i
processi di e-business e lo scambio tra compagnie di informazioni concernenti le risorse
umane. Il Position Matching Type può essere visto quindi come un formato comune di
rappresentazione delle informazioni relative alla domanda e all’offerta di forza lavoro.
Superate queste fasi preliminari, l'attenzione si è spostata sull’apprendimento e
l’uso delle librerie del GWT per realizzare l’interfaccia grafica in AJAX (Asynchronous
JavaScript and XML). Questa, oltre a comprendere i campi previsti dallo standard HR-
XML, è stata dotata di una sezione aggiuntiva per la creazione della descrizione
semantica del profilo individuale e del compito richiesto.
Una delle parti fondamentali del progetto, poi , è stata la realizzazione di un
sistema di Servlets Java per lo scambio di dati tra client e server. Con questo approccio
si è implementato il reperimento in formato xml di informazioni contenute nelle
ontologie e utilizzate dinamicamente per la creazione di parti di interfaccia, il
caricamento dei curriculum in formato testo sul server, e il collegamento al database
relazionale.
Il lavoro ha raggiunto la sua conclusione e compiutezza nell’ultima fase, nella
quale si è realizzato il collegamento al ragionatore MaMaS e l’implementazione
dell’algoritmo di match uno-a-uno, che restituisce infine i dati del miglior candidato
trovato nel database.
3
Nel caso di studio oggetto di questa tesi, il linguaggio OWL verrà utilizzato per
modellare il dominio delle compentenze, con la possibilità di descrivere sia quelle
possedute da un individuo, sia quelle richieste da un'azienda in cerca di forza-lavoro.
OWL [8] è infatti un linguaggio tramite il quale è possibile scrivere delle ontologie che
descrivono la conoscenza che abbiamo di un certo dominio. OWL supporta l'iniziativa
del Web semantico, che è un’estensione in continua evoluzione del world wide web,
nella quale i contenuti possono essere espressi non solo in linguaggio naturale, ma
anche in una forma che può essere capita, interpretata ed elaborata dai calcolatori,
consentendo a questi di cercare, condividere e integrare informazioni più facilmente, in
un sistema di conoscenza distribuito. Questa visione è in gran parte dovuta alle idee
diffuse dal direttore del W3C (World Wide Web Consortium), Tim Berners-Lee, il quale
immagina il web come un mezzo universale per lo scambio di informazioni e
conoscenza. Si tende, sostanzialmente, ad abbandonare la classificazione degli elementi
in base a parole chiave pure e semplici, ma si cerca di fornire una “spiegazione” dei
concetti comprensibile anche alle macchine.
Per lo sviluppo del progetto ho utilizzato diversi strumenti software, tutti con
licenza GPL (Gnu Public License) eccetto un plugin commerciale per l’IDE, e tutti
eseguiti in ambiente Ubuntu Linux, anche se nella fase di test dell’applicazione ho
utilizzato anche Microsoft Windows® per verificare la compatibilità. La
programmazione in Java è stata possibile appoggiandosi al Java Development Kit
(JDK) e al Java Runtime Environment (JRE) della Sun, e utilizzando come tool di
sviluppo l’IDE Eclipse, open source e ideato dalla Eclipse Foundation, un consorzio di
4
grandi società del settore. In accoppiata ad Eclipse, ho utilizzato il plugin GWT
Designer della Instantiations, utile per velocizzare lo sviluppo e il debug dell’interfaccia
grafica.
I principali strumenti software aggiuntivi utilizzati sono stati:
1. Google Web Toolkit - per la creazione dell’interfaccia web;
2. JDOM - per la creazione e il parsing di documenti XML;
3. Commons-FileUpload e Commons-I/O del Jakarta Project - per la gestione dei
files sul server;
4. MySQL Connector/J – per il collegamento a database;
5. Jena di HP - per la gestione delle ontologie;
6. Dig4J del SisInfLab - per il collegamento al ragionatore MaMaS;
1.1: Schema generale del sistema
5
2 Description Logics & OWL
In questo capitolo verranno introdotti i principi base delle logiche descrittive
(DL) e del linguaggio OWL, nell'ambito di un discorso più ampio sulla
rappresentazione della conoscenza, e i loro possibili impieghi nella risoluzione di
problemi di inferenza standard e non standard. La relazione tra DL e OWL verrà inoltre
introdotta.
2.1 La rappresentazione della conoscenza
La rappresentazione della conoscenza è un problema che emerge sia nella scienza
cognitiva che nell'intelligenza artificiale. Nella scienza cognitiva, essa si interessa di
come le persone acquisiscono ed elaborano informazioni. Nell'intelligenza artificiale
l'obiettivo principale è memorizzare conoscenza in modo che dei software possano
elaborarla simulando verosimilmente il comportamento del cervello umano. Proprio per
questo i ricercatori hanno fatto riferimento a teorie già in uso nella scienza cognitiva.
Dal momento che la conoscenza è usata per ottenere dei comportamenti intelligenti,
l'obiettivo fondamentale della rappresentazione della conoscenza è rappresentare le
informazioni in modo da facilitare il ragionamento, ovvero di trarre conclusioni da delle
premesse. Alcune questioni centrali in questo ambito, dal punto di vista dell' I.A., sono:
Come le persone rappresentano la conoscenza?
Qual'è la natura della conoscenza e come la rappresentiamo?
6
Uno schema di rappresentazione deve ridursi a un particolare dominio o
dovrebbe essere di utilizzo generale?
Possiamo dire che la risoluzione di un problema dipenderà strettamente dalla scelta che
facciamo in termini di rappresentazione della conoscenza, e che quindi non esiste
nessuna rappresentazione che possa rendere ogni problema ugualmente risolubile.
Tra i vari approcci che nel tempo sono stati proposti, quello delle “reti
semantiche” ha ottenuto un discreto successo, anche sotto la spinta, come detto in
precedenza, del W3C. Una rete semantica consiste in un grafo in cui ogni nodo
rappresenta un concetto, mentre gli archi sono usati per definire relazioni tra concetti.
Nonostante questo tipo di approccio sia ormai superato, è bene ricordare che alcune
delle sue astrazioni hanno costituito una spinta allo sviluppo delle logiche descrittive e
dei linguaggi OWL e XML.
2.2 Logiche descrittive (Description Logics)
Le logiche descrittive [11] sono una famiglia di formalismi utilizzati per rappresentare
la conoscenza in un dominio di applicazione detto “mondo”. In primo luogo sono
definiti i concetti rilevanti per quel dominio e, di seguito, utilizzando questi concetti si
specificano le proprietà degli oggetti e degli individui appartenenti al dominio. Gli studi
riguardanti le logiche descrittive si focalizzano sulla ricerca di metodi che descrivano in
modo sempre più specifico il dominio di interesse tale da essere utilizzati per costruire
applicazioni intelligenti. In questo contesto il termine intelligente è riferito all'abilità di
7
un sistema di trovare delle conseguenze implicite rispetto a quelle esplicite
rappresentate dalla conoscenza. Sistemi così caratterizzati sono definiti sistemi basati
sulla conoscenza (knowledge based systems).
Gli studi sulla rappresentazione della conoscenza iniziarono negli anni 70' e
hanno percorso varie strade, concentrandosi inizialmente sulla creazione si sistemi
terminologici per poi spostarsi sui costrutti ammessi dai linguaggi, e più recentemente i
risultati ottenuti sono stati utilizzati per lo sviluppo di linguaggi per la rappresentazione
come RDF (Resource Description Framework) e lo stesso OWL, che ne è un'estensione.
A seconda delle possibilità e delle restrizioni imposte nell'utilizzo delle logiche
descrittive, queste sono caratterizzate da una particolare capacità espressiva che può
essere indicata utilizzando una notazione letterale standard:
: indica la logica degli attributi e introduce gli operatori di congiunzione e le restrizioni universali ed esistenziali non qualificate;
C: descrive la possibilità di usare l'operatore di negazione;
S : estende la DL ALC con l'ulteriore possibilità di definire la chiusura transitiva di un ruolo;
H: fornisce la possibilità di definire gerarchie tra ruoli;
O: asserisce la presenza dell'operatore di enumerazione;
I : permette di riferirsi al ruolo inverso;
F , N e Q caratterizzano le possibilità di definire cardinalità rispettivamente funzionale, semplice e qualificata (in ordine di espressività crescente);
D descrive la possibilità di riferirsi a domini concreti.
Andando più nello specifico, gli elementi di base della logica descrittiva sono
concetti e ruoli. Per concetti si intendono degli insiemi di oggetti, per esempio
Laureato, IngegnereInformatico o CorsoDiInglese, mentre delle regole
8
AL
possono essere per esempio laureatoIn, esperienza, livelloDiConoscenza,
e legano oggetti di differenti concetti. Tutte le logiche descrittive sono dotate
dell'operatore di congiunzione denotato dal simbolo u, mentre solo alcune
comprendono i simboli di disgiunzione t e complemento :, con significati simili agli
operatori booleani. Le regole possono essere combinate con i concetti usando il
quantificatore esistenziale 9 (per esempio Laureatou9haAbilita.Negoziazione,
che descrive l'insieme delle persone laureate aventi abilità di negoziazione) e il
quantificatore universale 8 (per esempio Programmatoreu8
haLaurea.Ingegneria, che individua i programatori aventi solo una laurea in
ingegneria.
Altri costrutti possono essere restrizioni numeriche: Laureatou(·3
haAbilità) esprime i laureati aventi almeno tre abilità, e Manageru(¸2
hasTechnicalSkills) descrive i manager che possiedono almeno due abilità di
carattere tecnico.
La rappresentazione della conoscenza nel formalismo DL è realizzata mediante
dichiarazioni di inclusione e definizioni. Per esempio potremmo imporre che la
programmazione sia partizionata in strutturata e orientata agli oggetti, usando due
inclusioni: Programmazione ProgrammazioneStrutturata
ProgrammazioneAdOggetti e ProgrammazioneStrutturata
ProgrammazioneAdOggetti. Oppure potremmo stabilire che dei team di lavoro debbano
essere composti da almeno due membri Teamu(¸2 haMembri). Queste inclusioni
sono denominate Tbox (Terminological Box).
9
´ u
:
Il linguaggio che useremo è di tipo (Attributive Language with
unqualified Number rescrictions), che costituisce un buon compromesso tra espressività
e complessità computazionale degli algoritmi di ragionamento. I costrutti permessi in
una logica descrittiva di questo tipo sono:
top. Tutti gli oggetti nel dominio.
bottom. L'insieme vuoto.
A concetti atomici. Tutti gli oggetti appartenenti all'insieme indicato con A.
A negazione atomica. Tutti gli oggetti non appartenenti all'insieme A.
C D intersezione. Gli oggetti appartenenti sia a C che a D.
R.C restrizione universale. Gli oggetti che partecipano alla relazione R il cui
range è costituito dagli oggetti appartenenti alla classe C.
R restrizione esistenziale non qualificata. Esiste almeno un oggetto
partecipante alla relazione R.
( n R), ( n R), (= n R). Il massimo, minimo ed esatto numero di oggetti
partecipanti alla relazione R.
I problemi di ragionamento basilari per concetti espressi in DL sono la
“soddisfacibilità”, che è legata alla coerenza interna della descrizione – il che equivale
a dire che non ci sono proprietà in contraddizione tra loro – e la “sussunzione”
(subsumption) che tiene conto delle relazioni di maggiore o minore specificità tra
concetti, che costituiscono le basi della tassonomia. Le definizioni formali in DL sono
[1]:
10
ALN
>
?
:
u
8
9
· ¸
Definizione 1 Sussunzione. Data una DL , siano P e T due concetti in , e la
TBox sia un set di assiomi in . Un concetto T sussume un concetto P in
relazione a se ogni interpretazione di assegna a T un sottoinsieme
dell'insieme assegnato a P. Scriviamo quindi che P T per indicare che T
sussume P in relazione a .
Come esempio ipotizziamo che sia l'ontologia del dominio delle skill, e T e P siano
due concetti rappresentanti rispettivamente i requisiti per un lavoro da svolgere e le
abilità ricavate da un curriculum vitae, o più in generale un fornitore di conoscenza. Se
P T significa che le informazioni rappresentate da P sono più specifiche di quelle
rappresentate da T, e quindi il candidato ha tutte le capacità richieste per eseguire il
lavoro in questione e in questo caso si parla di “full match”, che è il miglior tipo di
match.
Definizione 2 Soddisfacibilità. Data una DL , un concetto C espresso in , e
un set di assiomi in , C è soddisfacibile nei riguardi di se esiste almeno
un modello di che assegni un'estensione non vuota a C. Dal momento che un
concetto C è soddisfacibile in se e solo se C non è sussunto da , scriviamo
che C per indicare che C è soddisfacibile in .
Nello scenario dell'esempio precedente, se P (rispettivamente T )
allora la descrizione P (rispettivamente T) è in conflitto con le informazioni modellate
11
T j= v ? T j= v ?
L
T L
T T
T j= v
T
T
T j= v
L L
T L T
T
T ?
T j= 6v? T
in . Ovvero, la richiesta (il curriculum) è contraddittoria. Allo stesso modo, se C=P
T, con P e T soddisfacibili in , la non soddisfacibilità di C può essere letta come
incompatibilità tra P e T.
2.3 Prerequisito: XML
Dal momento che OWL ed RDF nascono grazie ad XML [5] e ne costituiscono
un'evoluzione, prima di approfondire questi linguaggi, è necessario studiare XML
almeno nei tratti generali per comprenderne la logica e la struttura.
L'XML, acronimo di eXtensible Markup Language, ovvero “linguaggio di
marcatura estensibile”, è un metalinguaggio6 creato e gestito dal W3C. E' un
adattamento dell'SGML, da cui è nato nel 1988, e permette di definire la grammatica di
linguaggi derivati. A differenza dell'HTML, del quale potrebbe ricordare la sintassi, il
suo ambito di applicazione non è l'esposizione di informazioni, ma lo scambio dati tra
entità. E' quindi un linguaggio di back-office e non di front-office.
La struttura vera e propria dell'XML è composta dai tag creati dallo
sviluppatore, che hanno due caratteristiche:
• Devono essere comprensibili in funzione dello scopo del tag stesso, anche per
facilitare la comprensione agli altri utenti,
6 Per metalinguaggio si intende un linguaggio attraverso il quale formuliamo definizioni che si applicano al “linguaggio oggetto”, che nel nostro caso è il linguaggio naturale descrivente il dominio delle competenze.
12
T u
T
• devono rispettare delle regole: non possono iniziare con caratteri speciali o
numeri, contenere spazi e devono rispettare la differenza tra maiuscole e
minuscole. Queste regole sono simili a quelle di assegnazione delle variabili nei
comuni linguaggi di programmazione.
Ogni elemento viene chiamato nodo e ogni tag può essere accompagnato da degli
attributi. La struttura che questi nodi formano può essere vista concettualmente come un
database o come un albero, a seconda degli scopi. Ogni nodo può contenere altri
elementi, siano essi valori numerici, testuali, o nodi a loro volta. I tag che non hanno
l'omonimo tag di chiusura vanno chiusi con uno slash (/) finale. Per poter essere
correttamente interpretati dai browser, i documenti XML devono essere “ben formati”,
ovvero possedere le seguenti caratteristiche:
• Il Prologo, che è la prima istruzione presente, e specifica la versione dello
standard XML e la codifica ISO (<?xml version="1.0" encoding="UTF-8"?>);
• un unico elemento radice, ovvero il nodo principale che contiene tutti gli altri;
• tutti i tag devono esser chiusi. Ad esempio <html> va chiuso con <html/>.
Numerose software house hanno prodotto librerie apposite o aggiunto caratteristiche ai
propri linguaggi di programmazione, per supportare la creazione e il parsing di
documenti xml.
Di seguito vediamo un piccolissimo documento xml esemplificativo:
13
<?xml version="1.0" encoding="ISO-8859-1"?><utenti> <utente nome="Luca" cognome="Ruggiero" /> <utente nome="Max" cognome="Rossi" /></utenti>
2.4 Panoramica sul linguaggio OWL
OWL (Ontology Web Language) [8] è un linguaggio per definire e instanziare
ontologie. Un'ontologia OWL può comprendere sia la descrizione di classi che la
definizione delle loro proprietà e delle loro instanze, ed è progettato per essere usato in
applicazioni che necessitano di elaborare il contenuto delle informazioni invece di
presentare le informazioni stesse agli esseri umani. Inoltre mira a permettere una
maggiore interpretabilità del contenuto web rispetto a quella supportata da formati dati
come XML, RDF e RDF Schema, per mezzo principalmente di un vocabolario più
ampio. OWL è basato sui linguaggi precedenti OIL e DAML+OIL, ed è attualmente
supportato dal W3C.
OWL è visto come una delle tecnologie principali per l'implementazione futura
del Web semantico. Gioca un importante ruolo in un numero sempre crescente di
applicazioni, con un ambito di applicazione sempre più largo, e sta spingendo lo studio
di nuovi strumenti, tecniche di ragionamento ed estensioni del linguaggio.
Inizialmente fu realizzato per fornire un metodo comune per processare il
contenuto semantico delle informazioni presenti nel web e per aumentare la facilità
nell'espressione semantica precedentemente offerta da XML ed RDF.
Conseguentemente, è da considerarsi un'evoluzione di questi linguaggi.
Dal momento che OWL è basato su XML, le informazioni codificate in OWL
possono essere facilmente scambiate tra tipi diversi di computer utilizzando diversi
sistemi operativi e applicazioni scritte in linguaggi anche molto diversi fra loro. Dato
14
che è mirato all'elaborazione automatica, spesso viene considerato non facilmente
leggibile dall'utente umano, ma questo è un problema relativo e risolvibile con lo
sviluppo di semplici appositi tools di editing.
2.4.1 Sottolinguaggi
OWL fornisce tre sottolinguaggi con livello crescente di espressività e destinati a
specifiche aree di applicazione:
• OWL Lite: utile agli utenti che hanno bisogno di utilizzare classificazioni
gerarchiche e semplici restrizioni. Per esempio, pur supportando le restrizioni
numeriche, permette di usare valori pari soltanto a 0 o ad 1. Dovrebbe essere più
facile produrre strumenti software per Owl lite che per i suoi parenti più
espressivi.
• OWL DL: è un compromesso mirante a conservare la massima espressività
possibile pur mantenendo accettabili completezza e complessità computazionale
e decidibilità. Include tutti i costrutti del linguaggio OWL, ma questi possono
essere usati entro certi limiti come ad esempio la separazione dei tipo (una classe
non può essere anche un individuo o una proprietà, e una proprietà non può
anche essere un individuo o una classe). E' chiamato così per il suo legame con
gli studi relativi alle logiche descrittive.
• OWL Full: è dotato delle massime espressività e libertà sintattica possibili con
RDF, ma non offre garanzie computazionali. Ad esempio, una classe può essere
15
trattata contemporaneamente sia come una collezione di individui, che come un
individuo stesso. E' improbabile che si riescano a realizzare ragionatori software
capaci di supportare il ragionamento completo su ogni caratteristica di OWL
Full.
Ognuno di questi linguaggi è un'estensione del suo predecessore più semplice, sia per
quanto riguarda quello che può essere legalmente espresso, sia per le conclusioni valide
che possono esserne tratte. Sono valide le seguenti relazioni:
• Ogni ontologia valida in OWL Lite è valida anche in OWL DL;
• Ogni ontologia valida in OWL DL è valida anche in OWL Full;
• Ogni valida conclusione in OWL Lite è valida anche in OWL DL;
• Ogni valida conclusione in OWL DL è valida anche in OWL Full;
2.4.2 Sintassi
La sintassi di OWL, basata su XML ed RDF, prevede un numero abbastanza elevato di
costrutti, che possono essere sinteticamente raggruppati nelle seguenti categorie:
• Caratteristiche dello Schema RDF: Class: (Thing, Nothing), rdfs:subClassOf,
rdf:Property, rdfs:subPropertyOf, rdfs:domain, rdfs:range, Individual;
• Eguaglianze: equivalentClass, equivalentProperty, sameAs, differentFrom,
AllDifferent, distinctMembers;
16
• Caratteristiche delle proprietà: ObjectProperty, DatatypeProperty, inverseOf,
TransitiveProperty, SymmetricProperty, FunctionalProperty;
• Restrizioni sulle proprietà: Restriction, onProperty, allValuesFrom,
someValuesFrom
• Restrizioni sulla cardinalità: minCardinality, maxCardinality, cardinality;
• Informazioni sull'intestazione: Ontology, imports;
• Intersezione di classi: intersectionOf;
• Versioni: versionInfo, priorVersion, incompatibleWith, DeprecatedClass,...;
• Proprietà delle annotazioni: rdfs:label, rdfs:comment, rdfs:isDefinedBy,
rdfs:seeAlso, AnnotationProperty, OntologyProperty;
Tuttavia, quelle che per i nostri scopi assumono maggior rilievo sono quelle che
possono essere messe in relazione con la sintassi di Description Logic. Nella tabella
seguente possiamo osservare le corrispondenze tra i tag owl e il formalismo DL:
Sintassi OWL Sintassi DL
< owl: Thing />
< owl: Nothing />
< owl: Class rdf:about=”C” /> C< owl: ObjectProperty rdf:about=”R” /> R
< rdfs: subClassOf />
< owl: equivalentClass />
< owl: disjointWith />
< owl: intersectionOf />
<owl: allValuesFrom />
<owl: someValuesFrom />
17
>?
v´:u89
< owl: maxCardinality />
< owl: minCardinality />
<owl: cardinality />
Tabella 1.2: Corrispondenze tra la sintassi di OWL-DL e di un DL
Questo sottoinsieme dei tag di OWL consente di esprimersi in logica . Vediamo, a
titolo di esempio, un segmento di codice owl corrispondente alla descrizione di un
individuo nel dominio delle competenze:
<rdf:Description rdf:about="http://sisinflab.poliba.it/unnamed.owl#jack"> <rdf:type> <owl:Class> <owl:intersectionOf rdf:parseType="Collection"> <owl:Class rdf:about="http://sisinflab.poliba.it/unnamed.owl#AssetManager"/> <owl:Restriction> <owl:onProperty rdf:resource="http://sisinflab.poliba.it/unnamed.owl#advanced_knowledge"/> <owl:allValuesFrom> <owl:Class> <owl:intersectionOf rdf:parseType="Collection"> <owl:Class rdf:about="http://sisinflab.poliba.it/unnamed.owl#Programming"/> <owl:Class rdf:about="http://sisinflab.poliba.it/unnamed.owl#Computer_Graphics"/> </owl:intersectionOf> </owl:Class> </owl:allValuesFrom> </owl:Restriction> <owl:Restriction> <owl:onProperty rdf:resource="http://sisinflab.poliba.it/unnamed.owl#advanced_knowledge"/> <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int" >1</owl:minCardinality> </owl:Restriction> <owl:Restriction> <owl:onProperty rdf:resource="http://sisinflab.poliba.it/unnamed.owl#has_industry"/> <owl:allValuesFrom rdf:resource="http://sisinflab.poliba.it/unnamed.owl#Computer_Software"/> </owl:Restriction> </owl:intersectionOf> </owl:Class> </rdf:type> </rdf:Description>
18
·¸=
ALN
ALN
Per quanto a prima vista possa sembrare ostico, leggendolo si evince che Jack è un
assett manager con conoscenza avanzata nei campi della programmazione e della
computer grafica e che opera nel campo del software.
2.5 Servizi di inferenza non-standard
Riprendendo il discorso sulla Logica Descrittiva [11], è opportuno osservare che
sia la soddisfacibilità che la sussunzione forniscono risposte booleane, nel senso che
restituiscono una risposta di tipo vero/falso. Per i nostri scopi, invece, è necessario
prevedere sia una spiegazione che una possibilità di rivedere i requisiti (belief revision),
per poter gestire in maniera ottimale anche casi in cui non c'è perfetta corrispondenza
tra domanda e offerta. La “belief revision” è un processo che ci porta a ritrattare le
richieste iniziali sulla base di nuove informazioni che ci pervengono. Così, se P T non
è soddisfacibile nell'ontologia – per esempio, P e T sono incompatibili tra loro –
potremmo ritrattare alcuni requisiti G (Give Up) in T, per ottenere una nuova richiesta
contratta K (Keep) compatibile con P, per giungere a una situazione in cui P K
soddisfacibile in . Di seguito si illustrano le definizioni di due servizi di inferenza
non-standard che useremo per prevedere le spiegazioni e i suggerimenti di belief
revision [2].
Definizione 3 Concept Contraction. Sia una DL, P e T due concetti in , e
un set di assiomi in , dove sia P che T sono soddisfacibili in . Un problema
19
u
T
u
T
L L T
L T
di Concept Contraction (CCP), identificato con , T, P, , consiste nel trovare
una coppia di concetti G, K tali che T G K, e che K P sia
soddisfacibile in . Chiamiamo K una contrazione di T secondo P e .
Ovviamente un problema di Concept Contraction ammette sempre la soluzione banale
G, K = , che corrisponde a rinunciare interamente a T. Nel nostro sistema di
gestione delle competenze, questo rispecchia la situazione nella quale, a fronte di alcuni
profili P molto interessanti, incompatibili con la richiesta, il reclutatore rinuncia
completamente alle sue richieste iniziali T in modo da incontrare l'offerta. Dualmente,
quando è soddisfacibile in , la migliore soluzione possibile è che
corrisponde a non rinunciare a niente. E' scontato che in una situazione reale si cercherà
in ogni caso di rinunciare a meno caratteristiche possibili, e sarà quindi necessario
scegliere in maniera sensata a quali rinunciare e a quali no.
Quando il processo di contrazione non porta comunque a una corrispondenza
ottimale tra domanda e offerta (full match), può essere interessante cercare una
spiegazione del perché ciò non avvenga. In altre parole, si può voler sapere quali sono le
caratteristiche mancanti del candidato che lo rendono inadeguato all'assolvimento del
compito richiesto, ovvero le competenze che dovrebbe acquisire per diventare idoneo.
Questo risultato potrebbe essere usato, sfruttando un Learning Management System
(LMS), per creare automaticamente un percorso di apprendimento mirato che porti il
lavoratore ad acquisire le conoscenze mancanti. La 'spiegazione' della mancata
corrispondenza si ottiene risolvendo un problema di “Concept Abduction”.
20
hL T i
h i 2 L£ L T j= ´ u u
T T
h i hT;>i
P u T T h>; T i
• Definizione 4 Concept Abduction Siano S e D due concetti espressi in una
logica descrittiva , e sia un set di assiomi, con S e D soddisfacibili in . Un
problema di Concept Abduction (CAP), denotato da , consiste nel
trovare un concetto H tale che , e che .
La soluzione a un problema di Concept Abduction può essere interpretata come quello
che dovrebbe essere aggiunto a P per renderlo più specifico di T, rendendo valida la
relazione di sussunzione tra i due. Se un curriculum è compatibile con un dato compito
lavorativo – – ma il secondo non è completamente soddisfatto dal
primo, sarebbe interessante sapere quale parte dei requisiti non è coperta dal CV.
Un fatto importante da evidenziare è che l'assenza di un'informazione nel
curriculum P non viene interpretata come la sua negazione. Viene adottata cioè
un'ipotesi di “mondo aperto”: le parti non specificate della descrizione vengono
semplicemente viste come qualcosa che il possessore del curriculum ha dimenticato di
specificare, o che non ha ritenuto importante precisare. Proprio per questa ragione, con
il processo di abduction formuliamo ipotesi su ciò che non è conosciuto.
Dato un candidato P e un compito da svolgere T, entrambi descritti in
riferimento a un'ontologia , affinchè T sia completamente soddisfatto da P e si abbia
un match completo, deve valere la relazione di sussunzione . Se non
abbiamo match completo, T e P possono essere compatibili o meno. In caso di
compatibilità, risolvendo un CAP, può essere fornita una possibile spiegazione H sul
21
L T T
hL; C;D; T i
T 6j= S uH ´ ? T j= S uH v D
T 6j= P u T v ?
T
T j= P u T
perché il match non si verifichi; in caso di incompatibilità, invece, possiamo risolvere
un CCP ottenendo una possibile spiegazione G sul perchè P e T siano incompatibili, ed
è fornita inoltre una richiesta contratta K compatibile con P. In entrambi i casi siamo in
grado di 'calcolare' spiegazioni sull'assenza di corrispondenza esatta (full match).
Sintetizzando, quindi, se P e T sono compatibili, H e la spiegazione; se invece sono
incompatibili, risolvendo un CCP ricaviamo la spiegazione dell'incompatibilità G e una
nuova richiesta K compatibile con T. In un secondo step, risolvendo un CAP su P e K,
ricaviamo la spiegazione H' del perchè non abbiamo un match completo. In definitiva G
e H' sono le spiegazioni in caso di incompatibilità. Nel nostro sistema, per scegliere il
candidato che si discosta meno dai requisiti possiamo avvalerci di una funzione, detta
“match degree function (or Utility)” [2], che effettua una valutazione numerica delle
cause della mancanza di match esatto:
Questa funzione può essere usata sia in caso di incompatibilità che di compatibilità
(dove nel secondo caso si avrà ), ed è basata sulla funzione rankPotential [3],
utilizzata per classificare i match potenziali, che vediamo qui nella sua struttura di base:
22
U : hG;H; T i ¡! R
G = >
Algorithm rankPotential(C, D)
input concepts C, D, in forma normale, tali che C D è soddisfacibile
output rank n 0 di C rispetto a D, dove 0 significa (best ranking)
begin algorithm
let in
¸ C v D
n := 0
u
Una possibile espressione della funzione U è, come proposto in [2] :
=
• k: valutazione di K soluzione di un CCP tra e T –
• h: valutazione di H soluzione di un CAP tra K (T se non c'è contrazione) e –
23
U(hT; P;G;H; T i) j1¡ NN¡g ¤ (1¡ h
k )j
Pi
h = rankPotential(P;K; T )
k = rankPotential(>; K; T )
/* aggiunge ad n il numero dei nomi di concetti in D
che non sono tra i nomi di concetti in C */
1.
/* aggiunge ad n le restrizioni numeriche di D
che non sono implicate da quelle di C */
2. for each concept ( x R)
such that there is no concept with
3. for each concept
such that there is no concept with
/* per ogni restrizione universale in D
aggiunge il risultato di una chiamata ricorsiva */
4. for each concept
if there does not exist
then
else
return n;
end algorithm
8R:E 2 Dall
8R:F 2 Call
n := n+ rankPotential(>; E);
n := n+ rankPotential(F;E);
n := n+ 1;
n := n+ 1;
(· xR) 2 D#
(· yR) 2 C# y · x
(¸ yR) 2 C# y ¸ x
¸ 2D#
Pi
n := n+ jDnames+ ¡Cnames+j;
• g: valutazione di G soluzione di un CCP tra e T –
• N: valutazione di T –
Scegliendo il candidato che minimizza il valore di U, l'algoritmo tiene conto di una
misura numerica delle rinunce che si sono dovute accettare e delle ipotesi che sono state
fatte sul profilo analizzato.
2.5.1 Algoritmo di match Uno a Uno (One to One Skill Matching)
Il processo che assegna un lavoratore a un incarico richiede l'uso sia dell'abduction che
della contraction, e la scelta del candidato migliore è fatta sfruttando la funzione Utilità
vista poc'anzi. Definiamo quindi un algoritmo che valuta le possibili
corrispondenze tra un compito richiesto T e un insieme di profili ,
in relazione ad un'ontologia definita in .
24
Pi
g = rankPotential(K;T; T )
N = rankPotential(>; T; T )
Assign(T; P; T )
P = fPig; i = 1:::n
T ALN
1: Algorithm
2: input P, T concepts in such that both and
3: output
4: begin algorithm
5: ;
6: ;
7:
8: for each
9: if
10: then
Assign(T; P; T )´ K uG L T j= P 6´ ? T j= T 6´ ?
hPassigned ; H;Gi
Passigned ´ >Umin =1N = rankPotential(>; T; T );
Pi 2 P
T j= T u Pi ´ ?
In questo algoritmo determina che H è soluzione del
problema di concept abduction mentre
determina come soluzione del problema di concept contraction .
La procedura restituisce , ovvero il miglior candidato trovato tra tutti i
profili disponibili , insieme a una spiegazione H delle competenze che dovrebbe
possedere per ricoprire completamente i requisiti T, e al concetto G nel caso in cui sia
stata necessaria una contrazione preliminare dei requisiti. Il profilo che minimizza U
25
Hi = abduce(Pi;Ki; T );
hL; T; Pi; T i hGi;Kii = contract(Pi; T; T )
hGi;Kii hL; T; Pi;T i
Passigned
11:
12: else
13:
14: end if
15:
16:
17:
18:
19:
20: if
21: then
22:
23:
24:
25:
26: end if
27: end for each
28: return
29: end algorithm
Ui = u(N; ki; hi; gi);
Ui < Umin
Umin = Ui;
Passigned = Pi;
H = Hi;
G = Gi;
hPassigned ; H;Gi;
Pi
gi = rankPotential(Ti; Ki; T );
Hi = abduce(Pi;Ki; T );ki = rankPotential(>; Ki; T );hi = rankPotential(Pi;Ki; T );
hGi;Kii = contract(Pi; T; T );
Ki = T ;
viene scelto. Come vedremo nel capitolo 5, l'algoritmo può essere preceduto da un
preprocessing ed essere quindi applicato a un sottoinsieme di profili restituiti da una
query a un database.
2.6 Il ragionatore MaMaS-tng
MaMaS-tng (Match Making Service - The Next Generation) è un ragionatore in
DL che fornisce tutti i servizi di ragionamento standard (sussunzione,
soddisfacibilità...) più alcuni servizi di inferenza non standard come Concept
Contraction, Concept Abduction e RankPotential. E' sviluppato dal gruppo di ricerca del
SisInfLab del Politecnico di Bari ed è raggiungibile all'indirizzo
http://dee227.poliba.it:8080/MAMAS-tng/DIG. I servizi di inferenza non standard del
MaMaS non sono implementati da nessun altro ragionatore, e attraverso essi sarà
possibile implementare l'algoritmo di match uno a uno visto nel precedente paragrafo.
Consiste fondamentalmente in un sistema multi-utente di servlet Java ed
implementa un'interfaccia DIG 1.1 [4] modificata.
2.6.1 L'interfaccia DIG
L'interfaccia DIG è stata sviluppata con lo scopo di fornire un metodo comune di
comunicazione con i ragionatori in DL. Il protocollo di comunicazione utilizzato è
l'HTTP e il corpo delle richieste è costituito da messaggi codificati in XML. Vi sono
26
ALN
ALN
quattro tipi di richiesta: identificazione, gestione della knowledge base, tell and ask. che
consentono di aggiungere informazioni alla KB o a interrogare la KB stessa. Anche il
corpo dei pacchetti di risposta è codificato in XML secondo la sintassi tipica dello
standard DIG.
Quest'interfaccia, concepita dal Description Logic Implementation Group, è
stata estesa per fornire il supporto ai servizi di inferenza tipici del MaMaS. Una delle
caratteristiche principali del sistema è la monotonicità delle richieste TELL: una volta
che delle informazioni sono state aggiunte alla KB, non possono più essere modificate,
almeno fino a quando non si rilascia la KB e se ne crea un'altra. Ogni messaggio, infatti,
è identificato mediante l'URI della knowledge base, che è un codice alfanumerico che
identifica univocamente una sessione ed è legato all'indirizzo IP del client.
Dal momento che le descrizioni semantiche generate dal sistema sono in
linguaggio OWL-DL, e che il ragionatore accetta messaggi XML/DIG, è evidente che
vi dev'essere un'equivalenza tra le due sintassi. Di seguito vediamo la corrispondenza
tra la generica DL , OWL e DIG:
27
1.2: corrispondenza tra DL , OWL-DL e DIG
28
ALN
3 HR-XML e Staffing Exchange Protocol
Come detto in precedenza, il progetto ha come scopo la creazione di un sistema
intelligente di supporto alle decisioni nell'ambito della gestione delle risorse umane. Vi
sono due requisiti fondamentali per il funzionamento del sistema: la persistenza dei dati
relativi alle risorse umane, e la loro omogeneità.
Per persistenza si intende la memorizzazione permanente e sicura su un supporto
magnetico, e questo è ottenuto utilizzando il DBMS (MySQL in questo caso). Questo
oltre a memorizzare i dati in una tabella, possiede la caratteristica della
“transazionalità”, il che implica che un processo di inserimento o di interrogazione al
database può terminare esclusivamente in due modi: successo o fallimento, senza
possibilità di soluzioni intermedie.
Inoltre è necessario, ovviamente, che i record rappresentanti i profili dei
lavoratori abbiano tutti la medesima struttura (altrimenti non potrebbero essere inseriti
nella stessa tabella del DB). Nella progettazione della struttura da dare ai dati, e di
conseguenza all'interfaccia grafica che dovrebbe consentirne l'inserimento da parte
dell'utente, si è scelto di seguire un modello messo a punto dal consorzio HR-XML.
Questa scelta è dovuta sia al successo che l'iniziativa del consorzio sta ottenendo, e sia
al fatto che, dal momento che l'applicazione oggetto di questa tesi si inserisce nel grande
progetto del Web semantico, è stato ritenuto necessario seguire uno standard
sufficientemente diffuso nell'ottica dell'integrazione e dell'interscambiabilità dei dati.
29
3.1 Il consorzio HR-XML
Il consorzio HR-XML è un'organizzazione indipendente e non-profit dedicata
allo sviluppo e alla promozione di uno standard di specifiche XML di supporto all'e-
business e all'automazione dello scambio di dati concernenti le risorse umane.
Il 'mercato' elettronico delle risorse umane, o lo scambio di informazioni tra
organizzazioni, richiede un accordo tra i partecipanti riguardo le modalità di
svolgimento delle transazioni. La missione del consorzio HR-XML (dove HR indica,
appunto, risorse umane) è di risparmiare alle organizzazioni e ai datori di lavoro il
rischio e l'investimento economico necessario a coordinarsi per la creazione di un
sistema comune di scambio dati. Tramite lo sviluppo e la pubblicazione di standard
aperti basati su XML, il consorzio fornisce ad ogni compagnia il mezzo per trattare con
le altre senza bisogno di implementare molti sistemi differenti di comunicazione.
Il consorzio ha una struttura decentralizzata, e i suoi membri appartengono a
importanti compagnie del campo dell' Information Technology, che fungono anche da
sponsor e finanziatori.
3.2 Staffing Exchange Protocol (SEP 2.4)
I sistemi di reclutamento e assunzione attuali sono molto più complessi di quelli
esistenti solo pochi anni fa. Ogni parte del processo è stata isolata, migliorata e, in molti
casi, è stata resa orientata al web per realizzare una sempre maggiore efficienza.
30
E' possibile quindi che una gran varietà di tecnologie sia coinvolta in un singolo
processo di assunzione: job boards, sistemi di reclutamento delle aziende, parser di
curriculum, sistemi ERP (Enterprise Resource Planning) o altri. L'eterogeneità dei
sistemi rende necessaria la standardizzazione.
Lo Staffing Exchange Protocol è uno dei protocolli creati dal consorzio HR-
XML, e supporta molti tipi di transazioni inerenti allo scambio di risorse umane,
dall'inserimento di offerte di lavoro all'invio del curriculum dei candidati ad un altro
sistema di reclutamento, e prevede i seguenti tipi di documento:
• Curriculum (Resume)
• Candidato
• Posizione lavorativa (Position opening)
3.2.1 Differenza tra profilo del candidato e curriculum
Il profilo di un candidato può essere descritto come un insieme di dati
riguardanti un individuo strutturati in modo tale da permettere la corrispondenza
automatizzata tra domanda e offerta di lavoro. E' pensato in modo da contenere tutte le
informazioni storiche, le abilità e le preferenze lavorative del candidato.
Un curriculum, invece, è redatto senza seguire rigidamente un modello standard
e mette in risalto alcune informazioni a discapito di altre. Inoltre, può contenere dati su
hobbies e interessi personali, su pubblicazioni fatte, brevetti e premi, e non contenere
invece informazioni sulle preferenze salariali, ad esempio.
31
Un'altra importante differenza è che mentre la formattazione e la presentazione
sono aspetti importanti per un curriculum, che può essere redatto con le tecniche più
disparate (HTML, Flash, PDF...), sono invece marginali per il profilo del candidato, che
dev'essere flessibile.
Profilo del candidato e curriculum si differenziano anche per il modo in cui sono
creati e memorizzati: mentre il curriculum è usualmente redatto usando un word
processor e memorizzato localmente sul pc del proprietario, il profilo del candidato è
memorizzato in un database, accessibile via web mediante nome utente e password. Nel
progetto oggetto di questa tesi si cercherà di rendere accessibili contemporaneamente
sia il profilo del candidato che il suo curriculum.
3.2.2 Casi d'uso
Come abbiamo detto, lo Staffing Exchange Protocol cerca di adattarsi a quante
più applicazioni possibili. Due dei modelli più comunemente usati nella realizzazione
dei sistemi di reclutamento, sono quelli:
• a “Imbuto”: viene inserita nel sistema la descrizione di una posizione
lavorativa, e molti candidati vengono analizzati per trovare quelli che meglio si
adattano alle specifiche. E' un metodo incentrato sul datore di lavoro e usato
quando l'offerta (i candidati) è maggiore della domanda (le posizioni lavorative).
• a “Imbuto rovesciato”: viene inserito nel sistema il profilo di un candidato, e
molte offerte lavorative vengono filtrate per trovare quelle più idonee per il
32
candidato stesso. E' un metodo incentrato sul lavoratore e usato in contesti nei
quali la domanda è maggiore dell'offerta.
3.2.2.1 Modello a imbuto
Questa metodologia è basata sulle specifiche stabilite dall'azienda, e il processo
di selezione inizia quando un manager identifica la necessità di ricorrere a personale
aggiuntivo.
1.3: Schema grafico del processo a imbuto standard
33
Il datore di lavoro rende disponibile una posizione lavorativa, le cui specifiche
sono spesso basate su una descrizione preesistente del compito da assegnare. Una volta
che la posizione è approvata, viene inserita in un sistema informativo per le risorse
umane (HRIS). Successivamente, i candidati vengono vagliati e filtrati in varie fasi,
come mostra la figura:
2.3: Esempio di processo di reclutamento per il modello a imbuto standard
34
3.2.2.2 Modello a imbuto rovesciato
Il modello a imbuto rovesciato affronta il problema dal punto di vista
esattamente opposto, ed è pertanto orientato al candidato. Inizialmente si costruisce un
profilo del candidato in base ad esperienza, qualifica posseduta, carriera svolta,
disponibilità e preferenze, ecc. Successivamente ci si accerta delle competenze
possedute dal candidato sottoponendolo a un'intervista (tramite un form ad esempio), ed
infine viene prodotta una lista di posizioni lavorative alle quali il candidato potrebbe
essere interessato. Se questi è interessato a trovare immediatamente lavoro, vengono
elencati i posti disponibili; se invece è interessato alla carriera, viene fatta un'analisi
delle competenze mancanti per aiutare il candidato a raggiungere i suoi obiettivi
lavorativi.
35
3.3: Schema grafico del processo a imbuto rovesciato
In figura vediamo lo schema di un possibile processo di assunzione per un
sistema che adotta il modello a imbuto rovesciato.
4.3: Esempio di processo di reclutamento per il modello a imbuto rovesciato
36
Il modello utilizzato nella progettazione del sistema oggetto della tesi è quello a
imbuto standard. Passiamo ora ad analizzare la struttura che i dati devono avere secondo
il protocollo.
3.2.3 Schema del candidato - “Candidate”
Il modello del candidato è un insieme flessibile di moduli progettati per
contenere informazioni dettagliate su una persona nel contesto del processo di
assunzione. Lo schema è strutturato in modo tale da presentare ai livelli più alti le
informazioni relative alla transazione, come ad esempio annunci di lavoro ai quali il
candidato potrebbe rispondere, i dati dell'agente nel caso in cui il candidato sia
rappresentato da una terza persona o linee guida per regolare la diffusione dei dati
presenti nello schema.
(Per una breve legenda si veda lo schema a fine capitolo)
37
5.3: schema di CandidateType
Come si può notare anche dalla figura, lo schema comprende un gran numero di
informazioni. Per gli scopi di questo lavoro, tuttavia, prevedendo anche
l'implementazione del modello in termini di interfaccia grafica, si limiterà l'analisi
soltanto ad alcuni sotto-elementi dello standard SEP. In particolare, per quanto riguarda
il candidato, si studierà il modulo denominato “Preferred Position”, collocato
nell'albero che definisce il protocollo nella posizione illustrata dalla figura seguente:
6.3: posizione di Preferred Position nello schema generale
Questo modulo contiene informazioni dettagliate sugli interessi del candidato,
sulle sue preferenze e sulle sue competenze. Inoltre costituisce la parte che più di ogni
altra stabilisce la differenza tra il profilo del candidato e il curriculum, ed è
espressamente progettata per permettere il match elettronico tra candidato e posto di
lavoro. Infatti quasi tutti gli elementi qui presenti compaiono anche in Position
Opening. Quando questi dati sono presenti in Candidate, rappresentano quello che una
38
persona richiede o desidera, mentre quando sono presenti in Position Opening,
rappresentano i requisiti o le offerte proprie di una posizione lavorativa.
Lo schema corrispondente al modulo in questione è il seguente:
7.3: Schema delle componenti di PreferredPosition
39
Ognuno dei sotto-elementi di PreferredPosition è identificato dal nome e dal
tipo di dato, che può essere semplice – come nel caso di String o Integer – o composto,
e in questo caso bisognerà far riferimento allo schema corrispondente, che specificherà
a sua volta i campi da cui il dato è costituito e il loro tipo.
Sinteticamente quindi, il modulo in esame contiene informazioni su: tipo e
dimensioni dell'azienda, collocazione fisica del posto di lavoro, tipo di lavoro e sua
classificazione, orari e turni, competenze possedute, richieste economiche, “stile”
lavorativo, possibilità di trasferimento e viaggio. Questi moduli saranno visti più nel
dettaglio in seguito.
3.2.4 Schema della posizione lavorativa - “Position Opening”
Il modulo denominato “Position Opening” è strutturato in maniera simile a
quello del candidato, con le informazioni sulla transazione ai livelli più alti dello
schema.
40
8.3: Schema di PositionOpeningType
Qui chiaramente l'attenzione è orientata alla classificazione dell'offerta
lavorativa, alla sua descrizione e al numero di posti disponibili per il lavoro in
questione. Dal momento che ogni sotto-elemento dello schema è molto esteso, anche in
questo caso ci limiteremo all'analisi del modulo denominato “Position Detail”, duale
del modulo “Preferred Position” visto in precedenza, e collocato nell'albero che
definisce il protocollo nella posizione illustrata dalla figura seguente:
9.3: posizione di Position Detail nello schema generale
41
Lo schema del modulo è identico a quello già visto per “Preferred Position”,
con al sola differenza che al posto dell'elemento “Commute”, ora è presente il seguente:
10.3: estratto dello schema di “Position Detail”
3.2.5 Modulo di match - “Position Matching”
Per stabilire una corrispondenza tra domanda e offerta nel processo di selezione
dei candidati, è necessaria una struttura che funga da collegamento tra i moduli
“Preferred Position” (da Candidate) e “Position Detail” (da Position Opening).
“Position Matching” assolve proprio a questo compito.
11.3: corrispondenze tra moduli
42
Il modulo comprende molte più informazioni di quelle reperibili comunemente
in un annuncio di lavoro. Sono inclusi infatti altri dati che possono essere raccolti in
varie fasi del processo di assunzione, come valutazioni, interviste, ecc... . Le
informazioni meno strutturate facenti parte del modulo sono: informazioni sull'azienda
(nome, ID, dimensione), informazioni generali sul tipo di lavoro, sul suo stile e sulle
possibilità di viaggio e trasferimento. I moduli più complessi saranno analizzati
separatamente. Di seguito è riportato lo schema generale di “Position Matching”:
43
12.3: Schema principale di “Position Matching Type”
3.2.5.1 Physical Location
44
Questo modulo contiene tutte le informazioni utili all'individuazione geografica
di un posto di lavoro, che può anche essere diverso da quello del datore di lavoro. Nello
schema del candidato, specifica dove il candidato preferirebbe lavorare. Sono supportati
diversi tipi di dati: dalle coordinate geografiche, all'indirizzo esatto, alla definizione
flessibile di aree personalizzate.
13.3: Schema del modulo “SEPPhysicalLocation”
3.2.5.2 Postal Address
45
Questo modulo mira a fornire una descrizione dell'indirizzo postale abbastanza
estesa e flessibile da poter essere utilizzata universalmente, adattandosi agli indirizzi di
ogni Paese.
14.3: Schema del modulo “PostalAddressType”
3.2.5.3 Person Name
46
Il dato più comune riguardante gli individui che è possibile trovare pressochè in
ogni processo lavorativo è il nome. I nomi hanno diverse componenti, e queste possono
variare da uno Stato all'altro o da un'etnia all'altra. Dal momento che l'uso dei nomi può
variare anche in base allo scopo per il quale si utilizzano, bisogna prevedere sia la
possibilità di trattarli come stringhe monolitiche che come dati compositi. Il modulo
“Person Name” tenta di assolvere a questo compito.
15.3: Schema del modulo “Person Name”
3.2.5.4 Remuneration Package
47
Contiene tutte le informazioni riguardanti il salario, i pagamenti extra e i servizi
o le agevolazioni fornite dalla compagnia.
16.3: Schema del modulo ”Remuneration Package”
3.2.5.5 Shift
48
Contiene informazioni dettagliate sui giorni e le ore associati a un particolare
ruolo lavorativo, o quelli in cui il candidato preferisce lavorare. “ShiftPeriod” è il dato
più importante e definisce l'intervallo di tempo a cui gli altri dati fanno riferimento.
17.3: Schema del modulo “Shift”
3.2.5.6 Competency
Questo schema si prefigge l'obiettivo di valutare quantitativamente le
competenze dell'individuo, che nella logica del protocollo SEP sono ritenute attributi
misurabili. E' previsto anche l'uso di particolari tassonomie standardizzate per
classificare le competenze, e per ognuna di esse si possono individuare “pesi” numerici
ed “evidenze” (risultati, rapporti, risultati di test, certificati, titoli di studio...).
49
18.3: Schema del modulo “Competency”
19.3: Schema del modulo “Competency Evidence”
20.3: Schema del modulo “Competency Weight”
50
I moduli appena visti saranno il riferimento per la costruzione dell'interfaccia
grafica, oggetto del prossimo capitolo.
La tabella seguente vuole essere un riferimento per l'interpretazione dei simboli
usati negli schemi.
21.3: Simboli usati negli schemi del protocollo SEP
51
4 L'interfaccia grafica
Nel capitolo precedente sono state esaminate le componenti alla base della
costruzione dell'interfaccia grafica del sistema, mentre ora se ne vedrà l'effettiva
implementazione. Dal momento che il sistema è “bilaterale”, ovvero orientato sia al
candidato che vuole inserire il proprio curriculum nel sistema, sia all'azienda che
intende ricercare determinate competenze all'interno del pool di individui memorizzati,
il pannello iniziale permette all'utente di accedere, appunto, o come azienda o come
lavoratore.
Al fine di avere un'interfaccia simile a quella di un'applicazione desktop, ma che
sia ugualmente raggiungibile via web, è stata scelta la tecnologia Ajax, della quale
ricordiamo le principali caratteristiche.
4.1 AJAX
Ajax, acronimo di Asynchronous Javascript and XML, è una tecnica di sviluppo
web per creare applicazioni web interattive. L'intento di tale tecnica è quello di ottenere
pagine web che rispondono in maniera più rapida, grazie allo scambio in background di
piccoli pacchetti di dati con il server, così che l'intera pagina web non debba essere
ricaricata ogni volta che l'utente effettua una modifica. Questa tecnica riesce, quindi, a
migliorare l'interattività, la velocità e l' usabilità di una pagina web.
La tecnica Ajax utilizza una combinazione di:
52
• HTML (o XHTML) e CSS per markup e stile
• DOM (Document Object Model) manipolato tramite Javascript per interagire
con le informazioni.
• l'oggetto HTTPRequest per l'interscambio asincrono dei dati tra il browser
dell'utente e il server.
• XML è il formato più utilizzato per lo scambio dati.
1.4: confronto tra l'interazione client-server di un'applicazione web classica e di una basata su Ajax
53
Le applicazioni web tradizionali consentono agli utenti di compilare moduli e,
quando questi moduli vengono inviati, viene inviata una richiesta al web-server. Il web
server agisce in base a ciò che è stato trasmesso dal modulo e risponde mostrando una
nuova pagina. Dato che molto codice HTML della prima pagina è identico a quello della
seconda, viene sprecata moltissima banda. Il tempo di reazione dell'applicazione
dipende dal tempo di reazione del web server, e questo comporta che l'interfaccia utente
diventa molto più lenta di quanto dovrebbe essere. Se però lo scambio dei dati è
asincrono ed avviene in background, senza che l'utente ne sia pienamente consapevole,
il tempo di risposta viene notevolmente abbattuto, e l'interfaccia diventa molto più
reattiva.
4.2 Google Web Toolkit
Il Google Web Toolkit (brevemente, GWT) è un tool di sviluppo open source,
costituito principalmente da un insieme di librerie Java e da un web server Tomcat, che
consente al programmatore di sviluppare velocemente applicazioni in Ajax anche se non
è in possesso di un'elevata conoscenza di HTML e Javascript. Questo è possibile
grazie alla traduzione del codice Java in codice Javascript. Per questa ragione è
possibile utilizzare solo un sottoinsieme delle librerie standard del Java (java.lang
e java.util), e questo esclude diverse funzionalità quali ad esempio l'accesso al
filesystem.
54
Come scelta progettuale, quindi, si demanda gran parte dell'elaborazione
al server, sul quale possono essere implementati servizi secondo la tecnica
preferita, mentre il client funge quasi esclusivamente da “terminale”.
Schematicamente, l'architettura del GWT è riassunta in quest'immagine:
2.4: Architettura del GWT
Un'altra utile caratteristica del GWT è la disponibilità di componenti per le
pagine web già pronti per l'uso, denominati “Widgets” che possono poi essere estesi
dall'utente sempre senza scendere mai a livello di HTML.
Proprio per le opportunità offerte dal framework di Google, si è deciso di
adottarlo come strumento principale per la creazione dell'interfaccia grafica.
55
4.3 L'interfaccia grafica
In questo paragrafo si illustrerà l'aspetto esteriore delle varie componenti
dell'interfaccia, e si vedranno inoltre i dettagli implementativi di alcune soluzioni
adottate.
4.3.1 Pannello di accesso (ImageViewer.java)
56
3.4: Pannello di accesso:l'utente sceglie una delle due opzioni in base al ruolo che ricopre.
Questa è la prima schermata che l'utente si trova di fronte. Come si vede, vi sono
due possibili modalità d'accesso:
• come lavoratore: i dati che in seguito l'utente inserirà saranno utilizzati per la
creazione del suo profilo, che sarà inviato al database e memorizzato.
• come azienda: i dati inseriti verranno utilizzati per formulare una query da
sottoporre al database e una descrizione semantica dei requisiti da inviare al
ragionatore nella fase di match.
Le Widget utilizzate in questa fase sono fondamentalmente Button, Image, HTML,
VerticalPanel e FlexTable. L'interattività è ottenuta grazie alla gestione degli eventi (si
dice appunto che l'interfaccia è “event driven”), che permette di associare delle azioni a
determinati elementi. In questo caso, per esempio, ai due pulsanti sono stati associati dei
ClickListener, che entrano in azione eliminando il contenuto della tabella centrale
(quindi anche i pulsanti stessi) tramite la funzione clear() e inserendovi un nuovo
pannello, diverso in base alla scelta effettuata.
57
4.4: ClickListener associato al pulsante “Add your profile”
E' da notare inoltre che questa classe, costituendo il punto di inizio
dell'applicazione, implementa l'interfaccia EntryPoint, e conseguentemente il metodo
onModuleLoad(), che conterrà le operazioni iniziali da compiere (in questo caso la
creazione di questa semplice interfaccia grafica).
4.3.2 Pannello principale di inserimento dati (Candidate_Side.java)
58
5.4: Pannello del candidato
Lo stack panel racchiude in se otto sotto-pannelli che richiamano i nomi dei
corrispondenti moduli dello Staffing Exchange Protocol. Inoltre è stata aggiunta la parte
denominata “Semantic Description” , che come vedremo consiste in un pannello
interattivo per la definizione della descrizione semantica dell'individuo, in riferimento
alla struttura delle ontologie utilizzate.
Dal momento che, come già detto nel capitolo sul protocollo SEP, i dati inseriti
possono essere interpretati alternativamente come descrizione di un individuo o come
requisiti espressi dall'azienda, il pannello in esame è identico a quello dedicato
all'azienda, fatte salve soltanto alcune componenti della scheda “Other Informations”.
Il pulsante “Submit” causa l'invio dei dati inseriti al server, utilizzando
l'infrastruttura per le RPC del GWT.
4.3.3 Company Infos (CompanyInfos.java)
La prima scheda dello stack panel, visibile nella figura precedente, contiene,
oltre a una breve spiegazione degli scopi del form, i campi fondamentali per
l'identificazione e la classificazione dell'azienda. Gli elementi “Scale” e “Industry
code” sono compositi (estendono la classe “Composite”) e sono dotati di un pulsante
“Add” che permette di aumentare il numero di istanze degli stessi elementi (fino a un
59
massimo definito nel codice), visto che secondo il modello del SEP, questi attributi
possono avere molteplicità maggiore di uno.
Il codice che permette di ottenere questo risultato è molto semplice e consiste
nell'inserire una nuova copia degli elementi costituenti il Composito in una nuova riga
della tabella che li contiene.
6.4: Frammento di codice da IndustryCode.java. Si occupa dell'aggiunta di nuovi elementi in tabella
4.3.4 Personal Data & Availability (PersonalData_Candidate.java)
Questo pannello non rispecchia nessun modulo del protocollo SEP, a differenza
degli altri, ma è stato costruito riunendo insieme alcuni elementi basilari nella
formulazione del profilo di un individuo, e comprende pertanto una versione
60
semplificata dei dati anagrafici e dell'indirizzo, oltre ad alcune informazioni sulle
preferenze lavorative e a un campo per l'invio del curriculum in formato testo al server.
7.4: Pannello PersonalData_Candidate
I campi Nationality e Country permettono di scegliere la nazione tra quelle
previste dallo standard ISO 3166-2. Il campo Upload curriculum permette di inviare
tramite un form, sfruttando il metodo HTTP_POST, un file al server.
61
8.4: Upload widget dopo il corretto completamento di un upload
Vediamo qui la parte cruciale del codice: l'impostazione dei parametri del form e
la gestione degli eventi di successo e fallimento.
9.4: impostazione parametri del form e gestione dell'evento onSubmitComplete, il cui nome è
autoesplicativo
Per il corretto funzionameno del form, è necessario che il parametro “Action”
sia inizializzato con l'URL relativo sul quale è mappata, nei file di configurazione del
server, la servlet. Una volta invocato il metodo submit(), i valori contenuti negli
62
elementi figli del form dotati dell'attributo name, vengono inviati usando il metodo
HTTP prescelto. E' da notare che i metodi HTTP standard non prevedono alcuna
codifica dell'informazione, cosa che li rende inadeguati in applicazioni che richiedono
un certo grado di sicurezza.
Il pannello che abbiamo appena esaminato, insieme a quello per la semantica
che vedremo in seguito, costituisce il nucleo funzionale del sistema, dal momento che
gli altri non sono stati utilizzati per l'invio di dati. Questo però si potrebbe ottenere
incapsulando tutto lo StackPanel in un oggetto Form, dotando tutti i campi dell'attributo
name, e modificando il servlet in modo da elaborare i campi aggiuntivi.
4.3.5 Locations (PhysicalLocation.java e TabLocation2.java)
63
10.4: Pannello Locations
Contiene informazioni sull'area geografica nella quale risiede il lavoratore o
nella quale è collocata la posizione lavorativa in questione. L'area può essere
individuata attraverso coordinate geografiche, indicazioni stradali e altri tipi di
classificazione. Inoltre contiene informazioni aggiuntive sul nome del lavoratore o
dell'intestatario dell'organizzazione. Lo scopo del pannello è quindi individuare con la
massima precisione, e allo stesso tempo flessibilità, la collocazione di un elemento. Dal
momento che secondo il protocollo, il campo PhysicalLocation ha molteplicità massima
maggiore di uno, è prevista l'aggiunta di altre istanze dello stesso pannello in tab
separati. Come già visto questo è possibile definendo un array di PhysicalLocation e
64
instanziando un nuovo elemento di questo ad ogni pressione sul tasto Add, o eliminando
l'elemento correntemente visualizzato mediante il pulsante Delete.
11.4: Frammento di codice da TabLocation2. Aggiunta e rimozione di Locations
4.3.6 Job Details & Shift (JobPosition.java, JobCategory.java, Shift.java)
65
12.4: Pannello JobPosition
Questo pannello contiene informazioni sul tipo e sul nome della posizione
lavorativa, espresse tramite apposite tassonomie, e sui turni di lavoro (richiesti
dall'azienda o offerti dal lavoratore). Sia JobCategory che Shift possono avere
molteplicità maggiore di uno, e per entrambi è stato usata una visualizzazione a schede
con lo stesso metodo visto in precedenza per le Locations.
66
4.3.7 Competencies (Competency.java)
Come previsto dal protocollo SEP, ognuna delle molteplici competenze
possedute dal lavoratore o richieste dall'azienda è caratterizzata da due componenti
fondamentali: pesi e testimonianze. I primi misurano quantitativamente l'importanza
della competenza, e le seconde indicano le sedi nelle quali la competenza stessa è stata
verificata. Ognuno di questi due elementi ammette molteplicità maggiore di uno.
67
13.4: Pannello Competencies
4.3.8 Remuneration Package (RemunerationPackage.java)
Come già visto nel capitolo sullo Staffing Exchange Protocol, questo pannello
contiene informazioni sulla paga base, sulle eventuali paghe aggiuntive, e sulle
agevolazioni che l'azienda può fornire ai suoi dipendenti (o che l'aspirante dipendente
richiede).
68
14.4: Pannello Remuneration Package
4.3.9 Altre informazioni (OtherInfos_company.java e OtherInfos_candidate.java)
Questo pannello contiene informazioni sullo “stile” lavorativo caratteristico
della posizione in esame, sull'abbigliamento richiesto ed eventualmente fornito
dall'azienda, e infine sulla disponibilità del lavoratore a viaggiare, ad accettare eventuali
trasferimenti e ad essere pendolare. Nel “lato azienda” quest'ultima parte è sostituita
69
dalla descrizione del 'livello' del posto di lavoro, sempre facendo riferimento a
classificazioni create ad-hoc.
15.4: Panello Other Infos dedicato al lavoratore
16.4: JobLevel. Sostituisce Commute nel lato azienda
70
4.3.10 Composizione della descrizione semantica
L'ultimo pannello che analizziamo è quello denominato, appunto, “Semantic
Description”, che è uguale sia nel 'lato azienda' che nel 'lato lavoratore'. E' il nocciolo
del progetto, dal momento che costituisce l'elemento di innovazione rispetto ai sistemi
basati sul semplice confronto 'testuale' delle informazioni, il più delle volte contenute in
un database. Il presente vuole quindi essere un'estensione “intelligente” di detti sistemi.
Tramite questo pannello è possibile costruire graficamente la descrizione
semantica dell'individuo, dove per individuo non si intende necessariamente una
persona fisica. Infatti se il profilo viene costruito da un lavoratore che sta immettendo i
propri dati nel sistema, esso rappresenta la descrizione della sua formazione
professionale, delle conoscenze possedute e del grado di esperienza acquisito nei vari
ambiti; se invece il profilo viene creato da un'azienda, esso rappresenta i requisiti che i
candidati devono possedere per essere ritenuti idonei. Tuttavia, dal punto di vista dei
linguaggi OWL e DIG, il termine “individuo” viene utilizzato in entrambe le fattispecie,
in quanto la descrizione creata rappresenta un'istanza di determinate classi
dell'ontologia, o meglio un'istanza dell'intersezione di diversi concetti definiti
nell'ontologia.
71
17.4: Pannello per la descrizione semantica. A scopo dimostrativo, nell'immagine sono
contemporaneamente visibili tutti e tre gli alberi corrispondenti alle tre ontologie impiegate
La descrizione, quindi, dev'essere elaborata a partire da un'ontologia,
selezionandone alcuni elementi per costruire l'individuo. Per facilitare la creazione di
un'interfaccia grafica sufficientemente user-friendly, tale da rendere chiare all'utente le
componenti da utilizzare nella descrizione, sono state utilizzate tre ontologie distinte,
identificate dai nomi Degree (per le lauree e i titoli di studio), Level (per il livello di
certificazione ottenuto, es.: master, Phd) e Skill (per competenze, titoli di lavoro,
esperienza). Grazie a questa suddivisione, l'interfaccia guida l'utente attraverso tre step
successivi, nei quali è libero di selezionare elementi dalle corrispondenti ontologie.
72
Schematicamente, i passi da seguire possono essere descritti così:
• Step 1: selezione di un elemento dal riquadro Degree e click sul pulsante “+”.
Così l'elemento viene aggiunto all'albero riassuntivo sulla destra.
Quest'operazione può essere ripetuta più volte;
• Step 2: selezione di uno o più elementi dal riquadro Level;
• Step 3: selezione dall'apposita lista di una proprietà. Se per questa non è definito
un range, si inserisce un valore numerico che fungerà da restrizione per la
proprietà stessa. Se invece è dotata di range, le classi appartenenti a quest'ultimo
vengono caricate nel riquadro a destra ed è possibile selezionarle. Inoltre in tal
caso è possibile specificare una restrizione numerica in termini di un'altra
proprietà.
Esempio: has_job_title ha come range Job_Title. Quindi si può scegliere tra una
delle sottoclassi di Job_Title e specificare una restrizione numerica: >5 years.
Inoltre, facendo doppio click su un nodo, l'utente può eliminarlo insieme a tutti i
suoi nodi figli attraverso il comando 'Delete'.
73
18.4: Schema di funzionamento del pannello semantico
Vedendo il pannello in questione come una black box, si può dire che questa
abbia in ingresso le tre ontologie precedentemente descritte codificate in XML, e che
mediante l'interazione con l'utente, fornisca in uscita un documento XML che riproduce
la struttura dell'albero creato dalla funzione BuildSummaryTree().
Muovendosi attraverso i tre passi, alcune aree dell'interfaccia vengono attivate, e
altre disattivate per mezzo della funzione Steps(), che viene chiamata ad ogni
transizione tra i passi.
Come si può notare è stato largamente usato il linguaggio XML per la
trasmissione dei dati da e verso il server, in quanto si presta particolarmente alla
riproduzione di strutture ad albero, che sono centrali nell'applicazione che si sta
studiando. E' abbastanza facile, infatti, convertire alberi XML in widget di tipo Tree e
viceversa.
4.3.10.1 Ricezione delle ontologie
Dal momento che dal lato client non si ha accesso al file system e si può
utilizzare un ristretto sottoinsieme delle librerie standard di Java, è necessario che le
ontologie, e quindi i rispettivi file .owl, siano memorizzati sull' hard disk del server.
Bisogna quindi elaborare le ontologie sul server e inviarle al client in un formato che
74
possa manipolare con le poche librerie a disposizione. Si è optato quindi per una
codifica XML estremamente semplice, come si può osservare in figura.
19.4: Gerarchia di classi dell'ontologia Degree codificata in XML
La funzione XML2Tree accetta come parametro l'elemento radice dell'albero
XML e lo visita ricorsivamente convertendo, in pratica, gli Element XML in TreeItems.
75
<Degree> <Bachelor/> <Computer_Science/> <Design> <Architecture/> <Industrial_Design/> <Interior_Design/> </Design> <Engineering> <Bio_Engineering/> <Chemical_Engineering/> <Civil_Structural_Engineering/> <Computer_Science_Engineering/> <Electrical_Engineering/> <Electronics_Engineering/> <Industrial_Manufacturing_Engineering/> <Managerial_Engineering/> <Mechanical_Engineering/> <Systems_Process_Engineering/> </Engineering> <Master_Degree/> <Mathematics/> <Physics/> <Secondary_School/></Degree>
20.4: Funzione XML2Tree: converte alberi XML in strutture grafiche di tipo TreeItem
E' da notare che mentre le ontologie Degree e Level vengono caricate una sola
volta, l'ontologia Skill viene “interrogata” ogni volta che si seleziona una proprietà.
L'albero restituito sarà il sotto-albero dell'ontologia avente come radice il nodo
omonimo della proprietà selezionata.
Bisogna fare invece un discorso leggermente diverso per la ricezione dell'elenco
di proprietà. In questo caso la stringa XML ricevuta sarà di questo tipo:
21.4: Proprietà dell'ontologia Skill codificate in XML
Al momento della ricezione, gli elementi vengono inseriti in una HashMap,
ovvero una struttura che associa chiavi a valori. La chiave sarà il nome della proprietà, e
il valore l'eventuale range, che altrimenti sarà null.
4.3.10.2 Trasmissione della descrizione semantica
76
<Properties> <advanced_knowledge/> <has_experience range="Skill"/> <years/> <has_job_title range="Job_Title"/> <tools_knowledge range="Skill"/> <has_industry range="Industry"/> <has_job_type range="Job_Type"/></Properties>
La funzione BuildSummaryTree(), in base allo step nel quale ci si trova e agli
elementi definiti nelle varie sezioni del pannello, costruisce l'albero descrivente il
profilo dell'individuo. Nel fare ciò verifica che non vi siano elementi duplicati e
consente inoltre di aggiungere sotto-nodi a nodi già presenti nell'albero quando questi
corrispondono a proprietà con range.
Si pone poi il problema dell'invio dell'albero creato al server, dove verrà
convertito in OWL e inviato, a seconda dei casi, al database o all'algoritmo di match.
Anche in questo caso si è scelto di codificare l'informazione in XML con la funzione
XMLProfile().
<profile> <class name="Computer_Science"/> <class name="Phd"/> <restriction name="advanced_knowledge" sign="gt" value="1"/> <property name="has_job_title"> <class name="Programmer"/> <restriction name="years" sign=">" value="5"/> </property> </profile>
Tabella 1.4: Parallelo tra l'albero riassuntivo e la sua codifica in XML
5 Struttura e funzioni del server
Come visto in precedenza, l'applicazione oggetto di questa tesi deve
necessariamente avere una struttura client-server. I motivi sono diversi: innanzitutto è
necessario compiere delle elaborazioni che sul client non sarebbero possibili, vista la
77
limitatezza del linguaggio Javascript; è necessario inoltre utilizzare un database
centralizzato come repository di profili, e questo è chiaramente possibile solo
posizionandolo sul server; infine è necessario accedere a risorse memorizzate su files (le
ontologie), e l'interfaccia Web non consente ovviamente niente del genere. Queste
risorse inoltre devono essere immodificabili da parte degli utenti e gestite
dall'amministratore del sistema.
La conseguenza di queste scelte è che l'utente userà il proprio browser
semplicemente come un terminale per interfacciarsi al sistema. Inoltre, dato che non si
ritiene che l'applicazione debba gestire un elevato livello di concorrenzialità, o
equivalentemente un gran numero di sessioni per utenti diversi, è accettabile sottoporre
gran parte dell'elaborazione al server.
Sinteticamente i compiti svolti dal server consistono nell'invio delle
informazioni semantiche al client, e nell'elaborazione delle richieste di quest'ultimo,
siano esse di semplice 'stoccaggio' nel database o di interfacciamento al ragionatore.
5.1 Panoramica delle funzioni del server
Similmente a quanto fatto per il pannello semantico nel lato client, si può dare
una visione della struttura del lato server dell'applicazione:
78
1.5: Schema di principio dei servizi offerti dal server
Come si vede dalla figura, la comunicazione tra client e server è gestita da
quattro servlet java. Le servlet sono oggetti (in senso informatico) che operano
all'interno di un server per applicazioni (per esempio, Tomcat) e potenziano le sue
funzionalità.
La parola servlet deriva da una precedente, applet, che si riferisce a piccoli programmi
scritti in linguaggio Java che si eseguono all'interno di un browser. Per
contrapposizione, un servlet è un programma che si esegue in un server web.
Sostanzialmente quindi, si tratta di programmi che devono rispondere adeguatamente a
79
delle richieste HTTP, e sono associati a un determinato URL. Il mapping dei servlet
utilizzati è contenuto nel file ImageViewer.xml del progetto ed è il seguente:
<module> <inherits name="com.google.gwt.user.User"/> <entry-point class="com.mycompany.project.client.ImageViewer"/> <servlet path="/onto" class="com.mycompany.project.server.OntologyServletImpl"/> <servlet path="/send" class="com.mycompany.project.server.SendDataServletImpl"/> <servlet path="/fileupload" class="com.mycompany.project.server.UploadServlet"/> <servlet path="/filedownload" class="com.mycompany.project.server.DownloadServlet"/> <inherits name="com.google.gwt.xml.XML"/></module>
2.5: Mapping dei servlets: corrispondenza classe – URL
Le quattro servlet realizzate differiscono in un aspetto fondamentale: quelle di
Upload e Download sono servlet java “classiche”, ovvero estensioni della classe
HTTPServlet, e rispondono al client usando i metodi HTTP_GET e HTTP_POST. Sono
state realizzate in questo modo perchè devono svolgere compiti relativamente semplici.
Le altre due servlet, OntologyServlet e SendDataServlet, si occupano rispettivamente
dell'elaborazione e dell'inoltro dei dati ontologici al client, e dell'elaborazione delle
richieste del client. Sono state realizzate sfruttando i vantaggi dell'infrastruttura per le
chiamate a procedure remote messa a disposizione dal Google Web Toolkit.
5.2 RPC (Remote Procedure Call) in GWT
80
La differenza fondamentale tra le applicazioni GWT (e in generale Ajax) e le
normali applicazioni web HTML, sta nel fatto che le prime non hanno bisogno di
prelevare nuove pagine HTML durante la loro esecuzione, che è simile a quella dei
normali programmi desktop. In ogni caso, come tutte le applicazioni client/server,
possono aver bisogno di prelevare dati dal server. Il meccanismo di interazione con il
server attraverso la rete si chiama Remote Procedure Call (RPC). In GWT, tramite
questo strumento, è possibile passare e ottenere oggetti Java dal server sfruttando la
connessione HTTP. Inoltre il sistema messo a punto dal team di Google permette di
sfruttare facilmente i vantaggi delle chiamate asincrone tipiche del metodo AJAX, senza
bisogno di addentrarsi troppo nel mondo dei metodi HTTP, Javascript e della
serializzazione. Anche l'interfaccia Java Serializable, infatti, è stata modificata e
rinominata in isSerializable, e implementata dalla gran parte degli oggetti delle librerie
GWT. Secondo la terminologia usata dagli sviluppatori, le chiamate al server sono dette
anche servizi.
Lo schema seguente descrive le componenti necessarie per la chiamata di un
servizio. Ogni servizio è dotato di alcune classi e interfacce “aiutanti”, in parte definite
dall'utente e in parte create automaticamente senza che questi ne sia al corrente.
81
3.5: Schema dell'impianto RPC del GWT
A fronte di una maggiore semplicità, l'utente deve scrivere le due interfacce
(sincrona e asincrona), che si differenziano solo per il tipo di dati restituiti dai metodi e
per i parametri di questi ultimi. Nell'interfaccia *Async, infatti, ogni metodo ha come
parametro aggiuntivo un AsyncCallback, definito al momento della chiamata e che
gestisce gli eventi onSuccess e onFailure. In seguito si vedranno i dettagli delle servlet
implementate.
82
5.3 Upload Servlet
La servlet in questione viene chiamato dal form presente in
FancyFileUpload.java, secondo i metodi classici. Esattamente come in HTML, una
volta creato il form e assegnati i nomi ai campi, si imposta l'azione, ovvero l'URL
corrispondente al servlet, il metodo (POST in questo caso) e la codifica, che è impostata
come MULTIPART proprio per consentire l'invio di files.
4.5: impostazione dei parametri del form sul client
Quindi la servlet UploadServlet estende la classe HTTPServlet e ridefinisce il
metodo doPost(HttpServletRequest req, HttpServletResponse resp). Una volta ricevuta
una richiesta, verifica che sia codificata come MULTIPART, dopo di che scorre tutti gli
elementi ricevuti finchè non ne trova uno di tipo File. Avvalendosi di alcune classi delle
librerie “Commons.FileUpload” del progetto Jakarta/Apache, viene estratto il nome del
file (inizialmente è identificato col percorso completo che esso aveva sul client) e se ne
verifica la sua esistenza nella directory di default destinata all'upload dei files, che è
appunto “uploads/”. Se il file esiste già, viene rinominato aggiungendo al suo nome un
83
uploadForm.setAction("/fileupload");uploadForm.setEncoding(FormPanel.ENCODING_MULTIPART);uploadForm.setMethod(FormPanel.METHOD_POST);
numero compreso tra 0 e 1000 generato casualmente, finché non si trova un nome non
utilizzato.
Nel caso in cui il file è dotato di estensione, si aggiunge il numero al nome preservando
l'estensione.
5.5: Ciclo per la rinominazione dei files
Una volta terminata la scrittura del file, utilizzando l'output stream fornito da
HttpResponse, viene inviato al client, che lo interpreta come un messaggio di OK, il
path del file appena scritto. Il client memorizza in una variabile il path e lo invia al
database in modo da associare le informazioni strutturate e semantiche dell'individuo al
file del suo curriculum. Nel caso in cui la transazione non vada a buon fine il client,
conoscendo il nome del file appena scritto sul server, può invocare nuovamente il
servlet inviandogli come parametro URLENCODED il nome del file da cancellare. In
questo modo si evita di occupare spazio sull'hard disk per file relativi a transazioni
fallite.
Per i dettagli del codice, si rimanda all'Appendice.
84
5.4 Download Servlet
Quando il client inoltra una query al sistema, vengono interrogati
successivamente sia il repository dei profili che il ragionatore. La SendDataServlet
restituisce infine al client, attraverso una struttura di tipo Map, i dati del candidato
prescelto. Tra questi c'è anche il nome del file del curriculum associato al candidato
stesso, nome che viene usato per creare un apposito link tramite il quale scaricare il file.
Se però al candidato non è associato nessun file, il relativo link non viene mostrato.
DownloadServlet viene invocato in maniera leggermente diversa rispetto agli
altri casi, in quanto viene aperta una nuova finestra inizializzata all'url del servlet, con il
parametro da passare al server codificato nell'url stesso, come nella figura seguente:
6.5: Chiamata del servlet per il download del file con parametri passati attraverso l'URL
In realtà più che un semplice link HTML, la chiamata del servlet è stata fatta
all'interno di un EventListener.
Il metodo usato in questo caso è HTTP_GET, e la scelta è comunque forzata dal
modo particolare in cui si è invocato il servizio remoto. Quando il server riceve la
85
richiesta, estrae il parametro “filename” dall'header Http, imposta i parametri della
risposta e attraverso uno stream di byte invia il file al client.
7.5: Impostazione dell'header della risposta
Il MIME (Multipurpose Internet Mail Extensions) del file viene stabilito
utilizzando l'interfaccia java.net.FileNameMap, la lunghezza della risposta viene
impostata uguale alla lunghezza del file e si scrive un appropriato header nel pacchetto
di risposta indicante che in allegato è presente un file.
8.5: Scrittura del file sull'output stream della risposta. bbuf è un array di 4 Kbyte
5.5 Ontology Servlet
La servlet OntologyServlet gioca un ruolo centrale nel sistema, in quanto è
quella che il client delega ad accedere alle ontologie. Costituisce in effetti un'interfaccia
tra le ontologie e il client.
86
Come detto in precedenza, si dispone di tre ontologie sul server, che dovranno
essere utilizzate dal client per costruire una descrizione semantica coerente
dell'individuo. E' necessario quindi che il servlet, quando invocato, legga il contenuto
delle ontologie, ovvero dei file .owl, crei un modello e recuperi le informazioni
necessarie navigando in esso, per restituirle infine al client in un formato ad esso
comprensibile. Gran parte di queste operazioni vengono svolte grazie alle librerie Jena
[10] sviluppate da HP, studiate appositamente per la progettazione di applicazioni per il
web semantico e in grado di elaborare dati nei linguaggi RDF, RDFS e OWL.
Il servlet del quale ci si appresta ad esporre la struttura viene invocato
automaticamente quando si instanzia un oggetto della classe Semantic, ovvero il
pannello grafico per la definizione della descrizione. In particolare viene chiamata la
procedura loadModel(), che ha essenzialmente il compito di leggere i tre file .owl e
caricarne il contenuto in tre variabili globali di tipo OntModel create secondo il profilo
OWL_DL_LANG, e tali quindi da contenere ontologie definite in OWL-DL.
9.5: Allocazione dei modelli ontologici in OWL-DL
Subito dopo la creazione in memoria dei modelli, vengono chiamate altre tre
procedure remote con il compito di ottenere una rappresentazione del contenuto dei
modelli tale da poter essere sfruttata dal client per costruire l'interfaccia grafica.
87
5.5.1 Invio dell'albero delle lauree
Una volta disponibili in memoria i modelli, tramite la funzione ricorsiva
createTreeNodes, alla quale è passato come parametro un iteratore tra le classi di primo
livello dell'ontologia, viene creata una lista di elementi XML, utilizzando le librerie
JDOM. Dal momento che un elemento xml può contenere altri elementi figli, questa
lista riproduce fedelmente la gerarchia delle classi dell'ontologia, emulando in pratica
una struttura ad albero.
10.5: Funzione loadDegTree(). Restituisce al client un albero XML che riproduce l'ontologia
Il documento XML restituito sotto forma di stringa al client ha la seguente struttura:
88
11.5: Risposta XML contenente la gerarchia di classi dell'ontologia Degree.owl
5.5.2 Invio dell'albero dei Livelli
Questa funzione è esattamente identica alla precedente nella struttura, con la sola
differenza che anziché operare sull'ontologia Degree.owl, opera su Level.owl. Si riporta
l'output della funzione:
12.5: Risposta XML contenente la gerarchia di classi dell'ontologia Level.owl
89
<?xml version="1.0" encoding="UTF-8"?><Degree> <Bachelor /> <Computer_Science /> <Design> <Architecture /> <Industrial_Design /> <Interior_Design /> </Design> <Engineering> <Bio_Engineering /> <Chemical_Engineering /> <Civil_Structural_Engineering /> <Computer_Science_Engineering /> <Electrical_Engineering /> <Electronics_Engineering /> <Industrial_Manufacturing_Engineering /> <Managerial_Engineering /> <Mechanical_Engineering /> <Systems_Process_Engineering /> </Engineering> <Master_Degree /> <Mathematics /> <Physics /> <Secondary_School /></Degree>
<?xml version="1.0" encoding="UTF-8"?><Level> <Certification> <Certification_one /> </Certification> <Doctorate> <Phd /> </Doctorate> <Master_after_Master> <Management_Master /> </Master_after_Master></Level>
5.5.3 Invio delle proprietà
La funzione loadProperties() differisce leggermente dalle precedenti in quanto
non restituisce un albero, dato che non c'è nessun vincolo gerarchico tra le proprietà
definite nell'ontologia Skill.owl, ma semplicemente l'elenco delle proprietà, ottenuto
attraverso OntModel.listObjectProperties(). Anche il range delle proprietà, se presente,
viene inserito nella risposta XML:
13.5: Risposta XML contenente le proprietà dell'ontologia Skill.owl col rispettivo range
5.5.4 Invio dell'albero delle Skills
Mentre le funzioni appena viste vengono eseguite una sola volta nell'arco di vita
dell'applicazione, restituendo informazioni che occuperanno delle aree “statiche”
dell'interfaccia grafica, questa può essere invocata un numero arbitrario di volte. Infatti,
quando sul client l'utente seleziona una proprietà dotata di range, il nome della classe
range viene utilizzato come parametro della funzione loadSkTree, la quale segue una
procedura analoga a quella delle altre funzioni, con la differenza che l'albero restituito è
90
<?xml version="1.0" encoding="UTF-8"?><Properties> <advanced_knowledge /> <has_experience range="http://www.doom-srl.com/unnamed.owl#Skill" /> <years /> <has_job_title range="http://www.doom-srl.com/unnamed.owl#Job_Title" /> <tools_knowledge range="http://www.doom-srl.com/unnamed.owl#Skill" /> <has_industry range="http://www.doom-srl.com/unnamed.owl#Industry" /> <has_job_type range="http://www.doom-srl.com/unnamed.owl#Job_Type" /></Properties>
il sotto-albero della classe avente il nome ricevuto. Sul client quindi l'utente potrà
selezionare solo elementi facenti effettivamente parte del range della proprietà prescelta.
14.5: Codice che evidenzia l'estrazione del sotto-albero della classe indicata da resourceName
<?xml version="1.0" encoding="UTF-8"?><Job_Type> <Intern /> <Seasonal /> <Temporary_Conctract_Project /> <Worker_Employee /></Job_Type>
<?xml version="1.0" encoding="UTF-8"?><Job_Title> <Asset_Manager /> <Process_Manager /> <Programme_Manager> <Delivery_Programme_Manager /> <Infrastructure_Programme_Manager /> </Programme_Manager> <Programmer /> <Project_Manager> <Infrastructure_Project_Manager /> <Service_Design_Development /> </Project_Manager></Job_Title>
15.5: Due possibili output di loadSkTree
5.5.5 Sincronizzazione tra procedure remote
16.5: Chiamata sequenziale delle procedure remote
91
In figura si vede il frammento di codice del client che invoca le quattro
procedure remote utili alla creazione del pannello per la descrizione semantica.
Nonostante l'ordine delle chiamate sia sequenziale, bisogna osservare che queste
avvengono secondo il metodo AJAX, e sono quindi asincrone. Questo vuol dire che la
seconda potrebbe iniziare prima della conclusione della prima, e così per le altre. Se ciò
avvenisse, loadDegreeTree cercherebbe di caricare l'albero delle lauree prima che il
modello dell'ontologia sia caricato in memoria, e restituirebbe null. Per evitare questo
inconveniente, è stato implementato un metodo semplice per la sincronizzazione tra
procedure remote.
Sono stati creati tre flags, degreeOk, levelOk e mainOk, inizializzati come false,
che vengono posti uguali a true quando la funzione loadOwls termina di caricare i
rispettivi modelli ontologici. E' stato imposto quindi alle altre funzioni di non iniziare
finchè l'opportuno flag non diventa true, attraverso il seguente codice:
17.5: Ciclo di attesa della procedura loadDegTree
Questo ciclo impone al thread corrente di cessare la sua esecuzione per un tempo
prefissato espresso in millisecondi, per un certo numero di volte, finchè il modello
dell'ontologia appropriata non viene caricato.
92
5.6 Send servlet
Questa servlet costituisce la parte più complessa e importante del lato server
dell'applicazione. Si occupa infatti di ricevere i dati inviati dal client e di avviare, a
seconda dei casi, le procedure di inserimento nel database o il processo di ricerca di cui
si vedrà tra breve l'articolazione.
In entrambi i casi d'uso, il server riceve informazioni che descrivono il profilo di
un individuo, e in particolare riceve un'insieme di stringhe, una delle quali rappresenta
la descrizione semantica codificata in XML, già vista nel precedente capitolo. La prima
operazione che quindi sarà compiuta in ogni caso, è la conversione di questa struttura
XML in un'ontologia in OWL-DL contenente un individuo opportunamente descritto. Il
compito è svolto da una funzione iterativa che naviga attraverso l'albero xml e definisce
una lista di classi, proprietà e restrizioni. L'individuo non sarà altro che un'istanza della
classe data dall'intersezione di tutti questi concetti.
5.6.1 Struttura del Database
I profili dei candidati vengono memorizzati in un database appositamente creato.
Il DBMS usato è MySQL, noto oltre che per la sua facilità di utilizzo, anche per il fatto
di essere open source, e quindi gratuito.
93
Il database creato prende il nome di HumanResources e contiene una sola
tabella:
18.5: Schema SQL della tabella People
5.6.2 Inserimento di un profilo nel sistema
Nel caso in cui l'utente accede come 'candidato' al sistema, esso inserisce i suoi
dati nel form e crea la descrizione semantica della sua formazione professionale
94
CREATE TABLE `HumanResources`.`People` (
`name` varchar(30) NOT NULL,
`surname` varchar(30) NOT NULL,
`gender` char(6) default NULL,
`age` int(10) unsigned NOT NULL,
`phone` varchar(15) default NULL,
`mail` varchar(20) default NULL,
`nationality` char(2) default NULL,
`street` varchar(200) default NULL,
`number` varchar(5) default NULL,
`city` varchar(10) default NULL,
`postalCode` varchar(10) default NULL,
`country` char(2) default NULL,
`minPay` char(5) default NULL,
`relocation` char(5) default NULL,
`travel` char(5) default NULL,
`military` char(5) default NULL,
`contractType` varchar(30) default NULL,
`positionStatus` varchar(15) default NULL,
`filename` varchar(300) default NULL,
`owlprofile` longtext NOT NULL,
PRIMARY KEY (`name`,`surname`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
nell'apposito pannello. Infine, premendo “Submit”, i dati vengono inviati come stringhe
al server il quale, dopo aver convertito la stringa Xml in Owl, e dopo averla formattata
(con la funzione escapeChar) in modo da poter essere scritta nel DB, chiama la
funzione ToDB. Questa, avvalendosi delle librerie MySQL-Connector, alloca il driver
per la connessione al database e crea una connessione al DB HumanResources,
utilizzando l'account gwtapp:password, precedentemente creato dall'amministratore del
sistema. Infine inserisce i dati in un nuovo record eseguendo il comando SQL:
19.5: Comando SQL per l'inserimento di un nuovo profilo nel database.
Se l'inserimento nel DB non va a buon fine, anche il file del curriculum
eventualmente caricato, viene cancellato.
5.6.3 Ricerca del candidato ideale
Se l'utente accede al sistema come 'azienda', gli verrà chiesto di inserire alcuni
dati testuali più la descrizione semantica del profilo richiesto per un dato compito
lavorativo. Questo insieme di dati verrà utilizzato per ricercare il candidato ideale
utilizzando congiuntamente le tecniche tipiche dei database e i metodi di inferenza non
standard visti nel secondo capitolo.
95
INSERT INTO People VALUES (name, surname, gender, eta, phone, mail, nationality, street, n, city, postalCode, country, minPay, relocation, travel, military, contractType, positionStatus, filename, owlProfile);
I dati quindi sono inviati alla funzione sendCompany, che converte la
descrizione semantica XML ricevuta dal client prima in OWL e poi in DIG. DIG [4] è
un linguaggio basato su XML sviluppato con lo scopo di fornire un'interfaccia comune
ai sistemi di ragionamento. Il ragionatore MaMaS (http://sisinflab.poliba.it/MAMAS-
tng/) implementa un'interfaccia DIG 1.1 modificata, ed è accessibile utilizzando le
apposite librerie DIG4J messe a punto dal SisInfLab.
20.5: Procedura per la ricerca del candidato. La chiamata dell'algoritmo di match avviene all'interno della
funzione Query
Prima di poter essere correttamente utilizzato, il documento DIG necessita di
una fase di pre-processing mirata all'eliminazione delle definizioni di concetti e
proprietà, per contenere esclusivamente l'individuo ed evitare di entrare in conflitto con
le definizioni già presenti nella Knowledge Base.
96
21.5: Algoritmo purgeConcepts: elimina dalla descrizione DIG le definizioni di concetti e proprietà
Al termine di queste operazioni viene chiamata la funzione Query, che dopo aver
stabilito la connessione al database, formula una query SQL in base ai dati specificati
nella richiesta. Se ad esempio il campo “country” è vuoto, la clausola WHERE della
query non conterrà country=””, ma non conterrà affatto l'indicazione del Paese di
residenza. Inoltre, cercando i candidati aventi country=”US”, vengono restituiti anche i
candidati con country=””, che cioè non hanno specificato il Paese di residenza. Questa
è un' estensione dell'assunzione di “mondo aperto”, nel quale cioè la non definizione di
un elemento non implica di necessità la sua negazione.
97
22.5: Creazione dinamica della query al database dei profili
A questo punto, il set di risultati ottenuti è inviato alla funzione OneToOne, che
implementa l'algoritmo di match uno a uno descritto nel capitolo su Description Logics.
private Map OneToOne(DIGDocument demandProfile, ResultSet profiles) {
int i; MAMASReasoner mamas = new MAMASReasoner(); // Connessione e caricamento Knowledge Base URL rURL = new URL("http","dee227.poliba.it",8080,"/MAMAS-tng/DIG"); mamas.setReasonerURL(rURL); System.out.println("Connesso a MAMAS. Identifier:\n " + mamas.identify() ); DIGDocument skillAxioms = OWL2DIG(model); mamas.setLogFileName("MaMaS_Log.txt"); mamas.newKB(); System.out.println("KB URI: " + mamas.getKBURI()); mamas.send(skillAxioms); //invio l'ontologia System.out.println("Invio: SkillAxioms....OK"); mamas.send(demandProfile); //invio il profilo richiesto System.out.println("Invio: Demand....OK"); profiles.last(); int dim = profiles.getRow(); System.out.println(dim + " profili da inviare"); for(i=1; i<=dim; i++) { //invio i candidati estratti dal DB
profiles.absolute(i);DIGDocument profile =
purgeConcepts(OWL2DIG(profiles.getString("owlprofile")));mamas.send(profile);System.out.println("Invio: Supply ["+ i +"]....OK");
} // Processo di match int choosen = 0; float score = 0; Concept best = null; Individual T = new Individual("requested_profile"); float Umin = Float.POSITIVE_INFINITY; float N = mamas.rankPotential(T, new Top()); Concept Hfin = new Top(); Concept Gfin = new Top(); for(i=0; i<dim; i++) {
Concept Hi = new Top();Concept Gi = new Top();profiles.absolute(i+1);String name =
profiles.getString("name")+"_"+profiles.getString("surname");Individual Pi = new Individual(name);Concept Ki = T.createCopy();Concept[] GK = new Concept[2];try {
mamas.coverVerify(T, Pi);} catch (CoverVerifyException e1) {
GK = mamas.contract(T, Pi);Gi = GK[0];
98
Ki = GK[1];}try {
Hi = mamas.abduce(Ki, Pi);} catch (SubsumptionException e) {
Hi = new Top();}float k = mamas.rankPotential(Ki, new Top());float h = mamas.rankPotential(Pi, Ki);float g = mamas.rankPotential(T, Ki);float U = Math.abs(1 - N/(N-g) * (1-h/k));System.out.println(U);if(U < Umin) {
Umin = U;Hfin = Hi;Gfin = Gi;int bias = mamas.rankPotential(Ki, Pi);score = 100*(1f - (float)bias/N);choosen = i;
}profiles.absolute(choosen +1);best =
mamas.getIndividualDescription(profiles.getString("name")+"_"+profiles.getString("surname"));
}Map risultato = packResult(......);mamas.releaseKB();return risultato;
}
23.5: Implementazione in Java dell'algoritmo di match One-to-one
I dati relativi al candidato scelto vengono inseriti in un oggetto Map che verrà
restituito al client ripercorrendo a ritroso la catena di funzioni invocate. I dati saranno
quindi disponibili al client in formato testo e DIG/XML, come nei casi del profilo del
candidato, del concetto G (rinuncia, concetti in conflitto che sono stati rimossi) e del
concetto H, che è il risultato dell' abduction e descrive i requisiti che il candidato
dovrebbe acquisire per soddisfare completamente la richiesta. Il client quindi costruisce
un pannello riassuntivo delle caratteristiche del candidato, nel quale saranno risportati
sotto forma di albero la descrizione semantica dell'individuo, le compentenze mancanti
e i requisiti che si sono dovuti eliminare in fase di contrazione.
99
23.5: Pannello di presentazione dei risultati – esempio
6 Comportamento del sistema
In questo capitolo si espone in dettaglio il comportamento del sistema nella sua
parte fondamentale: il match uno a uno. Per farlo si analizzeranno alcuni esempi pratici,
in ognuno dei quali saranno dati un certo numero di candidati e un unico task, e si vedrà
se i risultati forniti rispecchiano quelli attesi. E' evidente che per un numero molto
ristretto di profili esaminati, come nel nostro caso, il risultato è deducibile in poco
tempo anche da un operatore umano. Lo scopo di un sistema basato sulla semantica,
infatti, è operare in situazioni nelle quali la scelta dev'essere fatta tra un numero molto
grande di candidati, assicurando velocità e oggettività del risultato.
Esempio 1
Supponiamo che un'organizzazione abbia il problema di assegnare la
realizzazione di un dato compito sulla base dei seguenti requisiti: Ingegneri, con più di
100
due anni di esperienza, capacità di leadership, coordinamento di gruppi di lavoro,
negoziazione e comunicazione. Il compito richiede conoscenza avanzata di tecnologie
per il web, ERP Systems, pianificazione e controllo dei processi. Questi requisiti
possono essere formalizzati in DL come: Engineering
advanced_knowledge.(Process_Performance_Monitoring Process_Planning Internet
_technologies ERPSystems) ( 2 has_experience).
Si supponga inoltre di dover effettuare la scelta tra i seguenti candidati:
Julia: Ingegnere dei processi, con conoscenza avanzata dei processi di business e
abilità nella coordinazione di gruppi di lavoro e nell'organizzazione di meeting. Si è
appena laureata.
Tom: Sviluppatore Web, preferisce utilizzare i linguaggi C++ e Java per la
realizzazione di applicazioni orientate al web.
Richard: Ingegnere, specializzato in ERP Systems. Ha buone capacità di leadership e di
coordinatore di team di lavoro. Ha inoltre un'esperienza di più di 3 anni.
Alison:Ingegnere gestionale, specializzata in consulenza informatica e HTML. Ha una
buona conoscenza dell'inglese e del tedesco.
I profili dei candidati sopra elencati può essere espresso in DL come segue:
101
u8
u u
u u ¸
P1 (Julia) = Process_Engineering advanced_knowledge.
Business_Operations_Optimization basic_knowledge.(TeamCoordinator
Meeting_Coordinator) (=0 has_experience)
P2 (Tom) = advanced_knowledge.(Internet_Technologies Internet_Development)
basic_knowledge.(Java C++)
P3 (Richard) = Engineering advanced_knowledge.ERPSystems
basic_knowledge.(Lead_role Team_Coordinator) ( 3
has_experience)
P4 (Alison) = Managerial_Engineering advanced_knowledge.(IT_Consulting
HTML) has_language_knowledge.(English German)
Sottoponendo questa richiesta e questo set di candidati al ragionatore, si
ottengono i seguenti valori della funzione U (utilità):
U
P1 (Julia) 0,5000
P2 (Tom) 0,7500
P3 (Richard) 0,3750
P4 (Alison) 0,8125
Tabella 1.6: Corrispondenza tra profili e valori della funzione utilità per il task T
102
u8
u 8 u
u
8 u u8
u
u8 u8
u u ¸
u8 u
u8 u
Inoltre il processo di match restituisce la spiegazione delle competenze mancanti
e dei requisiti eventualmente eliminati dalla richiesta in fase di contrazione. Per il
candidato prescelto si avrà quindi:
advanced_knowledge.(Internet_Technologies
Process_Performance_Monitoring Process_Planning)
basic_knowledge.(Negotiation_skills Communication_skills)
L'utente visualizza pertanto il seguente pannello risultati:
103
GP3 = >
HP3 =8 u
u u 8
u
1.6: Pannello di presentazione dei risultati al client nel caso trattato nell'esempio n° 1
Esempio 2
Consideriamo ora un task descritto come:
e un insieme di candidati descritti da:
-
-
-
I punteggi ottenuti sono:
U
P1 (Mario) 0,0000
P2 (John) 1,8000
P3 (Frank) 1,6667
104
T = 8basicKnowledge:(InternetUseuMarkupLanguage)
P1(Mario) =8basicKnowledge:(InternetUse uComputerGraphics)
uMasterDegree u (· 2hasExperience)
P2(John) =ManagerialEngineer u 8advancedKnowledge:
:InternetT echnologiesu 8toolsKnowledge:WebDesigning
P3(Frank) = Engineer u 8advancedKnowledge:
:(TotalQualityManagementu ClientServerProtocol u V BScript uOOP )
Tabella 2.6: corrispondenza tra profili e valori della funzione utilità per il task T
In questo caso il candidato P1 soddisfa completamente i requisiti ( ), visto
che la “distanza ontologica” tra questo e la richiesta T è nulla. Evidentemente quindi
Mario è il miglior candidato disponibile.
2.6: Pannello di presentazione dei risultati al client nel caso trattato nell'esempio n° 2
Esempio 3
Supponiamo ora di avere lo stesso set di candidati visto nell'esempio precedente,
ma di possedere anche l'informazione sulla nazionalità di ognuno. In particolare:
105
H = >
Mario IT
John US
Frank US
Tabella 3.6: schematizzazione delle nazionalità dei candidati
Il requisiti stavolta sono:
Inoltre si richiede che il candidato abbia nazionalità statunitense (US).
Quando la richiesta viene sottoposta al sistema, questo effettua una selezione
preliminare sui candidati contenuti nel DB, scartando quelli che non hanno nazionalità
americana e inviando all'algoritmo di ragionamento tutti quelli con nazionalità
americana e con nazionalità non specificata. In questo caso quindi l'algoritmo verrà
eseguito solo sui candidati John e Frank, che riporteranno i seguenti punteggi:
U
John 0,125
Frank 0,500
106
T = 8advancedKnowledge:(ClientServerProtocol u ProcessManagement)
u8toolsKnowledge:InternetDeveloping
Tabella 4.6: punteggi ottenuti dai candidati selezionati. A fronte dei 3 candidati totali, quelli analizzati dal
ragionatore sono 2 a causa del filtro a livello di query al database
Il risultato dell'eleborazione è mostrato nell'immagine seguente:
3.6: Presentazione al client dei risultati dell'elaborazione oggetto dell'esempio n° 3
Non è stata necessaria la contrazione, e come si può vedere l'unico requisito
mancante è . Il requisito sull'abilità di
processManagement è rispettato in quanto la classe ManagerialEngineer è definita
come
107
8advancedKnowledge:ClientServerProtocol
Engineering u advancedKnowledge:(ManagerialSkillu ProcessManagement)
Esempio 4
In alcuni casi è possibile che parte dei requisiti descritti nel task siano in
contrasto con la descrizione del candidato. Questo accade quando, nonostante e T
siano entrambi soddisfacibili nella TBox , la loro congiunzione non lo sia:
. In questo caso, quindi, sarà necessario risolvere un problema di Concept
Contraction (CCP), e rinunciare a parte dei requisiti formulati.
Supponiamo ad esempio di avere un candidato P definito da:
Computer_Science_Engineering advanced_knowledge.Object_Oriented_Programmi
ng ( 4 has_experience) e una richiesta T definita da: Engineering Phd ( 5
has_experience).
L'algoritmo di match in questo caso restituisce:
G = 5 has_experience
H = Phd
Si è così rinunciato al requisito sull'esperienza.
108
Pi
T
u8
u · u u ¸
¸
Pi u T v?
4.6: Presentazione dei risultati. Un'esempio di Concept Contraction
7 Conclusioni
E' stato realizzato un portale web “bilaterale” dedicato a lavoratori e ad aziende,
tramite il quale i primi possono inviare al sistema un insieme di informazioni
comprendente dati anagrafici, descrizione semantica della formazione culturale e
professionale e il proprio curriculum vitae in formato testuale, mentre i secondi possono
sottoporre al sistema i requisiti richiesti per lo svolgimento di un compito lavorativo e
trovare il candidato che meglio soddisfa le specifiche.
109
In aggiunta ai dati strutturati e alla descrizione semantica, è stata prevista la
possibilità per ogni candidato di inserire nel sistema anche un file contenente il suo
curriculum completo in formato testo.
L'interfaccia grafica è stata realizzata in AJAX rispettando lo schema richiesto
dallo Staffing Exchange Protocol del consorzio HR-XML, e il suo comportamento è
paragonabile a quello delle applicazioni desktop tradizionali.
E' stata implementata, inoltre, una procedura di ricerca del candidato che
combina efficacemente, sotto un'ipotesi di mondo aperto, le tecniche di selezione
tipiche delle basi di dati e le capacità inferenziali del ragionatore MaMaS-tng. Il
processo si articola quindi in una prima fase nella quale, basandosi sui dati strutturati
contenuti nei profili, si ottiene un sottoinsieme dei candidati registrati nel database, e in
una seconda fase nella quale le descrizioni semantiche estratte dai profili così ottenuti
vengono inviate al ragionatore, che restituisce al client il candidato più idoneo a
soddisfare i requisiti.
In fase di test, al sistema sono stati sottoposti un certo numero di problemi di
match, con demand e supply appropriati, e i risultati ottenuti sono risultati concordi alle
previsioni.
Infine , si può affermare che tutti gli obiettivi prefissati in fase di definizione di
questo lavoro di tesi siano stati raggiunti.
7.1 Sviluppi futuri
110
In futuro il sistema potrebbe essere ampliato e migliorato in diversi modi, o
integrato in un sistema più ampio. Gli obiettivi più ambiziosi potrebbero essere costituiti
dall'implementazione dell'algoritmo di match molti-ad-uno, per la composizione di team
in grado di soddisfare requisiti complessi. Se infatti non si riesce a trovare un singolo
candidato che copra in maniera soddisfacente i requisiti, la cosa più logica sembra
cercare un insieme di candidati che, formando un team, coprano al meglio le specifiche.
Potrebbe poi essere aggiunto al sistema un algoritmo che, in base alle
conoscenze mancanti al candidato in funzione di un certo task, elabori, sulla base di
un'ontologia di learning objects, un percorso di e-learning ad-hoc che porti il candidato
a soddisfare pienamente i requisiti nel minimo tempo possibile.
Un ulteriore miglioramento, più pionieristico rispetto a quelli descritti finora,
sarebbe l'estrazione automatica da testo della descrizione semantica individuale. In
questo modo il candidato non avrebbe più l'onere di costruire manualmente la propria
descrizione, ma questa verrebbe elaborata automaticamente sulla base del curriculum
testuale inviato al sistema.
Altri sviluppi futuri potrebbero prevedere:
• estensione delle ontologie:
• miglioramento dell'interfaccia grafica;
• accesso al sistema con identificazione dell'utente e creazione di un archivio delle
ricerche da esso svolte, se questi rappresenta un'azienda;
• sistema di approvazione dei curriculum da parte degli amministratori;
111
Bibliografia
[1] S. Colucci, T. Di Noia, E. Di Sciascio, F. M. Donini, A. Ragone – Semantic-based
skill management for automated task assignment and courseware composition.
[2] S. Colucci, T. Di Noia, E. Di Sciascio, F. M. Donini, A. Ragone – Integrated
semantic-based composition of skills and learning needs in knowledge intensive
organizations.
[3] T. Di Noia, E. Di Sciascio, F. M. Donini, M. Mongiello – A system for principled
matchmaking in an electronic marketplace
112
[4] SisInfLab - Extended MaMaS DIG Description Logic Interface 1.1
[5] Steven Holzner – Inside XML
[6] R. Hanson, A. Tacy – GWT in Action, easy AJAX with Google Web Toolkit
[7] Prabhakar Chaganti - GWT Java Ajax Programming
[8] M. Smith, C. Welty, D. McGuinness - Owl Web Ontology Language Guide
[9] K. Bartkus – Staffing Exchange Protocol – Reccomendation, 2006 Feb 28
[10] Jena 2 Ontology API - http://jena.sourceforge.net/ontology/index.html
[11] F.Baader, D. Calvanese, D. McGuinness, D. Nardi, P. Patel-Schneider - The
Description Logic Handbook - Theory, Implementation and Applications
Appendice
Si riporta ora il codice sorgente di alcune classi, corrispondenti alle parti salienti
dell'interfaccia grafica e alle servlet realizzate.
Candidate_Side.java
113
package com.mycompany.project.client;
import com.google.gwt.core.client.GWT;
public class Candidate_Side extends Composite {
public CompanyInfos company = new CompanyInfos(); public TabLocation2 tabLocation = new TabLocation2(); public JobPosition job = new JobPosition(); public TabCompetency tabCompetency = new TabCompetency(); public RemunerationPackage remunerationPackage = new RemunerationPackage(); public OtherInfos_candidate others = new OtherInfos_candidate(); public PersonalData_Candidate personal = new PersonalData_Candidate(); public Semantic semantic = new Semantic(); private FlexTable table = new FlexTable(); private StackPanel stackPanel = new StackPanel(); private ScrollPanel scrollCompany = new ScrollPanel(); private ScrollPanel scrollLocation = new ScrollPanel(); private ScrollPanel scrollJob = new ScrollPanel(); private ScrollPanel scrollCompetency = new ScrollPanel(); private ScrollPanel scrollRemuneration = new ScrollPanel(); private ScrollPanel scrollOthers = new ScrollPanel(); private ScrollPanel scrollPersonal = new ScrollPanel(); private HorizontalPanel buttonsPanel = new HorizontalPanel(); private Button submit = new Button("Submit"); public Candidate_Side() { initWidget(table); table.setWidget(0, 0, stackPanel); stackPanel.setSize("809px", "450px"); stackPanel.setStyleName("gwt-StackPanel .gwt-StackPanelItem");
//SLIDE #0 stackPanel.add(scrollCompany, "<img src=\"company.png\"> Company Infos", true); scrollCompany.setSize("800px", "400px"); scrollCompany.add(company); company.setSize("100%", "100%"); //SLIDE #1 (DATI PERSONALI) stackPanel.add(scrollPersonal, "<img src=\"personal.png\"> Personal Data & Availability", true); scrollPersonal.add(personal); personal.setSize("100%", "100%"); scrollPersonal.setSize("100%", "400px"); //SLIDE (DESCRIZIONE SEMANTICA) stackPanel.add(semantic, "<img src=\"gear.png\"> Semantic Description", true); //semantic.setSize("100%", "100%"); //SLIDE #2 stackPanel.add(scrollLocation, "<img src=\"location.png\"> Locations", true); scrollLocation.add(tabLocation); tabLocation.setSize("100%","100%"); scrollLocation.setSize("100%", "400px"); //SLIDE #3 stackPanel.add(scrollJob, "<img src=\"job.png\"> Job detail & Shifts", true);
114
scrollJob.add(job); job.setSize("100%", "100%"); scrollJob.setSize("100%", "400px"); //SLIDE #4 stackPanel.add(scrollCompetency, "<img src=\"competencies.png\"> Competencies", true); scrollCompetency.add(tabCompetency); tabCompetency.setSize("100%", "100%"); scrollCompetency.setSize("100%", "400px"); //SLIDE #5 stackPanel.add(scrollRemuneration, "<img src=\"remuneration.png\"> Remuneration Package", true); scrollRemuneration.add(remunerationPackage); remunerationPackage.setSize("100%", "100%"); scrollRemuneration.setSize("100%", "400px"); //SLIDE #6 stackPanel.add(scrollOthers, "<img src=\"others.png\"> Other informations", true); scrollOthers.add(others); others.setSize("100%","100%"); scrollOthers.setSize("100%", "400px"); table.setWidget(1, 0, buttonsPanel); buttonsPanel.add(submit);
submit.addClickListener(new ClickListener() { public void onClick(Widget sender) { if(validate()) { Loading load = new Loading(); final PopupPanel pop = new PopupPanel(); final SendDataServletAsync sendservice = (SendDataServletAsync)GWT.create(SendDataServlet.class); ServiceDefTarget endpoint = (ServiceDefTarget) sendservice; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL()+"send"); AsyncCallback callback = new AsyncCallback() { public void onFailure(Throwable caught) { pop.hide(); // in caso di fallimento, anche il file caricato dev'essere cancellato personal.fancyFileUpload.uploadForm.setEncoding(FormPanel.ENCODING_URLENCODED); personal.fancyFileUpload.deleteFiles(); Window.alert("Error sending data"); } public void onSuccess(Object result) { pop.hide(); Window.alert("Data successfully sent"); } }; String fileName = personal.fancyFileUpload.fileName; if(fileName.startsWith("<pre>")) fileName = fileName.substring(5, fileName.lastIndexOf('<')); sendservice.sendCandidate(personal.name.getText(), personal.surname.getText(), personal.gender.getItemText(personal.gender.getSelectedIndex()),
115
personal.age.getText(), personal.phone.getText(), personal.email.getText(), personal.nationality.getItemText(personal.nationality.getSelectedIndex()), personal.street.getText(), personal.number.getText(), personal.city.getText(), personal.postalCode.getText(), personal.country.getItemText(personal.country.getSelectedIndex()), Boolean.toString(personal.minimumPay.isChecked()), Boolean.toString(personal.relocation.isChecked()), Boolean.toString(personal.travel.isChecked()), Boolean.toString(personal.military.isChecked()), personal.contract.getItemText(personal.contract.getSelectedIndex()), personal.position.getItemText(personal.position.getSelectedIndex()), fileName, semantic.xmlprofile, callback); pop.setPopupPosition(Window.getClientWidth()/2, Window.getClientHeight()/2); pop.setWidget(load); pop.show(); } } }); } private boolean validate() { //VALIDATE - controllo dei campi (obbligatori solo nome, cognome, sesso e profilo semantico) //i campi non specificati, per sicurezza, li imposto a "" if(personal.name.getText().equals("") || personal.name.getText()==null) { Window.alert("Insert your name"); return false; } if(personal.surname.getText().equals("") || personal.surname.getText()==null) { Window.alert("Insert your surname"); return false; } if(semantic.xmlprofile.equals("") || semantic.xmlprofile==null) { Window.alert("Edit your profile\nGo to the semantic panel"); return false; } if(personal.age.getText()==null) personal.age.setText("0"); if(personal.phone.getText()==null) personal.phone.setText(""); if(personal.email.getText()==null) personal.email.setText(""); if(personal.nationality.getSelectedIndex()==-1) personal.nationality.setSelectedIndex(0); if(personal.street.getText()==null) personal.street.setText(""); if(personal.number.getText()==null) personal.number.setText(""); if(personal.city.getText()==null) personal.city.setText(""); if(personal.postalCode.getText()==null) personal.postalCode.setText(""); if(personal.country.getSelectedIndex()==-1) personal.country.setSelectedIndex(0); if(personal.contract.getSelectedIndex()==-1) personal.contract.setSelectedIndex(0); if(personal.position.getSelectedIndex()==-1) personal.position.setSelectedIndex(0); return true; }}
116
Company_Side.java
package com.mycompany.project.client;
import java.util.Map;
public class Company_Side extends Composite {
public CompanyInfos company = new CompanyInfos(); public TabLocation2 tabLocation = new TabLocation2(); public JobPosition job = new JobPosition(); public TabCompetency tabCompetency = new TabCompetency(); public RemunerationPackage remunerationPackage = new RemunerationPackage(); public OtherInfos_company others = new OtherInfos_company(); public PersonalData_Company personal = new PersonalData_Company(); public Semantic semantic = new Semantic(); private FlexTable table = new FlexTable(); private StackPanel stackPanel = new StackPanel(); private ScrollPanel scrollCompany = new ScrollPanel(); private ScrollPanel scrollLocation = new ScrollPanel(); private ScrollPanel scrollJob = new ScrollPanel(); private ScrollPanel scrollCompetency = new ScrollPanel(); private ScrollPanel scrollRemuneration = new ScrollPanel(); private ScrollPanel scrollOthers = new ScrollPanel(); private ScrollPanel scrollPersonal = new ScrollPanel(); private HorizontalPanel buttonsPanel = new HorizontalPanel(); private Button submit = new Button("Submit"); public Company_Side() { initWidget(table); table.setWidget(0, 0, stackPanel); stackPanel.setSize("809px", "450px"); stackPanel.setStyleName("gwt-StackPanel .gwt-StackPanelItem");
//SLIDE #0 stackPanel.add(scrollCompany, "<img src=\"company.png\"> Company Infos", true); scrollCompany.setSize("800px", "400px"); scrollCompany.add(company); company.setSize("100%", "100%");
//SLIDE #1 (DATI PERSONALI) stackPanel.add(scrollPersonal, "<img src=\"personal.png\"> Personal Data & Availability", true); scrollPersonal.add(personal); personal.setSize("100%", "100%"); scrollPersonal.setSize("100%", "400px"); //SLIDE (DESCRIZIONE SEMANTICA) stackPanel.add(semantic, "<img src=\"gear.png\"> Semantic Description", true); //semantic.setSize("100%", "100%"); //SLIDE #2 stackPanel.add(scrollLocation, "<img src=\"location.png\"> Locations", true); scrollLocation.add(tabLocation); tabLocation.setSize("100%","100%"); scrollLocation.setSize("100%", "400px"); //SLIDE #3
117
stackPanel.add(scrollJob, "<img src=\"job.png\"> Job detail & Shifts", true); scrollJob.add(job); job.setSize("100%", "100%"); scrollJob.setSize("100%", "400px"); //SLIDE #4 stackPanel.add(scrollCompetency, "<img src=\"competencies.png\"> Competencies", true); scrollCompetency.add(tabCompetency); tabCompetency.setSize("100%", "100%"); scrollCompetency.setSize("100%", "400px"); //SLIDE #5 stackPanel.add(scrollRemuneration, "<img src=\"remuneration.png\"> Remuneration Package", true); scrollRemuneration.add(remunerationPackage); remunerationPackage.setSize("100%", "100%"); scrollRemuneration.setSize("100%", "400px"); //SLIDE #6 stackPanel.add(scrollOthers, "<img src=\"others.png\"> Other informations", true); scrollOthers.add(others); others.setSize("100%","100%"); scrollOthers.setSize("100%", "400px"); table.setWidget(1, 0, buttonsPanel); buttonsPanel.add(submit); submit.addClickListener(new ClickListener() { public void onClick(Widget sender) { if(validate()) { Loading load = new Loading(); final PopupPanel pop = new PopupPanel(); final SendDataServletAsync sendservice = (SendDataServletAsync)GWT.create(SendDataServlet.class); ServiceDefTarget endpoint = (ServiceDefTarget) sendservice; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL()+"send"); AsyncCallback callback = new AsyncCallback() { public void onFailure(Throwable caught) { Window.alert("Error sending data"); } public void onSuccess(Object result) { Map candidate = (Map)result; if(candidate.containsKey("error")) { pop.hide(); Window.alert((String)candidate.get("error")); } else { pop.hide(); DrawResultPanel(candidate); } } }; sendservice.sendCompany(personal.gender.getItemText(personal.gender.getSelectedIndex()), personal.age.getText(), personal.nationality.getItemText(personal.nationality.getSelectedIndex()),
118
Boolean.toString(personal.minimumPay.isChecked()), Boolean.toString(personal.relocation.isChecked()), Boolean.toString(personal.travel.isChecked()), Boolean.toString(personal.military.isChecked()), personal.contract.getItemText(personal.contract.getSelectedIndex()), personal.position.getItemText(personal.position.getSelectedIndex()), semantic.xmlprofile, callback); pop.setPopupPosition(Window.getClientWidth()/2, Window.getClientHeight()/2); pop.setWidget(load); pop.show(); } } }); } private void DrawResultPanel(Map candidate) { //costruisco gli alberi Profile, H, G Tree candidateTree = new Tree(); candidateTree.addItem(XMLDIG2Tree((String)candidate.get("candidate"))); candidateTree.getItem(0).setState(true); candidateTree.setImageBase("tree/"); Tree giveUpTree = new Tree(); giveUpTree.addItem(XMLDIG2Tree((String)candidate.get("G"))); giveUpTree.getItem(0).setState(true); giveUpTree.setImageBase("tree/"); Tree hTree = new Tree(); hTree.addItem(XMLDIG2Tree((String)candidate.get("H"))); hTree.getItem(0).setState(true); hTree.setImageBase("tree/"); //inserisco le informazioni del lavoratore nel pannello ResultPanel pan = new ResultPanel(); pan.nameSurname.setText((String)candidate.get("name") + " " + (String)candidate.get("surname")); pan.age.setText((String)candidate.get("age")); pan.city.setText((String)candidate.get("city")); pan.contractType.setText((String)candidate.get("contractType")); pan.country.setText((String)candidate.get("country")); pan.NomeFile = (String)candidate.get("filename"); if(pan.NomeFile.equals("uploads/") || pan.NomeFile.equals("") || pan.NomeFile==null) { pan.image.setVisible(false); pan.downloadLabel.setVisible(false); } pan.gender.setText((String)candidate.get("gender")); pan.mail.setText((String)candidate.get("mail")); pan.military.setText((String)candidate.get("military")); pan.minPay.setText((String)candidate.get("minPay")); pan.nationality.setText((String)candidate.get("nationality")); pan.number.setText((String)candidate.get("number")); pan.phone.setText((String)candidate.get("phone")); pan.positionStatus.setText((String)candidate.get("positionStatus")); pan.postalCode.setText((String)candidate.get("postalCode")); pan.relocation.setText((String)candidate.get("relocation")); pan.street.setText((String)candidate.get("street")); pan.travel.setText((String)candidate.get("travel")); pan.profileScroll.setWidget(candidateTree); pan.Hscroll.setWidget(hTree); pan.Gscroll.setWidget(giveUpTree); //svuoto la tabella e vi inserisco il pannello del lavoratore table.clear(); table.setWidget(0, 0, pan); } private TreeItem XMLDIG2Tree(String profile) {
119
Document doc = XMLParser.parse(profile); Element top = doc.getDocumentElement(); TreeItem root = new TreeItem("Thing"); if(top.getTagName().equals("catom")) root.addItem(top.getAttribute("name")); if(top.getTagName().equals("all")) { TreeItem prop = new TreeItem(); NodeList list = top.getChildNodes(); for(int j=0; j<list.getLength(); j++) { if(list.item(j).getNodeType()==Node.ELEMENT_NODE) { Element subel = (Element)list.item(j); if(subel.getTagName().equals("ratom")) prop.setText(subel.getAttribute("name")); if(subel.getTagName().equals("catom")) prop.addItem(subel.getAttribute("name")); if(subel.getTagName().equals("and")) { NodeList list2 = subel.getChildNodes(); for(int k=0; k<list2.getLength(); k++) { if(list2.item(k).getNodeType()==Node.ELEMENT_NODE) { Element elem2 = (Element)list2.item(k); if(elem2.getTagName().equals("catom")) prop.addItem(elem2.getAttribute("name")); if(elem2.getTagName().equals("atleast") || elem2.getTagName().equals("atmost")) { String value = elem2.getAttribute("num"); String sign = new String(); if(elem2.getTagName().equals("atmost")) sign = "<"; else sign = ">"; String name = new String(); NodeList nlist2 = elem2.getChildNodes(); for(int m=0; m<nlist2.getLength(); m++) if(nlist2.item(m).getNodeType()==Node.ELEMENT_NODE) { Element e2 = (Element)nlist2.item(m); if(e2.getTagName().equals("ratom")) name = e2.getAttribute("name"); } prop.addItem(sign + value + " " + name); } } } } } } root.addItem(prop); } if(top.getTagName().equals("and")) { NodeList children = top.getChildNodes(); if(children != null) { children = top.getChildNodes();
120
for(int i=0; i<children.getLength(); i++) { if(children.item(i).getNodeType()==Node.ELEMENT_NODE) { Element elem = (Element)children.item(i); if(elem.getTagName().equals("catom")) root.addItem(elem.getAttribute("name")); if(elem.getTagName().equals("atleast") || elem.getTagName().equals("atmost")) { String value = elem.getAttribute("num"); String sign = new String(); if(elem.getTagName().equals("atmost")) sign = "<"; else sign = ">"; String name = new String(); NodeList nlist = elem.getChildNodes(); for(int l=0; l<nlist.getLength(); l++) if(nlist.item(l).getNodeType()==Node.ELEMENT_NODE) { Element e = (Element)nlist.item(l); if(e.getTagName().equals("ratom")) name = e.getAttribute("name"); } root.addItem(sign + value + " " + name); } if(elem.getTagName().equals("all")) { TreeItem prop = new TreeItem(); NodeList list = elem.getChildNodes(); for(int j=0; j<list.getLength(); j++) { if(list.item(j).getNodeType()==Node.ELEMENT_NODE) { Element subel = (Element)list.item(j); if(subel.getTagName().equals("ratom")) prop.setText(subel.getAttribute("name")); if(subel.getTagName().equals("catom")) prop.addItem(subel.getAttribute("name")); if(subel.getTagName().equals("and")) { NodeList list2 = subel.getChildNodes(); for(int k=0; k<list2.getLength(); k++) { if(list2.item(k).getNodeType()==Node.ELEMENT_NODE) { Element elem2 = (Element)list2.item(k); if(elem2.getTagName().equals("catom")) prop.addItem(elem2.getAttribute("name"));
121
if(elem2.getTagName().equals("atleast") || elem2.getTagName().equals("atmost")) { String value = elem2.getAttribute("num"); String sign = new String(); if(elem2.getTagName().equals("atmost")) sign = "<"; else sign = ">"; String name = new String(); NodeList nlist2 = elem2.getChildNodes(); for(int m=0; m<nlist2.getLength(); m++) if(nlist2.item(m).getNodeType()==Node.ELEMENT_NODE) { Element e2 = (Element)nlist2.item(m); if(e2.getTagName().equals("ratom")) name = e2.getAttribute("name"); } prop.addItem(sign + value + " " + name); } } } } } } root.addItem(prop); } } } } } return root; } private boolean validate() { //VALIDATE - controllo dei campi (obbligatori solo nome, cognome, sesso e profilo semantico) //i campi non specificati, per sicurezza, li imposto a "" if(semantic.xmlprofile.equals("") || semantic.xmlprofile==null) { Window.alert("Edit the requested profile\nGo to the semantic panel"); return false; } if(personal.age.getText()==null) personal.age.setText("0"); if(personal.gender.getSelectedIndex()==-1) personal.gender.setSelectedIndex(0); if(personal.nationality.getSelectedIndex()==-1) personal.nationality.setSelectedIndex(0); if(personal.contract.getSelectedIndex()==-1) personal.contract.setSelectedIndex(0); if(personal.position.getSelectedIndex()==-1) personal.position.setSelectedIndex(0); return true; }
122
}
Semantic.java
package com.mycompany.project.client;
import java.util.HashMap;import java.util.Iterator;import java.util.Map;
import com.google.gwt.core.client.GWT;import com.google.gwt.user.client.Window;import com.google.gwt.user.client.rpc.AsyncCallback;import com.google.gwt.user.client.rpc.ServiceDefTarget;import com.google.gwt.user.client.ui.AbsolutePanel;import com.google.gwt.user.client.ui.Button;import com.google.gwt.user.client.ui.ClickListener;import com.google.gwt.user.client.ui.Composite;import com.google.gwt.user.client.ui.FlexTable;import com.google.gwt.user.client.ui.FocusListener;import com.google.gwt.user.client.ui.HTML;import com.google.gwt.user.client.ui.HasHorizontalAlignment;import com.google.gwt.user.client.ui.Label;import com.google.gwt.user.client.ui.ListBox;import com.google.gwt.user.client.ui.ScrollPanel;import com.google.gwt.user.client.ui.TextBox;import com.google.gwt.user.client.ui.Tree;import com.google.gwt.user.client.ui.TreeItem;import com.google.gwt.user.client.ui.TreeListener;import com.google.gwt.user.client.ui.Widget;import com.google.gwt.xml.client.Document;import com.google.gwt.xml.client.Element;import com.google.gwt.xml.client.Node;import com.google.gwt.xml.client.NodeList;import com.google.gwt.xml.client.ProcessingInstruction;import com.google.gwt.xml.client.XMLParser;
public class Semantic extends Composite {
private final int SELECT_DEGREE = 0; private final int SELECT_LEVEL = 1; private final int SELECT_PROP = 2; private final int SELECT_SKILLS = 3; private int actualStep = SELECT_DEGREE; private FlexTable grid = new FlexTable(); private HTML html = new HTML("Degree"); private HTML html_1 = new HTML("Properties"); private HTML skillTitleLabel = new HTML("Select a property"); private HTML html_3 = new HTML("Summary"); private HTML html_4 = new HTML("Level"); private HTML html_5 = new HTML("Value"); private ScrollPanel scrollDegree = new ScrollPanel(); private ScrollPanel scrollLevel = new ScrollPanel(); private ScrollPanel scrollSkills = new ScrollPanel(); private ScrollPanel scrollSummary = new ScrollPanel();
123
private Tree degreeTree = new Tree(); private Tree levelTree = new Tree(); private Tree skillTree = new Tree(); private MyTree summaryTree = new MyTree(); private boolean fromSkill=false; private boolean fromValue=false; private boolean fromProp=false; private Label summaryLabel = new Label(); private FlexTable valueTable = new FlexTable(); private ListBox leastMost = new ListBox(); private TextBox valueTxt = new TextBox(); private Label insertValueLabel = new Label("Insert value"); private ListBox listProp = new ListBox(); private ListBox listProp2 = new ListBox(); private AbsolutePanel horizontalPanel = new AbsolutePanel(); private Button prevButton = new Button(); private Button nextButton = new Button(); private Button addButton = new Button(); private Map propAndRange = new HashMap(); private String description = new String(); public String xmlprofile = new String(); private boolean first = true; private final OntologyServletAsync service = (OntologyServletAsync)GWT.create(OntologyServlet.class); public Semantic() {
//definisco il servizio di collegamento alle ontologie ServiceDefTarget endpoint = (ServiceDefTarget) service; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL()+"onto");
initGUI(); loadOwls(service); loadDegreeTree(service); loadLevelTree(service); loadProperties(service); steps(); } private void initGUI() { initWidget(grid); grid.setSize("820px", "420px"); degreeTree.setImageBase("tree/"); levelTree.setImageBase("tree/"); skillTree.setImageBase("tree/"); summaryTree.setImageBase("tree/"); TreeItem rootSumm = new TreeItem("AND"); summaryTree.addItem(rootSumm); scrollSummary.add(summaryTree); skillTree.addTreeListener(new TreeListener() { public void onTreeItemSelected(TreeItem item) { fromSkill = true; valueTable.setStyleName("border2"); leastMost.setEnabled(true); valueTxt.setEnabled(true); listProp2.setVisible(true); } public void onTreeItemStateChanged(TreeItem item) { } });
124
grid.setWidget(0, 0, html); grid.getCellFormatter().setHeight(0, 0, "10px"); grid.getCellFormatter().setWidth(0, 0, "200px"); html.setStyleName("gwt-HTML2"); grid.setWidget(0, 1, html_1); grid.getCellFormatter().setWidth(0, 1, "190px"); html_1.setStyleName("gwt-HTML2"); grid.setWidget(0, 2, skillTitleLabel); grid.getCellFormatter().setWidth(0, 2, "200px"); skillTitleLabel.setStyleName("gwt-HTML2"); grid.setWidget(0, 3, html_3); grid.getCellFormatter().setWidth(0, 3, "200px"); html_3.setStyleName("gwt-HTML2"); grid.setWidget(2, 0, html_4); grid.getCellFormatter().setHeight(2, 0, "10px"); html_4.setStyleName("gwt-HTML2"); grid.setWidget(2, 1, html_5); html_5.setStyleName("gwt-HTML2"); grid.setWidget(1, 0, scrollDegree); scrollDegree.setSize("200px", "200px"); grid.getCellFormatter().setWordWrap(1, 0, false); scrollDegree.setStyleName("border"); grid.setWidget(3, 0, scrollLevel); scrollLevel.setStyleName("border"); scrollLevel.setHeight("200px"); grid.setWidget(1,2,scrollSkills); scrollSkills.setSize("200px", "100%"); scrollSkills.setStyleName("border"); grid.getFlexCellFormatter().setRowSpan(1, 2, 3); grid.setWidget(1,3,scrollSummary); scrollSummary.setSize("200px", "100%"); scrollSummary.setStyleName("border"); grid.getFlexCellFormatter().setRowSpan(1, 3, 3); //scrollSummary.setWidget(summaryLabel); grid.setWidget(3, 1, valueTable); valueTable.setSize("100%", "100%"); grid.getCellFormatter().setHeight(3, 1, "100%"); valueTable.setStyleName("border"); valueTable.setWidget(1, 0, leastMost); leastMost.addItem("="); leastMost.addItem("<"); leastMost.addItem(">"); valueTable.setWidget(1, 1, valueTxt); valueTxt.addFocusListener(new FocusListener() { public void onFocus(Widget sender) { TextBox source = (TextBox)sender; source.selectAll(); } public void onLostFocus(Widget sender) { TextBox source = (TextBox)sender; if(source.getText().length()>0) { try { Integer numero = new Integer(source.getText());
125
fromValue = true; } catch (Exception e) { Window.alert("Not an integer"); source.setText(""); } } } }); valueTxt.setWidth("140px"); valueTable.setWidget(0, 1, insertValueLabel); insertValueLabel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER); valueTable.setWidget(2, 1, listProp2); valueTable.getFlexCellFormatter().setColSpan(2, 1, 2); description = ""; listProp2.setWidth("140px"); listProp2.setVisibleItemCount(4); grid.setWidget(4, 0, horizontalPanel); horizontalPanel.setSize("100%", "28px"); horizontalPanel.setStyleName("coloured"); grid.getCellFormatter().setHorizontalAlignment(4, 0, HasHorizontalAlignment.ALIGN_RIGHT); grid.getFlexCellFormatter().setColSpan(4, 0, 4); grid.getCellFormatter().setHorizontalAlignment(4, 3, HasHorizontalAlignment.ALIGN_RIGHT); horizontalPanel.add(addButton, 370, 0); addButton.setHTML("<img src=\"add.png\"/>"); addButton.addClickListener(new ClickListener() { public void onClick(Widget sender) { buildSummaryTree(); valueTxt.setText(""); } }); horizontalPanel.add(prevButton); prevButton.setEnabled(false); prevButton.setHTML("<img src=\"back-off.png\"/>"); prevButton.addClickListener(new ClickListener() { public void onClick(Widget sender) { if(actualStep!=0) { actualStep--; steps(); } } }); horizontalPanel.add(nextButton); nextButton.setHTML("<img src=\"forward.png\"/>"); nextButton.addClickListener(new ClickListener() { public void onClick(Widget sender) { if(actualStep!=2) { actualStep++; steps(); } else { actualStep = SELECT_DEGREE; first = true; XMLProfile(); steps(); } } });
126
grid.setWidget(1, 1, listProp); listProp.addClickListener(new ClickListener() { public void onClick(Widget sender) { if(!fromValue) fromSkill=false; ListBox list = (ListBox)sender; String selected = list.getItemText(list.getSelectedIndex()); String resource = (String)propAndRange.get(selected); if(resource!=null) { loadSkillTree(resource, service); scrollSkills.setVisible(true); skillTree.setVisible(true); scrollSkills.setStyleName("border2"); leastMost.setEnabled(false); leastMost.setVisible(true); valueTxt.setEnabled(false); valueTxt.setVisible(true); insertValueLabel.setVisible(true); listProp2.setVisible(false); TreeItem node = existInTree(selected); //se la proprietà è già presente nel profilo creato if(node!=null) { //viene riabilitata la possibilità di aggiungere delle valueTable.setStyleName("border2"); //restrizioni numeriche alla proprietà leastMost.setVisible(true); valueTxt.setVisible(true); leastMost.setEnabled(true); valueTxt.setEnabled(true); listProp2.setVisible(true); valueTxt.setText(""); } } else { scrollSkills.clear(); skillTree.setVisible(false); skillTitleLabel.setHTML("Select a property"); valueTable.setStyleName("border2"); insertValueLabel.setVisible(true); leastMost.setVisible(true); valueTxt.setVisible(true); leastMost.setEnabled(true); valueTxt.setEnabled(true); listProp2.setVisible(false); valueTxt.setText(""); } } }); listProp.setSize("200px", "100%"); listProp.setVisibleItemCount(11); } private void loadOwls(OntologyServletAsync service) { AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { } public void onFailure(Throwable caught) { Iterator it = grid.iterator(); while(it.hasNext()) ((Widget)it.next()).setVisible(false); Window.alert("ERROR: Wrong or missing knowledge base!"); } }; service.loadModel(callback); //chiama la procedura remota }
127
private void loadDegreeTree(OntologyServletAsync service) { AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { Document xmlTree = XMLParser.parse((String)result); degreeTree.addItem(XML2Tree(xmlTree.getDocumentElement())); degreeTree.getItem(0).setState(true); scrollDegree.add(degreeTree); } public void onFailure(Throwable caught) { Window.alert("ERROR: degrees"); scrollDegree.setVisible(false); } }; service.loadDegTree(callback); } private void loadLevelTree(OntologyServletAsync service) { AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { Document xmlTree = XMLParser.parse((String)result); levelTree.addItem(XML2Tree(xmlTree.getDocumentElement())); levelTree.getItem(0).setState(true); scrollLevel.add(levelTree); } public void onFailure(Throwable caught) { Window.alert("ERROR: level"); scrollLevel.setVisible(false); } }; service.loadLevelTree(callback); } private void loadSkillTree(String resourceName, OntologyServletAsync service) { AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { Document xmlTree = XMLParser.parse((String)result); Element root = xmlTree.getDocumentElement(); skillTitleLabel.setHTML(root.getTagName().replace('_', ' ')); skillTree.clear(); skillTree.addItem(XML2Tree(xmlTree.getDocumentElement())); skillTree.getItem(0).setState(true); scrollSkills.setWidget(skillTree); } public void onFailure(Throwable caught) { Window.alert("ERROR: skills"); scrollSkills.setVisible(false); } }; service.loadSkTree(resourceName, callback); } private void loadProperties(OntologyServletAsync service) { AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { int i, j, imin; Document xmlTree = XMLParser.parse((String)result); Element root = xmlTree.getDocumentElement(); NodeList list = root.getChildNodes(); for(i=0; i<list.getLength(); i++) { if(list.item(i).getNodeType()==Node.ELEMENT_NODE) { listProp.addItem(((Element)list.item(i)).getTagName()); if(((Element)list.item(i)).hasAttribute("range")) propAndRange.put(((Element)list.item(i)).getTagName(), ((Element)list.item(i)).getAttribute("range"));
128
} } //ordinamento alfabetico for(i=0; i<listProp.getItemCount()-1; i++) { imin = i; for(j=i+1; j<listProp.getItemCount(); j++) if(listProp.getItemText(j).compareTo(listProp.getItemText(i))<1) imin = j; String temp = listProp.getItemText(i); listProp.setItemText(i, listProp.getItemText(imin)); listProp.setItemText(imin, temp); } for(i=0; i<listProp.getItemCount(); i++) listProp2.addItem(listProp.getItemText(i)); } public void onFailure(Throwable caught) { Window.alert("Errore: properties"); listProp.setVisible(false); listProp2.setVisible(false); } }; service.loadAProperties(callback); } private static TreeItem XML2Tree(Node node) { int i; TreeItem element = new TreeItem(((Element)node).getTagName()); NodeList children = node.getChildNodes(); if (children != null) for (i = 0; i < children.getLength(); i++) if(children.item(i).getNodeType()==Node.ELEMENT_NODE) element.addItem(XML2Tree(children.item(i))); return element; } private void buildSummaryTree() { if(actualStep==SELECT_DEGREE) { TreeItem item = new TreeItem(degreeTree.getSelectedItem().getText()); if(existInTree(item.getText())==null) { TreeItem root = summaryTree.getItem(0); root.addItem(item); summaryTree.getItem(0).setState(true); } } if(actualStep==SELECT_LEVEL) { TreeItem item = new TreeItem(levelTree.getSelectedItem().getText()); if(existInTree(item.getText())==null) { TreeItem root = summaryTree.getItem(0); root.addItem(item); summaryTree.getItem(0).setState(true); } } if (actualStep==SELECT_PROP) { String selected = listProp.getItemText(listProp.getSelectedIndex()); String resource = (String)propAndRange.get(selected); if(resource==null) { //caso di proprietà senza range if(valueTxt.getText().length()>0) { TreeItem item = new TreeItem(leastMost.getItemText(leastMost.getSelectedIndex())+valueTxt.getText()+" "+selected); TreeItem root = summaryTree.getItem(0); if(existInTree(item.getText())==null) root.addItem(item); valueTxt.setText(""); } else
129
Window.alert("Please specify a value"); } else { //se la proprietà ha un range TreeItem node = existInTree(selected); if(node==null) { //se non è già presente nel profilo if(skillTree.getSelectedItem()==null) Window.alert("Select a "+skillTitleLabel.getText()); else { TreeItem prop = new TreeItem(listProp.getItemText(listProp.getSelectedIndex())); TreeItem range = new TreeItem(skillTree.getSelectedItem().getText()); prop.addItem(range); if(valueTxt.getText().length()>0) { //se inoltre specifico un peso numerico if(listProp2.getSelectedIndex()==-1) //ma non è selezionata nessuna proprietà Window.alert("Select a property"); else { //quando invece è tutto ok String s = leastMost.getItemText(leastMost.getSelectedIndex())+valueTxt.getText()+" "+listProp2.getItemText(listProp2.getSelectedIndex()); TreeItem num = new TreeItem(s); prop.addItem(num); } } summaryTree.getItem(0).addItem(prop); } } else { //se la proprietà è stata già specificata boolean done = false; if(skillTree.getSelectedItem()!=null) { //se seleziono una classe da aggiungere TreeItem addedRange = new TreeItem(skillTree.getSelectedItem().getText()); if(!existInNode(node, addedRange.getText())) node.addItem(addedRange); done = true; } if(valueTxt.getText().length()>0) { //se seleziono anche un "peso" numerico if(listProp2.getSelectedIndex()==-1) //ma non è selezionata nessuna proprietà Window.alert("Select a property"); else { //se è tutto ok String s = leastMost.getItemText(leastMost.getSelectedIndex())+valueTxt.getText()+" "+listProp2.getItemText(listProp2.getSelectedIndex()); TreeItem addedNum = new TreeItem(s); if(!existInNode(node, s)) node.addItem(addedNum); done = true; } } if(!done) Window.alert("Nothing to do"); } } } } public void steps() { if(actualStep==SELECT_DEGREE) { degreeTree.setVisible(true); scrollDegree.setVisible(true);
130
scrollDegree.setStyleName("border2"); levelTree.setVisible(false); scrollLevel.setStyleName("border"); listProp.setEnabled(false); valueTable.setStyleName("border"); skillTree.setVisible(false); scrollSkills.setStyleName("border"); leastMost.setEnabled(false); valueTxt.setEnabled(false); listProp2.setVisible(false); prevButton.setHTML("<img src=\"back-off.png\"/>"); nextButton.setHTML("<img src=\"forward.png\"/>"); } if (actualStep==SELECT_LEVEL) { degreeTree.setVisible(false); scrollDegree.setStyleName("border"); scrollLevel.setStyleName("border2"); levelTree.setVisible(true); listProp.setEnabled(false); valueTable.setStyleName("border"); skillTree.setVisible(false); scrollSkills.setStyleName("border"); leastMost.setEnabled(false); valueTxt.setEnabled(false); listProp2.setVisible(false); prevButton.setHTML("<img src=\"back.png\"/>"); prevButton.setEnabled(true); nextButton.setHTML("<img src=\"forward.png\"/>"); } if (actualStep==SELECT_PROP) { listProp.setEnabled(true); degreeTree.setVisible(false); scrollDegree.setStyleName("border"); levelTree.setVisible(false); scrollLevel.setStyleName("border"); scrollSkills.setStyleName("border"); listProp.setSelectedIndex(0); valueTable.setStyleName("border"); String selected = listProp.getItemText(listProp.getSelectedIndex()); String resource = (String)propAndRange.get(selected); if(resource!=null) { loadSkillTree(resource, service); scrollSkills.setStyleName("border2"); skillTree.setVisible(true); Iterator it = valueTable.iterator(); valueTxt.setEnabled(false); leastMost.setEnabled(false); } else { scrollSkills.clear(); skillTree.setVisible(false); skillTitleLabel.setHTML("Select a property"); scrollSkills.setStyleName("border"); valueTable.setStyleName("border"); leastMost.setEnabled(true); valueTxt.setEnabled(true); listProp2.setVisible(false); valueTxt.setText(""); } nextButton.setHTML("<img src=\"ok.png\"/>"); } } //verifica se un elemento è già presente nell'albero riassuntivo private TreeItem existInTree(String s) { TreeItem root = summaryTree.getItem(0);
131
TreeItem found = null; for(int i=0; i<root.getChildCount(); i++) if(root.getChild(i).getText().equals(s)) found = root.getChild(i); return found; } private boolean existInNode(TreeItem node, String s) { for(int i=0; i<node.getChildCount(); i++) if(node.getChild(i).getText().equals(s)) return true; return false; } //trasforma l'albero riassuntivo in un documento XML da inviare al server //si è preferita una logica iterativa al posto di una ricorsiva in quanto //l'albero ha al massimo 2 livelli public void XMLProfile() { TreeItem root = summaryTree.getItem(0); Document description = XMLParser.createDocument(); Element profile = description.createElement("profile"); description.appendChild(profile); for(int i=0; i<root.getChildCount(); i++) { TreeItem child = root.getChild(i); String text = child.getText(); Element item = null; if(child.getChildCount()>0) { //se ha figli è per forza una proprietà (il "forAll" è sottinteso??) item = description.createElement("property"); item.setAttribute("name", text); for(int j=0; j<child.getChildCount(); j++) { TreeItem subchild = child.getChild(j); String subtext = subchild.getText(); Element subitem = null; if(subtext.startsWith("=") || subtext.startsWith("<") || subtext.startsWith(">")) { //restr. num. String restr = subtext.split(" ")[0]; String sign = restr.substring(0, 1); String value = restr.substring(1, restr.length()); String name = subtext.split(" ")[1]; subitem = description.createElement("restriction"); subitem.setAttribute("name", name); subitem.setAttribute("sign", sign); subitem.setAttribute("value", value); } else { //classe subitem = description.createElement("class"); subitem.setAttribute("name", subtext); } item.appendChild(subitem); } } else { //è una classe o una restrizione numerica if(text.startsWith("=") || text.startsWith("<") || text.startsWith(">")) { //restr. num. String restr = text.split(" ")[0]; String sign = restr.substring(0, 1); if(sign.equals("<")) sign = "lt"; if(sign.equals(">")) sign = "gt"; String value = restr.substring(1, restr.length()); String name = text.split(" ")[1]; item = description.createElement("restriction"); item.setAttribute("name", name); item.setAttribute("sign", sign);
132
item.setAttribute("value", value); } else { //classe item = description.createElement("class"); item.setAttribute("name", text); } } profile.appendChild(item); } xmlprofile = description.toString(); Window.alert("Profile successfully updated"); }}
DownloadServlet.java
package com.mycompany.project.server;
import java.io.DataInputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;
import java.net.FileNameMap;import java.net.URLConnection;
import javax.servlet.ServletContext;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;
public class DownloadServlet extends HttpServlet {
private static final long serialVersionUID = 4200425454295441313L;
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { String filename = req.getParameter("filename"); File f = new File(filename); int length = 0; ServletOutputStream op = resp.getOutputStream();
FileNameMap mimemap = URLConnection.getFileNameMap(); String MIMEtype = mimemap.getContentTypeFor(filename); resp.setContentType( (MIMEtype != null) ? MIMEtype : "application/octet-stream" ); resp.setContentLength( (int)f.length() ); resp.setHeader( "Content-Disposition", "attachement; filename=\"" + filename + "\"" );
byte[] bbuf = new byte[4096]; DataInputStream in = new DataInputStream(new FileInputStream(f));
while ((in != null) && ((length = in.read(bbuf)) != -1)) { op.write(bbuf,0,length);
133
}
in.close(); op.flush(); op.close(); }
}
UploadServlet.java
package com.mycompany.project.server;
import java.io.File;import java.io.IOException;import java.io.PrintWriter;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;
import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;import org.apache.commons.fileupload.FileItemFactory;import org.apache.commons.fileupload.FileUploadException;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;import org.apache.commons.io.FilenameUtils;
public class UploadServlet extends HttpServlet{
private static final long serialVersionUID = 5450103090519695906L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String rootDirectory = "uploads/"; boolean writeToFile = true; String returnOKMessage = "OK"; String suffix = ""; boolean isMultipart = ServletFileUpload.isMultipartContent(request);
PrintWriter out = response.getWriter();
// Create a factory for disk-based file items
if (isMultipart) { // We are uploading a file (deletes are performed by on multipart requests) FileItemFactory factory = new DiskFileItemFactory();
// Create a new file upload handler
134
ServletFileUpload upload = new ServletFileUpload(factory); // Parse the request try { List items = upload.parseRequest(request); // Process the uploaded items Iterator iter = items.iterator(); while (iter.hasNext()) { FileItem item = (FileItem) iter.next(); if (item.isFormField()) { } else { if (writeToFile) { suffix = ""; String fileName = item.getName(); if (fileName != null && !fileName.equals("")) { fileName = FilenameUtils.getName(fileName); File uploadedFile = new File(rootDirectory + fileName); //se il file esiste già cambia il nome aggiungendo un suffisso casuale //se ha un estensione, modifico solo il nome e concateno l'estensione while(uploadedFile.exists()) { suffix = Integer.toString((int)Math.round(Math.random()*10000)); if(fileName.lastIndexOf('.')!=-1) { String name = fileName.substring(0,fileName.lastIndexOf('.')); String ext = fileName.substring(fileName.lastIndexOf('.')+1, fileName.length()); fileName = name + suffix + "." + ext; } else fileName = fileName + suffix; uploadedFile = new File(rootDirectory + fileName); } try { item.write(uploadedFile); //out.print(returnOKMessage); String requestUrl = request.getRequestURL().toString(); requestUrl = requestUrl.substring(0, requestUrl.lastIndexOf('/')+1); //restituisce al client il percorso relativo del file out.print(rootDirectory + fileName); } catch (Exception e) { e.printStackTrace(); } } } else {}
135
} } } catch (FileUploadException e) { e.printStackTrace(); } } else { //Process a request to delete a file String file = request.getParameter("filePass"); //il nome del file appena salvato (ed eventualmente modificato) String fileName = FilenameUtils.getName(file); File deleteFile = new File(rootDirectory+fileName); if(deleteFile.delete()){ out.print(returnOKMessage); } } }}
OntologyServletImpl.java
package com.mycompany.project.server;
import java.io.FileInputStream;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.TreeMap;
import org.jdom.*;import org.jdom.output.Format;import org.jdom.output.XMLOutputter;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;import com.hp.hpl.jena.ontology.OntClass;import com.hp.hpl.jena.ontology.OntModel;import com.hp.hpl.jena.ontology.OntModelSpec;import com.hp.hpl.jena.ontology.OntProperty;import com.hp.hpl.jena.ontology.OntResource;import com.hp.hpl.jena.rdf.model.ModelFactory;import com.hp.hpl.jena.rdf.model.Resource;import com.hp.hpl.jena.util.iterator.ExtendedIterator;import com.hp.hpl.jena.util.iterator.Filter;import com.mycompany.project.client.OntologyServlet;
public class OntologyServletImpl extends RemoteServiceServlet implements OntologyServlet { private static final long serialVersionUID = -9043461573726526934L;
private OntModel modelDegree = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM); private OntModel modelLevel= ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM); private OntModel modelMain= ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM);
136
private boolean degreeOk = false; private boolean levelOk = false; private boolean mainOk = false; public void loadModel() { if(modelDegree.isEmpty() && modelLevel.isEmpty() && modelMain.isEmpty()) { String degreePath="ontologies/Degree.owl"; String levelPath="ontologies/Level.owl"; String mainPath="ontologies/Skill.owl"; FileInputStream fin1,fin2,fin3; try { fin1 = new FileInputStream(degreePath); modelDegree.read(fin1, null); fin1.close(); degreeOk = true; fin2 = new FileInputStream(levelPath); modelLevel.read(fin2, null); fin2.close(); levelOk = true; fin3 = new FileInputStream(mainPath); modelMain.read(fin3, null); fin3.close(); mainOk = true; } catch(Exception ex){ ex.printStackTrace(); } } }
public String loadDegTree() { do{ try { Thread.sleep(500); }catch (InterruptedException e) {} } while(!degreeOk); Iterator itRootCls = modelDegree.listHierarchyRootClasses().filterDrop( new Filter(){ public boolean accept( Object o ) { return ((Resource) o).isAnon();// || ((Resource)o).getLocalName().equals("Nothing"); } }); List rootNodes = createTreeNodes(itRootCls); //lista di Element Element root = new Element("Degree"); Iterator it2 = rootNodes.iterator(); while (it2.hasNext()) //aggiungo il contenuto alla radice root.addContent((Content)it2.next()); Document degrees = new Document(root); XMLOutputter out = new XMLOutputter(); out.setFormat(Format.getPrettyFormat()); String XMLdegTree = out.outputString(degrees); return XMLdegTree; } public String loadLevelTree() { do{ try { Thread.sleep(500); }catch (InterruptedException e) {} } while(!levelOk);
137
Iterator itRootCls = modelLevel.listHierarchyRootClasses().filterDrop( new Filter(){ public boolean accept( Object o ) { return ((Resource) o).isAnon();// || ((Resource)o).getLocalName().equals("Nothing"); } }); List rootNodes = createTreeNodes(itRootCls); //lista di Element Element root = new Element("Level"); Iterator it2 = rootNodes.iterator(); while (it2.hasNext()) //aggiungo il contenuto alla radice root.addContent((Content)it2.next()); Document level = new Document(root); XMLOutputter out = new XMLOutputter(); out.setFormat(Format.getPrettyFormat()); String XMLlevelTree = out.outputString(level); return XMLlevelTree; } public String loadSkTree(String resourceName) { do{ try { Thread.sleep(500); }catch (InterruptedException e) {} } while(!mainOk); String title=resourceName.split("#")[1]; Element root = new Element(title); OntClass classe=modelMain.getOntClass(resourceName); Iterator itClass=classe.listSubClasses(true); List rootNodes = createTreeNodes(itClass); Iterator it2 = rootNodes.iterator(); while(it2.hasNext()) root.addContent((Content)it2.next()); Document skills = new Document(root); XMLOutputter out = new XMLOutputter(); out.setFormat(Format.getPrettyFormat()); String XMLskills = out.outputString(skills); return XMLskills; } public String loadAProperties() { do{ try { Thread.sleep(500); }catch (InterruptedException e) {} } while(!mainOk); ExtendedIterator itPropt = modelMain.listObjectProperties(); Element properties = new Element("Properties"); while (itPropt.hasNext()) { OntProperty prop = (OntProperty) itPropt.next(); OntResource proprange=prop.getRange(); Element elem = new Element(prop.getLocalName()); if(proprange!=null) elem.setAttribute("range", proprange.getURI()); properties.addContent(elem); } Document prop = new Document(properties); XMLOutputter out = new XMLOutputter(); out.setFormat(Format.getPrettyFormat()); String XMLprop = out.outputString(prop); return XMLprop; }
138
public List createTreeNodes(Iterator RootCls) { Map map = new TreeMap(); while (RootCls.hasNext()) { OntClass o = (OntClass) RootCls.next(); map.put(o.getLocalName(), o); } List rootList = new ArrayList(); //lista di Element (elementi xml) Iterator a = map.values().iterator(); while (a.hasNext()){ OntClass classe = (OntClass)a.next(); String localName = classe.getLocalName(); Element next = new Element(localName); rootList.add(next); } Iterator it = map.values().iterator(); for (int i = 0; i < map.values().size(); i++) { OntClass o = (OntClass) it.next(); List sub; if (o.hasSubClass()) { sub = createTreeNodes(o.listSubClasses(true).filterDrop(new Filter() { //funzione ricorsiva public boolean accept(Object o) { return ((Resource)o).isAnon() || ((Resource)o).getLocalName().equals("Nothing"); } })); Element temp = (Element)rootList.get(i); Iterator subit = sub.iterator(); while (subit.hasNext()){ Content nodoNuovo = (Content)subit.next(); temp.addContent(nodoNuovo); } } } return rootList; }}
SendDataServletImpl.java
package com.mycompany.project.server;
import it.poliba.sisinflab.dig.Concept;import it.poliba.sisinflab.dig.DIGDocument;import it.poliba.sisinflab.dig.DIGElement;import it.poliba.sisinflab.dig.Individual;import it.poliba.sisinflab.dig.Top;import it.poliba.sisinflab.dig.reasoner.ReasonerErrorException;import it.poliba.sisinflab.dig.reasoner.mamas.CoverVerifyException;import it.poliba.sisinflab.dig.reasoner.mamas.MAMASReasoner;import it.poliba.sisinflab.dig.reasoner.mamas.SubsumptionException;import it.poliba.sisinflab.util.OWLDIGTranslator;
import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.StringReader;import java.io.StringWriter;
139
import java.io.Writer;import java.net.*;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.text.CharacterIterator;import java.text.StringCharacterIterator;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;
import org.dom4j.DocumentException;import org.dom4j.io.SAXReader;import org.dom4j.io.XMLWriter;import org.jdom.*;import org.jdom.input.SAXBuilder;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;import com.hp.hpl.jena.ontology.AllValuesFromRestriction;import com.hp.hpl.jena.ontology.IntersectionClass;import com.hp.hpl.jena.ontology.ObjectProperty;import com.hp.hpl.jena.ontology.OntClass;import com.hp.hpl.jena.ontology.OntModel;import com.hp.hpl.jena.ontology.OntModelSpec;import com.hp.hpl.jena.ontology.Restriction;import com.hp.hpl.jena.rdf.model.ModelFactory;import com.hp.hpl.jena.rdf.model.RDFList;import com.mycompany.project.client.SendDataServlet;
public class SendDataServletImpl extends RemoteServiceServlet implements SendDataServlet {
private static final long serialVersionUID = 4118960530240802976L; OntModel model = null; Map prefixes = null; String Ns = "http://www.doom-srl.com/unnamed.owl#";
public void sendCandidate(String name, String surname, String gender, String age, String phone, String mail, String nationality, String street, String n, String city, String postalCode, String country, String minPay, String relocation, String travel, String military, String contractType, String positionStatus, String filename, String xmldescription) { initModel(); String owlProfile = escapeChar(XML2OWL(name.replace(' ', '_'), surname.replace(' ', '_'), xmldescription)); ToDB(name, surname, gender, age, phone, mail, nationality, street, n, city, postalCode, country, minPay, relocation, travel, military, contractType, positionStatus, filename, owlProfile); } public Map sendCompany(String gender, String age, String nationality, String minPay, String relocation, String travel, String military, String contract, String position, String xmldescription) {
initModel();
DIGDocument demand = purgeConcepts(OWL2DIG(XML2OWL("requested", "profile", xmldescription)));
140
Map risultato = Query(demand, gender, age, nationality, minPay, relocation, travel, military, contract, position); return risultato; } private Map Query(DIGDocument demand, String gender, String age, String nation, String minPay, String relocation, String travel, String military, String contract, String position) { try { Class.forName("com.mysql.jdbc.Driver").newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } String andGender = ""; String andYears = ""; String andNation = ""; String andContract = ""; String andPosition = ""; if(gender.length()>0) andGender = " AND (gender=\'"+gender+"\' OR gender=\'\')"; if(!age.equals("0") && age.length()>0) andYears = " AND (age<"+age+")"; if(nation.length()>0) andNation = " AND (nationality=\'"+nation+"\' OR nationality=\'\')"; if(contract.length()>0) andContract = " AND (contractType=\'"+contract+"\' OR contractType=\'\')"; if(position.length()>0) andPosition = " AND (positionStatus=\'"+position+"\' OR positionStatus=\'\')"; Connection conn; String query = "SELECT * FROM People WHERE (minPay="+"\'"+minPay+"\') AND (relocation=\'" +relocation+"\') AND (travel=\'"+travel+"\') AND (military=\'"+military+"\')" + andGender + andYears + andNation + andContract + andPosition; ResultSet rs = null; try { conn = DriverManager.getConnection("jdbc:mysql://localhost/HumanResources?user=gwtapp&password=password"); Statement stmt = conn.createStatement(); rs = stmt.executeQuery(query); rs.last(); if(rs.getRow()==0) { Map m = new HashMap(); m.put("error", "No profile found"); return m; } //CHIAMO L'ALGORITMO DI MATCH //invio la richiesta e i profili selezionati all'algoritmo di match che deve RESTITUIRE 1 lavoratore Map risultato = OneToOne(demand, rs); conn.close(); return risultato; } catch (SQLException e) { System.out.println("SQLException: "+e.getMessage()); System.out.println("SQLState: "+e.getSQLState()); System.out.println("VendorError: "+e.getErrorCode());
141
e.printStackTrace(); Map m = new HashMap(); m.put("error", "Error connecting database"); return m; } } private Map OneToOne(DIGDocument demandProfile, ResultSet profiles) { int i; MAMASReasoner mamas = new MAMASReasoner(); try { // CONNESSIONE E CARICAMENTO KB URL rURL = new URL("http","dee227.poliba.it",8080,"/MAMAS-tng/DIG"); mamas.setReasonerURL(rURL); System.out.println("Connesso a MAMAS. Identifier:\n " + mamas.identify() ); DIGDocument skillAxioms = OWL2DIG(model); mamas.setLogFileName("MaMaS_Log.txt"); mamas.newKB(); System.out.println("KB URI: " + mamas.getKBURI()); mamas.send(skillAxioms); //invio l'ontologia System.out.println("Invio: SkillAxioms....OK"); mamas.send(demandProfile); //invio il profilo richiesto System.out.println("Invio: Demand....OK"); profiles.last(); int dim = profiles.getRow(); System.out.println(dim + " profili da inviare"); for(i=1; i<=dim; i++) { //invio i candidati estratti dal DB profiles.absolute(i); DIGDocument profile = purgeConcepts(OWL2DIG(profiles.getString("owlprofile"))); mamas.send(profile); System.out.println("Invio: Supply ["+ i +"]....OK"); } // PROCESSO DI MATCH int choosen = 0; float score = 0; Concept best = null; Individual T = new Individual("requested_profile"); float Umin = Float.POSITIVE_INFINITY; float N = mamas.rankPotential(T, new Top()); Concept Hfin = new Top(); Concept Gfin = new Top(); for(i=0; i<dim; i++) { Concept Hi = new Top(); Concept Gi = new Top(); profiles.absolute(i+1); String name = profiles.getString("name")+"_"+profiles.getString("surname"); Individual Pi = new Individual(name); Concept Ki = null; Concept[] GK = new Concept[2]; if(!mamas.coverVerify(T, Pi)) { GK = mamas.contract(Pi, T); Gi = GK[0]; Ki = GK[1]; } else Ki = T; // T = G AND K, se (G,K)=(TOP,T) non rinuncio a niente try {
142
Hi = mamas.abduce(Ki, Pi); } catch (SubsumptionException e) { // se i concetti si sussumono (domanda meno specifica dell'offerta) Hi = new Top(); // l'abduction non è possibile e la supply copre completamente la demand } float k = mamas.rankPotential(Ki, new Top()); float h = mamas.rankPotential(Ki, Pi); float g = mamas.rankPotential(Ki, T); float U = Math.abs(1 - N/(N-g) * (1-h/k)); System.out.println(U); if(U < Umin) { Umin = U; Hfin = Hi; Gfin = Gi; int bias = mamas.rankPotential(Ki, Pi); score = 100*(1f - (float)bias/N); choosen = i; } profiles.absolute(choosen +1); best = mamas.getIndividualDescription(profiles.getString("name")+"_"+profiles.getString("surname")); } Map risultato = packResult(profiles.getString("name"), profiles.getString("surname"), profiles.getString("gender"), profiles.getInt("age"), profiles.getString("phone"), profiles.getString("mail"), profiles.getString("nationality"), profiles.getString("street"), profiles.getString("number"), profiles.getString("city"), profiles.getString("postalCode"), profiles.getString("country"), profiles.getString("minPay"), profiles.getString("relocation"), profiles.getString("travel"), profiles.getString("military"), profiles.getString("contractType"), profiles.getString("positionStatus"), profiles.getString("filename"), score, best, Gfin, Hfin); mamas.releaseKB(); return risultato; } catch (SQLException e) { e.printStackTrace(); } catch (MalformedURLException e1) { e1.printStackTrace(); } catch (ReasonerErrorException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (CoverVerifyException e) { e.printStackTrace(); } return null; } private Map packResult(String name, String surname, String gender, int age, String phone, String email, String nationality, String street, String number, String city, String postalCode, String country, String minPay, String relocation, String travel, String military, String contractType, String positionStatus, String filename, float score, Concept best, Concept G, Concept H) { Map m = new HashMap(); m.put("name", name);
143
m.put("surname", surname); m.put("gender", gender); m.put("age", Integer.toString(age)); m.put("phone", phone); m.put("email", email); m.put("nationality", nationality); m.put("street", street); m.put("number", number); m.put("city", city); m.put("postalCode", postalCode); m.put("country", country); m.put("minPay", minPay); m.put("relocation", relocation); m.put("travel", travel); m.put("military", military); m.put("contractType", contractType); m.put("positionStatus", positionStatus); m.put("filename", filename); m.put("score", Float.toString(score)+" %"); m.put("candidate", Concepts2XML(best)); m.put("G", Concepts2XML(G)); m.put("H", Concepts2XML(H)); return m; } private String Concepts2XML(Concept c) { String xmlDig = c.asXML(); org.dom4j.io.SAXReader reader = new SAXReader(); org.dom4j.io.XMLWriter writer = new XMLWriter(); org.dom4j.Document document = null; StringReader sr = new StringReader(xmlDig); String xml = new String(); try { document = reader.read(sr); sr.close(); Writer sw = new StringWriter(); sw.write(xml); writer.setWriter(sw); writer.write(document); xml = sw.toString(); } catch (DocumentException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return xml; } private DIGDocument OWL2DIG(String owlString) { StringReader sr = new StringReader(owlString); OntModel m = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM); m.read(sr, null); sr.close(); OWLDIGTranslator translator = new OWLDIGTranslator(); translator.clearKB(false); translator.translateDomain(false); translator.translateRange(false); translator.translateNS(false); return translator.translateOWL(m, null); } private DIGDocument OWL2DIG(OntModel model) { OWLDIGTranslator translator = new OWLDIGTranslator(); translator.clearKB(true);
144
translator.translateDomain(false); translator.translateRange(false); translator.translateNS(false); return translator.translateOWL(model, null); } //elimina dai profili DIG le dichiarazioni di classi e proprietà, per non andare in conflitto con le ontologie private DIGDocument purgeConcepts(DIGDocument doc) { DIGElement tells = doc.getRootElement(); Iterator elit = tells.elementIterator(); while(elit.hasNext()) { org.dom4j.Element e = (org.dom4j.Element)elit.next(); if(e.getName().equals("defconcept") || e.getName().equals("defrole")) tells.remove(e); } return doc; } private void ToDB(String name, String surname, String gender, String age, String phone, String mail, String nationality, String street, String n, String city, String postalCode, String country, String minPay, String relocation, String travel, String military, String contractType, String positionStatus, String filename, String owlProfile) { int eta = Integer.parseInt(age); //load Driver try { Class.forName("com.mysql.jdbc.Driver").newInstance(); } catch (InstantiationException e1) { e1.printStackTrace(); } catch (IllegalAccessException e1) { e1.printStackTrace(); } catch (ClassNotFoundException e1) { e1.printStackTrace(); } //obtain a connection try { Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/HumanResources?user=gwtapp&password=password"); Statement stmt = conn.createStatement(); stmt.executeUpdate("INSERT INTO People VALUES (\""+name+"\", \""+surname+"\", \""+gender+"\", "+eta+", \""+phone+"\", \""+mail+"\", \"" +nationality+"\", \""+street+"\", \""+n+"\", \""+city+"\", \""+postalCode+"\", \""+country+"\", \""+minPay+"\", \""+relocation+"\", \"" +travel+"\", \""+military+"\", \""+contractType+"\", \""+positionStatus+"\", \""+filename+"\", \""+owlProfile+"\")"); conn.close(); } catch (SQLException e) { System.out.println("SQLException: "+e.getMessage()); System.out.println("SQLState: "+e.getSQLState()); System.out.println("VendorError: "+e.getErrorCode()); e.printStackTrace(); } } //converte il messaggio xml del client in un modello owl e restituisce la descrizione dell'individuo private String XML2OWL(String name, String surname, String xmldesc) { OntModel m = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM); m.setNsPrefixes(prefixes); //uso gli stessi NS dell'ontologia principale //parsing del documento xml
145
StringReader sr = new StringReader(xmldesc); Document documento = null; SAXBuilder saxBuilder = new SAXBuilder(); try { documento = saxBuilder.build(sr); Element profileRoot = documento.getRootElement(); //elemento radice dell'albero xml List list = profileRoot.getChildren(); Iterator it = list.iterator(); RDFList elements = m.createList(); //scorro la lista degli elementi xml while(it.hasNext()) { //e li aggiungo alla lista di RDFnodes Element e = (Element)it.next(); if(e.getName().equals("class")) { String className = e.getAttributeValue("name"); OntClass c = m.getOntClass(Ns + className); if(c==null) c = m.createClass(Ns + className); elements = elements.cons(c); } if(e.getName().equals("restriction")) { String restrName = e.getAttributeValue("name"); String restrValue = e.getAttributeValue("value"); String restrType = e.getAttributeValue("sign"); ObjectProperty p = m.getObjectProperty(Ns + restrName); if(p==null) p = m.createObjectProperty(Ns + restrName); Restriction anonR = m.createRestriction(p); if(restrType.equals("gt")) anonR = m.createMinCardinalityRestriction(null, p, Integer.parseInt(restrValue)); if(restrType.equals("lt")) anonR = m.createMaxCardinalityRestriction(null, p, Integer.parseInt(restrValue)); if(restrType.equals("=")) anonR = m.createCardinalityRestriction(null, p, Integer.parseInt(restrValue)); elements = elements.cons(anonR); } if(e.getName().equals("property")) { String propName = e.getAttributeValue("name"); ObjectProperty p = m.getObjectProperty(Ns + propName); //restriction: on property p if(p==null) p = m.createObjectProperty(Ns + propName); RDFList subelements = m.createList(); //creo una lista dei sottoelementi List sublist = e.getChildren(); //classi o restrizioni numeriche Iterator it2 = sublist.iterator(); while(it2.hasNext()) { Element e2 = (Element)it2.next(); if(e2.getName().equals("class")) { String className = e2.getAttributeValue("name"); OntClass c = m.getOntClass(Ns + className); if(c==null) c = m.createClass(Ns + className);
146
subelements = subelements.cons(c); } if(e2.getName().equals("restriction")) { String restrName = e2.getAttributeValue("name"); String restrValue = e2.getAttributeValue("value"); String restrType = e2.getAttributeValue("sign"); ObjectProperty p2 = m.getObjectProperty(Ns + restrName); if(p2==null) p2 = m.createObjectProperty(Ns + restrName); Restriction anonR = m.createRestriction(p); if(restrType.equals(">")) anonR = m.createMinCardinalityRestriction(null, p2, Integer.parseInt(restrValue)); if(restrType.equals("<")) anonR = m.createMaxCardinalityRestriction(null, p2, Integer.parseInt(restrValue)); if(restrType.equals("=")) anonR = m.createCardinalityRestriction(null, p2, Integer.parseInt(restrValue)); subelements = subelements.cons(anonR); } } IntersectionClass inters = m.createIntersectionClass(null, subelements); //e creo la classe intersezione di essi AllValuesFromRestriction restr = m.createAllValuesFromRestriction(null, p, inters); elements = elements.cons(restr); } } IntersectionClass profile = m.createIntersectionClass(null, elements); //classe finale, intersezione di tutte le classi, restrizioni numeriche e proprietà m.createIndividual(Ns + name+"_"+surname, profile); //output del modello su stringa String stringa = new String(); Writer sw= new StringWriter(); sw.write(stringa); m.write(sw, "RDF/XML-ABBREV"); String owlString = new String(); owlString = sw.toString(); sw.close(); return owlString; } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } //per memorizzare la stringa nel DB, non deve contenere caratteri come ", che converto in tag html private String escapeChar(String string) { final StringBuffer result = new StringBuffer(); final StringCharacterIterator iterator = new StringCharacterIterator(string); char c = iterator.current(); while(c != CharacterIterator.DONE) {
147
if (c == '\"') result.append("\\\""); else result.append(c); c = iterator.next(); } return result.toString(); } private void initModel() { FileInputStream fis; try { fis = new FileInputStream("ontologies/Skill.owl"); model = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM); model.read(fis, null); prefixes = model.getNsPrefixMap(); fis.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }}
148
“Mai mandare un essere umano a fare il lavoro di una macchina” (Agente Smith)
149
Top Related