You are on page 1of 153

POLITECNICO DI BARI

FACOLTÀ DI INGEGNERIA
CORSO DI LAUREA IN INGEGNERIA INFORMATICA
DIPARTIMENTO DI ELETTROTECNICA ED ELETTRONICA

TESI DI LAUREA
IN
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:

 AL: 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
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 u

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
Il linguaggio che useremo è di tipo ALN (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 u D intersezione. Gli oggetti appartenenti sia a C che a D.

 8 R.C restrizione universale. Gli oggetti che partecipano alla relazione R il cui

range è costituito dagli oggetti appartenenti alla classe C.

 9R 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
 Definizione 1 Sussunzione. Data una DL , siano P e T due concetti in L, e la

TBox T sia un set di assiomi in L. Un concetto T sussume un concetto P in

relazione a T se ogni interpretazione di T assegna a T un sottoinsieme

dell'insieme assegnato a P. Scriviamo quindi che T j=P v T per indicare che T

sussume P in relazione a T .

Come esempio ipotizziamo che T 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

T j=P v 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 L, un concetto C espresso in L, e

T un set di assiomi in L, C è soddisfacibile nei riguardi di T se esiste almeno

un modello di T che assegni un'estensione non vuota a C. Dal momento che un

concetto C è soddisfacibile in T se e solo se C non è sussunto da ?, scriviamo

che T j=C 6v? per indicare che C è soddisfacibile in T .

Nello scenario dell'esempio precedente, se T j=P v ? (rispettivamente T j=T v ?)

allora la descrizione P (rispettivamente T) è in conflitto con le informazioni modellate

11
in T . Ovvero, la richiesta (il curriculum) è contraddittoria. Allo stesso modo, se C=P u

T, con P e T soddisfacibili in T , 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
• 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:

<?xml version="1.0" encoding="ISO-8859-1"?>


<utenti>
<utente nome="Luca" cognome="Ruggiero"
/>
<utente nome="Max" cognome="Rossi" />
</utenti>

13
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 /> v
< owl: equivalentClass /> ´
< owl: disjointWith /> :
< owl: intersectionOf /> u
<owl: allValuesFrom /> 8
<owl: someValuesFrom /> 9

17
< owl: maxCardinality /> ·
< owl: minCardinality /> ¸
<owl: cardinality /> =

Tabella 1.2: Corrispondenze tra la sintassi di OWL-DL e di un DL ALN

Questo sottoinsieme dei tag di OWL consente di esprimersi in logica ALN . 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
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 uT non

è soddisfacibile nell'ontologia T – 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 uK

soddisfacibile in T . 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 L una DL, P e T due concetti in L, e T

un set di assiomi in L, dove sia P che T sono soddisfacibili in T . Un problema

19
di Concept Contraction (CCP), identificato con hL, T, P, T i, consiste nel trovare

una coppia di concetti hG, K i 2 L £ L tali che T j=T ´ G uK, e che K uP sia

soddisfacibile in T . Chiamiamo K una contrazione di T secondo P e T .

Ovviamente un problema di Concept Contraction ammette sempre la soluzione banale

hG, K i = hT; >i, 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 P u T è soddisfacibile in T , la migliore soluzione possibile è h>; T i 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
• Definizione 4 Concept Abduction Siano S e D due concetti espressi in una

logica descrittiva L, e sia T un set di assiomi, con S e D soddisfacibili in T . Un

problema di Concept Abduction (CAP), denotato da hL; C; D; T i, consiste nel

trovare un concetto H tale che T 6j= S u H ´ ?, e che T j= S u H v D .

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 – T 6j= P u T v ? – 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 T , affinchè T sia completamente soddisfatto da P e si abbia

un match completo, deve valere la relazione di sussunzione T j= P u T . 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
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:

U : hG; H; T i ¡! R

Questa funzione può essere usata sia in caso di incompatibilità che di compatibilità

(dove nel secondo caso si avrà G = >), ed è basata sulla funzione rankPotential [3],

utilizzata per classificare i match potenziali, che vediamo qui nella sua struttura di base:

Algorithm rankPotential(C, D)
input concepts C, D, in forma normale, tali che C uD è soddisfacibile
output rank n ¸ 0 di C rispetto a D, dove 0 significa C v D (best
ranking)
begin algorithm
let n := 0 in

22
/* aggiunge ad n il numero dei nomi di concetti in D
che non sono tra i nomi di concetti in C */
1. n := n + jDnames+ ¡ Cnames+ j;
/* aggiunge ad n le restrizioni numeriche di D
che non sono implicate da quelle di C */
2. for each concept (¸ x R) 2 D#
such that there is no concept (¸ yR) 2 C# with y ¸ x
n := n + 1;
3. for each concept (· xR) 2 D#
such that there is no concept (· yR) 2 C# with y · x
n := n + 1;
/* per ogni restrizione universale in D
aggiunge il risultato di una chiamata ricorsiva */
4. for each concept 8R:E 2 Dall
if there does not exist 8R:F 2 Call
then n := n + rankP otential(>; E);
else n := n + rankP otential(F; E);
return n;
end algorithm

Una possibile espressione della funzione U è, come proposto in [2] :


N
U (hT; P; G; H; T i) = j1 ¡ N ¡g
¤ (1 ¡ hk )j

• k: valutazione di K soluzione di un CCP tra Pi e T –


k = rankP otential(>; K; T )

• h: valutazione di H soluzione di un CAP tra K (T se non c'è contrazione) e Pi –

h = rankP otential(P; K; T )

23
• g: valutazione di G soluzione di un CCP tra Pi e T –

g = rankP otential(K; T; T )

• N: valutazione di T – N = rankP otential(>; T; 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 Assign(T; P; T ) che valuta le possibili

corrispondenze tra un compito richiesto T e un insieme di profili P = fPi g; i = 1:::n ,

in relazione ad un'ontologia T definita in ALN .

1: Algorithm Assign(T; P; T )
2: input P, T ´ K u G concepts in L such that both T j= P 6´ ? and T j= T 6´ ?
3: output hPassigned ; H; Gi
4: begin algorithm
5: Passigned ´ >;
6: Umin = 1;
7: N = rankP otential(>; T; T );
8: for each Pi 2 P
9: if T j= T u Pi ´ ?
10: then

24
11: hGi ; Ki i = contract(Pi ; T; T );
12: else
13: Ki = T ;
14: end if
15: Hi = abduce(Pi ; Ki ; T );
16: ki = rankP otential(>; Ki; T );
17: hi = rankP otential(Pi ; Ki ; T );
18: gi = rankP otential(Ti; Ki ; T );
19: Ui = u(N; ki ; hi ; gi );
20: if Ui < Umin
21: then
22: Umin = Ui ;
23: Passigned = Pi ;
24: H = Hi ;
25: G = Gi ;
26: end if
27: end for each
28: return hPassigned ; H; Gi;
29: end algorithm

In questo algoritmo Hi = abduce(Pi ; Ki ; T ); determina che H è soluzione del

problema di concept abduction hL; T; Pi ; T i mentre hGi ; Ki i = contract(Pi ; T; T )

determina hGi ; Ki i come soluzione del problema di concept contraction hL; T; Pi ; T i.

La procedura restituisce Passigned , ovvero il miglior candidato trovato tra tutti i

profili disponibili Pi, 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
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 ALN 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
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.

ALN 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 ALN , OWL-DL e DIG

28
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.

<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>

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
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:

<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>

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
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.

uploadForm.setAction("/fileupload");
uploadForm.setEncoding(FormPanel.ENCODING_MULTIPART);
uploadForm.setMethod(FormPanel.METHOD_POST);

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
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
<?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>

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:


<?xml version="1.0" encoding="UTF-8"?>
<Level>
<Certification>
<Certification_one />
</Certification>
<Doctorate>
<Phd />
</Doctorate>
<Master_after_Master>
<Management_Master />
</Master_after_Master>
</Level>

12.5: Risposta XML contenente la gerarchia di classi dell'ontologia Level.owl

89
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:

<?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>

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
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"?> <?xml version="1.0" encoding="UTF-8"?>


<Job_Type> <Job_Title>
<Intern /> <Asset_Manager />
<Seasonal /> <Process_Manager />
<Temporary_Conctract_Project /> <Programme_Manager>
<Worker_Employee /> <Delivery_Programme_Manager />
</Job_Type> <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:

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

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
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:

INSERT INTO People VALUES (name, surname, gender, eta,


phone, mail, nationality, street, n, city, postalCode,
country, minPay, relocation, travel, military,
contractType, positionStatus, filename, owlProfile);

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
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("surnam
e"));
}
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: Engineeringu8

advanced_knowledge.(Process_Performance_MonitoringuProcess_PlanninguInternet

_technologiesuERPSystems)u(¸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
P1 (Julia) = Process_Engineeringu8 advanced_knowledge.

Business_Operations_Optimization u 8 basic_knowledge.(TeamCoordinatoru

Meeting_Coordinator) u (=0 has_experience)

P2 (Tom) = 8 advanced_knowledge.(Internet_Technologies u Internet_Development)u8

basic_knowledge.(Java u C++)

P3 (Richard) = Engineering u8 advanced_knowledge.ERPSystems u8

basic_knowledge.(Lead_role u Team_Coordinator)u(¸3

has_experience)

P4 (Alison) = Managerial_Engineering u8 advanced_knowledge.(IT_Consulting u

HTML) u8 has_language_knowledge.(English u 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
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:

GP3 = >

HP3 = 8 advanced_knowledge.(Internet_Technologies u

Process_Performance_Monitoring u Process_Planning) u 8

basic_knowledge.(Negotiation_skills u Communication_skills)

L'utente visualizza pertanto il seguente pannello risultati:

103
1.6: Pannello di presentazione dei risultati al client nel caso trattato nell'esempio n° 1

Esempio 2

Consideriamo ora un task descritto come:

T = 8basicKnowledge:(InternetU se u M arkupLanguage)

e un insieme di candidati descritti da:

- P1 (M ario) =8basicKnowledge:(InternetU se u ComputerGraphics)

uM asterDegree u (· 2hasExperience)

- P2 (John) = M anagerialEngineer u 8advancedKnowledge:

:InternetT echnologies u 8toolsKnowledge:W ebDesigning

- P3 (F rank) = Engineer u 8advancedKnowledge:

:(T otalQualityM anagement u ClientServerP rotocol u V BScript u OOP )

I punteggi ottenuti sono:

U
P1 (Mario) 0,0000
P2 (John) 1,8000
P3 (Frank) 1,6667

104
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 (H = >), 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
Mario IT
John US
Frank US

Tabella 3.6: schematizzazione delle nazionalità dei candidati

Il requisiti stavolta sono:

T = 8advancedKnowledge:(ClientServerP rotocol u P rocessM anagement)

u8toolsKnowledge:InternetDeveloping

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
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 è 8advancedKnowledge:ClientServerP rotocol. Il requisito sull'abilità di

processManagement è rispettato in quanto la classe ManagerialEngineer è definita

come

Engineering u advancedKnowledge:(ManagerialSkill u P rocessM anagement)

107
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 Pi e T

siano entrambi soddisfacibili nella TBox T , la loro congiunzione non lo sia:

Pi u T v?. 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_Engineeringu8 advanced_knowledge.Object_Oriented_Programmi

ngu (·4 has_experience) e una richiesta T definita da: Engineering u Phd u (¸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
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.getModuleBaseU
RL()+"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.uploadF
orm.setEncoding(FormPanel.ENCODING_URLENCODED);
personal.fancyFileUpload.deleteF
iles();
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(per
sonal.country.getSelectedIndex()), Boolean.toString(personal.minimumPay.isChecked()),
Boolean.toString(personal.reloca
tion.isChecked()), Boolean.toString(personal.travel.isChecked()),
Boolean.toString(personal.milita
ry.isChecked()), personal.contract.getItemText(personal.contract.getSelectedIndex()),
personal.position.getItemText(pe
rsonal.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.getModuleBaseU
RL()+"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)can
didate.get("error"));
}
else {
pop.hide();
DrawResultPanel(candidat
e);
}
}
};
sendservice.sendCompany(personal.gender.getItemT
ext(personal.gender.getSelectedIndex()),
personal.age.getText(),
personal.nationality.getItemText(personal.nationality.getSelectedIndex()),

118
Boolean.toString(personal.minimu
mPay.isChecked()), Boolean.toString(personal.relocation.isChecked()),
Boolean.toString(personal.travel
.isChecked()), Boolean.toString(personal.military.isChecked()),
personal.contract.getItemText(pe
rsonal.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().eq
uals("catom"))
prop.addItem(ele
m2.getAttribute("name"));
if(elem2.getTagName().eq
uals("atleast") || elem2.getTagName().equals("atmost")) {
String value =
elem2.getAttribute("num");
String sign =
new String();
if(elem2.getTagN
ame().equals("atmost")) sign = "<";
else sign = ">";
String name =
new String();
NodeList nlist2
= elem2.getChildNodes();
for(int m=0;
m<nlist2.getLength(); m++)
if(nlist
2.item(m).getNodeType()==Node.ELEMENT_NODE) {

Element e2 = (Element)nlist2.item(m);

if(e2.getTagName().equals("ratom"))

name = e2.getAttribute("name");
}
prop.addItem(sig
n + 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("atm
ost")) sign = "<";
else sign = ">";
String name = new String();
NodeList nlist =
elem.getChildNodes();
for(int l=0;
l<nlist.getLength(); l++)
if(nlist.item(l).getNode
Type()==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).getNodeT
ype()==Node.ELEMENT_NODE) {
Element subel =
(Element)list.item(j);
if(subel.getTagN
ame().equals("ratom"))
prop.set
Text(subel.getAttribute("name"));
if(subel.getTagN
ame().equals("catom"))
prop.add
Item(subel.getAttribute("name"));
if(subel.getTagN
ame().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.exist
s()) {
suffix =
Integer.toString((int)Math.round(Math.random()*10000));
if(fileName.last
IndexOf('.')!=-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(uploa
dedFile);
//out.print(retu
rnOKMessage);
String
requestUrl = request.getRequestURL().toString();
requestUrl =
requestUrl.substring(0, requestUrl.lastIndexOf('/')+1);
//restituisce al
client il percorso relativo del file
out.print(rootDi
rectory + fileName);
}
catch (Exception e) {
e.printStackTrac
e();
}
}
}
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("surnam
e"));
}
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

You might also like