Tesi Specialistica – Capitolo 2: Il framework jCOLIBRI’

jCOLIBRI è un framework object-oriented, sviluppato in linguaggio Java, per la creazione di sistemi di Case Based Reasoning. E’ stato sviluppato dal GAIA (GROUP FOR ARTIFICIAL INTELLIGENCE APPLICATIONS) dell’Università Complutense di Madrid. E’ disponibile in 2 major release, la versione 1 e la versione 2.

jCOLIBRI 1 è la prima versione del framework, che contiene un’interfaccia grafica completa per guidare l’utente nella creazione di sistemi di CBR. E’ quindi una versione rivolta ai designers dei sistemi e non agli sviluppatori.

jCOLIBRI 2 invece è la nuova implementazione del framework che consiste in un modello basato su una nuova architettura divisa in 2 livelli: un livello orientato agli sviluppatori (finito) ed un livello orientato ai designers (sviluppo futuro).
Questo nuovo design dell’architettura rappresenta quindi un white-box framework completo aperto agli sviluppatori java che voglio includere alcune features di jCOLIBRI nei loro sistemi di CBR.

Tipologie di Frameworks
I framework possono essere suddivisi in 2 categorie in base alla loro struttura:

  • White-Box Framework
    • Riutilizzati per lo più tramite subclassing, cioè sfruttando l’ereditarietà e
      specializzando le classi del framework
    • Sono orientati agli sviluppatori
  • Black-Box Framework
    • Riutilizzati tramite parametrizzazione
    • Hanno associato un builder per la generazione del codice
      dell’applicazione
    • Il livello costituito dal builder è rivolto ai designers dei sistemi.

Ritornando alle 2 versioni di jCOLIBRI possiamo quindi dire che jCOLIBRI Version1 consiste in un black-box framework con un builder visuale, mentre jCOLIBRI Version2 ha un nuovo design che prova a rimodellare l’architettura suddividendola in una parte che costituisce un chiaro sistema white-box orientato agli sviluppatori e in un’altra che costituisce una black-box con builder orientata invece ai designers.

L’architettura di jCOLIBRI
L’idea di base della nuova architettura è quella di separare le classi che costituiscono il core da quelle che costituiscono l’interfaccia utente.
Il livello più basso non contiene quindi nessun tipo di tool o di funzionalità grafica per lo sviluppo di applicazioni di CBR; rappresenta semplicemente la parte white-box del sistema che deve essere utilizzata dagli sviluppatori per implementare quanto necessario. Il livello più alto contiene delle descrizioni semantiche dei componenti e diversi tool che aiutano l’utente nello sviluppo dell’applicazione.

Creazione di un’applicazione di CBR
Un’applicazione CBR in jCOLIBRI deve implementare l’interfaccia
jcolibri.cbrapplications.StandardCBRApplication.
Questa interfaccia divide l’applicazione in 3 steps:

  • Precycle: viene eseguito una sola volta e consiste nell’inizializzazione
    dell’applicazione, con il caricamento della base di casi e la precomputazione di algoritmi dispendiosi, come ad esempio quelli utilizzati quando si lavora sui testi.
  • Cycle: rappresenta l’esecuzione del ciclo di CBR e viene eseguito ogni volta che si effettua una query.
  • Postcycle: rappresenta codice di post esecuzione e di mantenimento.

jCOLIBRI Framework StandardCBRApplication

Case Base
jCOLIBRI2 può gestire qualsiasi DBM (Data Base Manager) siccome esso usa internamente Hibernate. Hibernate (www.hibernate.org) è un servizio object-oriented di persistenza e di query ad alte prestazioni attraverso l’uso del quale jCOLIBRI permette l’utilizzo di qualunque DBM come Oracle, MySQL, ecc. In questo modo è possibile riutilizzare qualunque data base per creare la base di casi della propria applicazione CBR.

Rappresentazione dei casi
In jCOLIBRI2 i casi vengono rappresentati tramite i Java Beans. Un Java Bean è una qualunque classe che abbia definiti i metodi get() e set() per ogni suo attributo. La sua modifica e gestione può essere effettuata in modo automatico mediante una tecnologia Java chiamata Introspezione che è completamente trasparente per lo sviluppatore.
Usando i Java Beans in jCOLIBRI2, gli sviluppatori possono rappresentare i propri casi come normali classi Java. Inoltre Hibernate usa i Java Beans per memorizzare l’informazione nel database.

Case Components
L’unica restrizione riguardo al Java Bean che rappresenta il caso è che esso deve definire un ID che identifica lo stesso nella tabella del DBM; questa restizione è definita nell’interfaccia jcolibri.cbrcore.CaseComponent:
jCOLIBRI Framework CaseComponent

Casi e Query in jCOLIBRI2
In jCOLIBRI2 i casi e le query sono composte da oggetti di tipo CaseComponents. Un caso è suddiviso in 4 componenti:

  • Descrizione del problema
  • Soluzione al problema
  • Risultato dell’applicazione della soluzione
  • Motivazione della soluzione (perché questa soluzione è stata scelta)

Non tutti questi campi sono richiesti e possono essere lasciati vuoti, ad esclusione del componente descrizione che è l’unico obbligatorio.
Ogni query ha sempre una descrizione ed in generale la sua struttura è definita dalla classe jcolibri.cbrcore.CBRQuery:

jCOLIBRI Framework CBRQuery

Se una query contiene una descrizione, ne deriva che un caso è una query con in più una soluzione, un risultato e una motivazione per la soluzione. Per questo un caso è definito dalla classe jcolibri.cbrcore.CBRCase che estende CBRQuery aggiungendo i campi appena menzionati:

jCOLIBRI Framework CBRCase

jCOLIBRI Framework CBRCase

Ogni oggetto di tipo CaseComponent può avere attributi che a loro volta sono CaseComponents; in questo modo gli sviluppatori possono creare strutture complesse con attributi composti.

Definizione di nuovi tipi di attributi
Nella creazione di alcune applicazioni può capitare che i tipi built-in non siano sufficienti e che lo sviluppatore debba definirsi i propri tipi necessari in quel determinato caso. Nella definizione di un nuovo tipo lo sviluppatore deve specificare per esso in che modo verrà memorizzato e letto dal mezzo di persistenza. Questa operazione viene effettuata implementando l’interfaccia jcolibri.connectors.TypeAdaptor:
jCOLIBRI Framework TypeAdaptor

I 2 livelli di persistenza di jCOLIBRI
jCOLIBRI separa il problema della gestione della base di casi in 2 sottoparti, secondo quella che viene definita architettura a 2 livelli di persistenza. Questi 2 livelli sono più precisamente il meccanismo di persistenza e l’organizzazione in memoria. L’architettura che ne deriva può essere rappresentata con lo schema di figura:
jCOLIBRI Framework persistence layers

I Connettori di jCOLIBRI2
La persistenza viene implementata e gestita attraverso i connettori che rappresentano il primo livello del framework che si appoggia sui dispositivi di memorizzazione fisica dei dati. Essi sono oggetti che permettono di accedere e recuperare i casi dai mezzi fisici per renderli disponibili al sistema di CBR e garantiscono una certa flessibilità rispetto ai mezzi fisici stessi, in modo che i designers possano scegliere il più appropriato per il loro sistema o svilupparne uno ad hoc.

Di base, jCOLIBRI2 mette a disposizione i seguenti connettori:

  • DataBaseConnector: gestisce la persistenza dei casi utilizzando databases e utilizza la suo interno la libreria Hibernate
  • PlainTextConnector: gestisce la persistenza dei casi utilizzando dei files di testo
  • OntologyConnector: utilizza OntoBridge per gestire la base di casi memorizzata
    in ontologie

L’interfaccia di un connettore deve ovviamente includere i metodi per recuperare i casi dal dispositivo fisico e caricarli in memoria e quello inverso per salvare informazioni dalla memoria sul mezzo fisico. In jCOLIBRI2 questo viene definito dall’interfaccia jcolibri.cbrcore.Connector che definisce appunto i metodi che un connettore deve implementare. Quindi se uno sviluppatore necessita di uno specifico connettore può crearselo estendendo questa interfaccia.
I connettori vengono configurati attraverso un file XML e, per ogni tipo, è definito lo schema XML secondo cui il file di configurazione deve essere realizzato.

Un meccanismo di questo tipo, rappresenta una sorta di punto debole del framework, perché con esso si presume di caricare l’intera base di casi in memoria per poi operarci, ma questo deve anche poter essere fatto, in termini di spazio. Come risaputo, per applicazioni CBR reali un approccio simile risulta impraticabile e, al fine di risolvere ciò, si sta pensando ad estensioni sotto forma di filtri che permettano di caricare in memoria solo una sottoparte della base di casi, filtrata appunto in fase di caricamento quindi a livello di SQL.
jCOLIBRI Framework Connector

Organizzazione in memoria dei casi
Il secondo livello dell’architettura per la gestione della base di casi è quello riguardante la struttura dati usata per organizzare i casi una volta caricati in memoria dopo il recupero dal mezzo fisico. L’organizzazione della base di casi può avere una grande influenza nel processo di CBR quindi l’idea del framework è quella di lasciare libera la scelta su quale struttura utilizzare.
L’interfaccia che definisce l’organizzazione in memoria dei casi è jcolibri.cbrcore.CBRCaseBase:
jCOLIBRI Framework CBRCaseBase

Analogamente per quanto avviene con l’interfaccia Connector per i connettori, gli sviluppatori possono crearsi la propria struttura di organizzazione in memoria dei casi implementando questa interfaccia. Comunque, di base, jCOLIBRI include le seguenti strutture per le basi di casi:

  • jcolibri.casebase.LinealCaseBase: rappresenta una Basic Lineal Case Base che sostanzialmente memorizza i casi in una lista (List)
  • jcolibri.casebase.CachedLinealCaseBase: simile alla precedente con però un meccanismo di caching che permette di effettuare la persistenza dei casi quando si chiude l’applicazione e non volta per volta.
  • jcolibri.casebase.IDIndexedLinealCaseBase: rappresenta una estensione della LinealCaseBase che però mantiene anche un indice dei casi utilizzando il loro ID.

Caricamento della base di casi
Il caricamento della base di casi avviene tramite due componenti: un connettore ed un oggetto di tipo CBRCaseBase, visto in precedenza ai quali vengono poi assegnate istanze del tipo di connettore (DataBase, Ontologia, File di testo) e del tipo di struttura da utilizzare per la base di casi (Lineal, Indexed, ecc..). Il connettore deve essere configurato attraverso un file XML che viene passato come parametro al metodo initFromXMLfile del connettore stesso. Successivamente il connettore cosi creato viene utilizzato dal metodo init dell’oggetto CBRCaseBase per caricare i casi nella base di casi.

Configurazione del connettore
Il connettore per data base, che è quello che è stato utilizzato durante tutta la fase di sviluppo, deve essere configurato come detto attraverso un file XML, chiamato databaseconfigfile.xml che deve contenere le seguenti informazioni:

  • HibernateConfigFile: il percorso in cui si trova il file di configurazione di Hibernate, ibernate.cfg.xml
  • DescriptionMappingFile: il percorso del file XML che contiene il mapping tra feature e campi del database per la parte di descrizione dei casi
  • DescriptionClassName: il percorso della classe che costituisce la parte di descrizione dei casi
  • SolutionMappingFile: : il percorso del file XML che contiene il mapping tra feature e campi del database per la parte di soluzione dei casi
  • SolutionClassName: il percorso della classe che costituisce la parte di soluzione dei casi

Un esempio di file XML di configurazione del connettore per database utilizzato per alcuni test nella fase di sviluppo è riportato qui di seguito:

<DataBaseConfiguration>
     <HibernateConfigFile>
          jcolibri/test/rhene/hibernate.cfg.xml
     </HibernateConfigFile>
     <DescriptionMappingFile>
          jcolibri/test/rhene/RheneDescription.hbm.xml
     </DescriptionMappingFile>
     <DescriptionClassName>
          jcolibri.test.rhene.RheneDescription
     </DescriptionClassName>
     <SolutionMappingFile>
          jcolibri/test/rhene/RheneSolution.hbm.xml
     </SolutionMappingFile>
     <SolutionClassName>
          jcolibri.test.rhene.RheneSolution
     </SolutionClassName>
</DataBaseConfiguration>

File di configurazione di Hibernate
Come detto occorre definire un file di configurazione per Hibernate, chiamato hibernate.cfg.xml; tale file indica come effettuare l’accesso al DBMS. Hibernate ha la funzione di interfacciare il sistema con il DBMS sottostante, qualunque esso sia, purchè opportunamente configurato. La configurazione consiste nella corretta definizione delle seguenti proprietà:

  • Connection.driver_class: clase driver jdbc del DBMS che si vuole utilizzare (deve essere incluso nel classpath)
  • Connection.url: indirizzo della connessione jdbc
  • Username and password
  • Dialect

In generale sono comunque pochi i cambiamenti da effettuare per poter utilizzare Hibernate con un DBMS piuttosto che un altro.

Di seguito è riportato l’estratto di file di configurazione creato per l’utilizzo in alcuni esempi durante la fase di sviluppo.

<hibernate-configuration>
    <session-factory>
      <!-- Database connection settings -->
      <property name="connection.driver_class">
            org.postgresql.Driver
</property>
      <property name="connection.url">
            jdbc:postgresql://localhost/jcolibri
      </property>
      <property name="connection.username">Davis</property>
      <property name="connection.password">dede</property>
      <!-- SQL dialect -->
      <property name="dialect">
            org.hibernate.dialect.PostgreSQLDialect
      </property>
    </session-factory>
</hibernate-configuration>

Creazione dei files di mapping
I files di mapping definiscono come mappare i campi dei Java Bean che costituiscono i componenti dei casi con i campi delle tabelle del database. Per prima cosa si definisce quale tabella è utilizzata per recuperare i valori e poi si configurano le corrispondenze tra le colonne della tabella e ogni attributo del Java Bean.
Un esempio di come può essere fatto questo procedimento è presente in uno degli esempi distribuiti con il framework:
jCOLIBRI Framework mapping attributes - tables

Nella figura si vede un esempio di sistema per agenzia di viaggi; in questo caso i campi tipo, numero di persone, Zona, Mezzo di trasporto, Durata, Stagione e tipo di Soggiorno costituiscono la descrizione, mentre la soluzione è data dal prezzo e dal nome dell’Hotel.
Lo schema della tabella del database in cui sono memorizzati i casi è la seguente:

create table travel( caseId VARCHAR(15),
HolidayType VARCHAR(20), Price INTEGER, NumberOfPersons INTEGER, Region VARCHAR(30), Transportation VARCHAR(30), Duration INTEGER,
Season VARCHAR(30), Accommodation VARCHAR(30), Hotel VARCHAR(50)
);

e di conseguenza il file di mapping, ad esempio per la descrizione, risulta essere:
jCOLIBRI Framework Hibernate mapping file

In modo analogo, prendendo in considerazione i campi come indicato dalla figura precedente si costruisce il file di mapping per il componente Soluzione.

Questo in generale è un file di mapping per un Java Bean che contiene solo attributi “normali”; in molti casi invece può darsi che il java bean abbia dei campi che sono di un tipo definito dall’utente e in questo caso si aggiunge un tag type con valore jcolibri.connector.databaseutils.GenericUserType e poi un tag param che indica quale è la classe che definisce il tipo di attributo (e che deve implementare l’interfaccia type adaptor).
jCOLIBRI Framework GenericUserType

Questo è quello che si è, ad esempio dovuto definire, nella seconda parte della tesi, quando si è definito un nuovo tipo di attributo, la Serie Temporale. Dopo aver definito la classe, implementando l’interfaccia TypeAdaptor come richiesto, nel file di mapping si è scritta la corrispondenza nel modo indicato dallo schema soprastante, ovvero:

<property name="sig_2" column="segnale_2">
  <type name="jcolibri.connector.databaseutils.GenericUserType">
      <param name="className">jcolibri.test.rhene.TimeSerie</param>
  </type>
</property>

Leave a Reply

Your email address will not be published. Required fields are marked *