← SIS

SIS Abstraction 2

La SISAbstraction2 è una libreria pensata per gli sviluppatori di applicazioni che si appoggiano al framework Space Integration Services (SIS).

La libreria offre una serie di primitive di alto livello per l'interazione con gli Endpoint REST di SIS supportando anche il meccanismo di notifica asincrona (push).

Developed by

Alessio Vertemati and Marco Covelli

Version

2.1.2 for SIS version 2.5.x

Last update

09/10/2013 11.16

Subversion

https://mars.sal.disco.unimib.it/svn/sis/SISAbstraction2/

Version history

Changelog

versione 2.1.2:


Introduzione

La libreria espone una interfaccia globale che racchiude i servizi disponibili (interfaccia SIS nel package sal.sis) e singole interfacce per ogni tipologia di servizio realizzato dalla piattaforma SIS (package sal.sis.interfaces).

Per poter utilizzare la libreria è necessario disporre:

Per ottenere l'istanza del client SIS chiamare il metodo SISServiceFactory.build(String url, String token).

Attenzione: gli esempi utilizzati nel seguito non hanno gestione delle eccezioni per facilitarne la lettura.

Download

SIS Abstraction2 - 2.1.2 (3,5 MB zip)

SIS Abstraction2 - 2.1.2 single jar with all dependencies (4 MB zip)

Effettuare una pubblicazione

Per effettuare una pubblicazione è necessario utilizzare il servizio events mediator, la cui interfaccia è esposta tramite IEventsMediator, come segue:

//creo l'istanza del client SIS mediante la factory
SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken");

//recupero il servizio Events Mediator
IEventsMediatorService eventsMediator = _sis.eventsMediator();

//Creo il contesto su cui effettuerò la pubblicazione. In questo caso un contesto enumerativo con una sola locazione all'interno 
EnumerativeContext ctx = new EnumerativeContext("spaceName", "locatorName");

//effettuo la pubblicazione di una informazione tematica (l'informazione tematica può anche essere una stringa vuota)
eventsMediator.publish(new ThematicInfo("Information"), ctx);

È possibile fare una pubblicazione su più contesti mediante l'overload publish(ThematicInfo ti, final List<SpatialContext> context).

A partire dalla versione 2.1.2 della libreria è anche disponibile il metodo publish in versione non bloccante. Tale metodo ritorna subito il controllo al chiamante e schedula l'invio della pubblicazione il prima possibile. Eventuali errori di invio sono intercettabili attraverso la callback registrata al momento della chiamata al metodo.

//supponendo di avere già effettuato la configurazione come nel frammento di codice della publish bloccante
//effetuiamo ora la chiamata alla publish non bloccante passando la callback che ci notifica di un eventuale errore di pubblicazione
eventsMediator.publish(new ThematicInfo("Information"), ctx, new ErrorCallback<SISServiceException>(){

    @Override
    public void complete(SISServiceException ex){

        if(e!=null){
            //si è verificata una eccezione in fase di pubblicazione
        }
        else{
            //la pubblicazione è andata a buon fine
        }

    }

});

I contesti utilizzabili in generale sono di tipo enumerativo (EnumerativeContext) o dichiarativo (DeclarativeContext). Nel contesto enumerativo le locazioni vengono espresse una ad una mentre nel contesto dichiarativo viene espressa una selezione mediante distanza da una singola locazione. Seguono esempi di tali contesti.

//un contesto dichiarativo su uno spazio a griglia che selezionerà tutte le celle a distanza 4 dalla cella indicata
DeclarativeContext c1 = new DeclarativeContext("gridSpaceName", "2,2", "4");

//Contesto enumerativo che comprende tutte le locazioni di uno spazio
EnumerativeContext c2 = new EnumerativeContext("spaceName", "*");

//Contesto enumerativo su più di una locazione dello spazio
EnumerativeContext c3 = new EnumerativeContext("spaceName", new ArrayList<String>(){{ 
    add("location1");
    add("location2");
    add("location3"); }}));

Hint per chi non lo sapesse la dichiarazione new ArrayList<String>(){{ add("location1"); add("location2");add("location3"); }} permette di dichiarare e popolare una Collection generica nel medesimo tempo in cui viene istanziata. Questa pratica è molto usata soprattutto se si vuole associare una Collection ad una variabile statica.

Effettuare una sottoscrizione

Una sottoscrizione prevede la specifica di uno o più contesti spaziali di interesse nei quali la pubblicazione di una informazione tematica scatenerà il processo di notifica verso le applicazioni sottoscritte.

La libreria di astrazione rende facile implementare il meccanismo di sottoscrizione asincrona (push), oltreché a fornire le primitive per la sottoscrizione sincrona in stile polling (la sottoscrizione sincrona non sarà oggetto di esempio, si veda la javadoc per la documentazione)

//creo l'istanza del client SIS mediante la factory
SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken");

//recupero il servizio Events Mediator
IEventsMediatorService eventsMediator = _sis.eventsMediator();

//Creo il contesto su cui effettuerò la sottoscrizione. In questo caso tutto lo spazio spaceName 
EnumerativeContext ctx = new EnumerativeContext("spaceName", "*");

//effettuo la sottoscrizione passando contesto e handler delle notifiche e recupero l'handle per la desottoscrizione
AsyncSubscriptionHandle handle = eventsMediator.subscribe(ctx, new MatchingsHandler() {

    public void handleMatchings(List<Matching> matchings){
        System.out.println("Matchings: " + matchings);
    }

});

//Prima di terminare il programma è importante effettuare la desottoscrizione
eventsMediator.unsubscribe(handle);

Hint in questo semplice esempio l'handler delle notifiche è creato mediante Anonymous Inner Class solo per brevità, nulla vi vieta di creare una classe che implementa l'interfaccia MatchingsHandler nel suo file.

Esplorare gli spazi

L'esplorazione degli spazi esistenti avviene attraverso il servizio space inspector rappresentato tramite l'interfaccia ISpaceInspector.

Recuperare gli spazi esistenti

In particolare il servizio permette di recuperare l'elenco di tutti gli spazi presenti sull'istanza di SIS in funzione del livello di autenticazione del token utilizzato:

//creo l'istanza del client SIS mediante la factory
SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken");

//recupero il servizio Space Inspector
ISpaceInspector spaceInspector = _sis.spaceInspector();

List<String> spaces = spaceInspector.getSpaceNames();

System.out.println("Spazi: " + spaces);

Recuperare le caratteristiche degli spazi

Per ottenere i dettagli di uno spazio sono disponibili due metodi: getSpaceInformation(...) e getSpatialModel(...).

Attraverso getSpatialModel(String) è possibile recuperare la tipologia del modello spaziale alla base di uno spazio dato il suo nome. Le tipologie supportate sono evidenziate mediante l'enumerazione SpatialModels.

//creo l'istanza del client SIS mediante la factory
SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken");

//recupero il servizio Space Inspector
ISpaceInspector spaceInspector = _sis.spaceInspector();

SpatialModels model = spaceInspector.getSpatialModel("nomeDiUnoSpazio");

Se invece si desidera recuperare informazioni quali, ad esempio, l'estensione di uno spazio a griglia o le locazioni di uno spazio di nomi è necessario usare il metodo getSpaceInformation(String). (Attenzione: nella precedente versione della libreria questo metodo si chiama getSpaceParameters)

//creo l'istanza del client SIS mediante la factory
SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken");

//recupero il servizio Space Inspector
ISpaceInspector spaceInspector = _sis.spaceInspector();

SpaceInformation info = spaceInspector.getSpaceInformation("nomeDiUnoSpazio");

//in SpaceInformation troviamo

//nome e tipo dello spazio
String name = info.getSpaceName();
SpatialModels model = info.getSpatialModel();

//la configurazione iniziale dello spazio, e.g. le locazioni iniziali, l'estensione della griglia (dipende dal tipo di spazio)
SpaceParameters params = info.getParameters();

I paranmetri di configurazione dipendono fortemente dal tipo di spazio. Qui di seguito vengono riportati i più comuni tipi di spazio e la classe che ne rappresenta i parametri (maggiori dettagli tramite l'enumerazione SpatialModels):

Verificare che una locazione esista in uno spazio

È possibile fare richiesta di verifica che una locazione sia presente all'interno di uno spazio mediante il metodo locationExists(String space, Location location):

//creo l'istanza del client SIS mediante la factory
SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken");

//recupero il servizio Space Inspector
ISpaceInspector spaceInspector = _sis.spaceInspector();

//verifico che la locazione "locazione1" esista nello spazio di nomi nomeDiUnoSpazio 
boolean exists = spaceInspector.locationExists("nomeDiUnoSpazio", new Name("locazione1"));

Nell'esempio Name è la classe che rappresenta una generica locazione all'interno di uno spazio di nomi. Sono disponibili classi quali Grid2DCell, Grid3DCell, Node e Edge per rappresentare locazioni all'interno di spazi a griglia bi- e tri-dimensionale e nodi e archi di un grafo.

Gestione degli spazi

Le operazioni di modifica degli spazi sono racchiuse nel servizio space manager esposto mediante l'interfaccia ISpaceManager.

È possibile creare, eliminare o modificare spazi in funzione del livello di autorizzazione del token utilizzato. La modifica degli spazi consiste nell'aggiunta e/o rimozione di locazioni (Attenzione: non è possibile aggiungere e/o rimuovere locazioni in spazi a griglia, provate ad indovinarne il motivo)

Creazione ed eliminazione di spazi

//creo l'istanza del client SIS mediante la factory
SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken");

//recupero il servizio Space Manager
ISpaceManager spaceManager = _sis.spaceManager();

//Creo lo spazio nomi "Matrix_Cast_and_Crew"
NameSpaceParameters params = new NameSpaceParameters(new ArrayList<String>(){{
    add("Andy Wachowski");
    add("Lana Wachowski");
    add("Keanu Reeves");
    add("Laurence Fishburne");
    add("Carrie-Anne Moss");
    add("Hugo Weaving");
    add("Gloria Foster");
    add("Joe Pantoliano");
    add("Marcus Chong");
    //... and the others
}}));

//ora effettuo la creazione dello spazio di nomi vera e propria
spaceManager.defSpace("Matrix_Cast_and_Crew", SpatialModels.NAME, params);

//per eliminare lo spazio appena creato
spaceManager.undefSpace("Matrix_Cast_and_Crew");

Attenzione: nella precedente versione della libreria la chiamata defSpace restituisce un identificativo dello spazio, nominato handle, che ne permetterà l'eliminazione.

Hint i dati per questi esempi si riferiscono al film Matrix (1999).

Aggiunta e rimozione di locazioni

//creo l'istanza del client SIS mediante la factory
SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken");

//recupero il servizio Space Manager
ISpaceManager spaceManager = _sis.spaceManager();

//Supponiamo di aver già lo spazio "Matrix_Cast_and_Crew" e volerci aggiungere "Bill Pope"
List<Location> addLocations = new ArrayList<Location>(){{
    add(new Name("Bill Pope"));
}});

//aggiungo le nuove locazioni
spaceManager.addLocations("Matrix_Cast_and_Crew", addLocations);

//rimuovo le locazioni
List<Location> removeLocations = new ArrayList<Location>(){{
    add(new Name("Bill Pope"));
}});
spaceManager.removeLocations("Matrix_Cast_and_Crew", removeLocations);

Per facilitare la creazione delle corrette istanze della gerarchia di Location è possibile avvalersi del metodo statico Location.fromString(LocationType type, String location).

Creare ed eliminare mapping

Per operare sui mapping il servizio di riferimento è mappings manager tramite l'interfaccia IMappingsManager.

Un mapping è una relazione che intercorre tra almeno due contesti spaziali.

//creo l'istanza del client SIS mediante la factory
SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken");

//recupero il servizio Mappings Manager
IMappingsManager mappingsManager = _sis.mappingsManager();

//supponiamo di avere definito lo spazio "Matrix_Cast_and_Crew" e lo spazio "Matrix_Roles", ora associamo i membri del cast al rispettivo ruolo nella produzione cinematografica

//per prima cosa definiamo i contesti sorgente e target del mapping
EnumerativeContext source = new EnumerativeContext("Matrix_Cast_and_Crew", "Hugo Weaving");
EnumerativeContext target = new EnumerativeContext("Matrix_Roles", "Actor");

//ora andiamo a fare la richiesta di creazione del mapping vero e proprio
mappingsManager.map(source, target);

//Supponiamo la presenza di un mapping tra (Matrix_Cast_and_Crew, "Bill Pope") -> (Matrix_Roles, "Actor") che non è corretto e che quindi va eliminato
mappingsManager.unmap(
    new EnumerativeContext("Matrix_Cast_and_Crew", "Bill Pope"), 
    new EnumerativeContext("Matrix_Roles", "Actor"));

Esplorare i mapping

L'esplorazione dei mapping esistenti avviene tramite il servio mapping inspector la cui interfaccia è IMappingsInspector.

//creo l'istanza del client SIS mediante la factory
SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken");

//recupero il servizio Mappings Inspector
IMappingsInspector mappingsInspector = _sis.mappingsInspector();

È possibile recuperare tutti i mapping definiti:

List<MappingDescription> mappings = mappingsInspector.getMappings();
//La lista contiene tutti i mapping espressi mediante i rispettivi contesti sorgente e destinazione 

oppure solo quelli da una locazione verso uno spazio:

EnumerativeContext ctx = mappingsInspector.getForwardMapped("Matrix_Cast_and_Crew", "Bill Pope", "Matrix_Roles");
//ctx contiene tutte le locazioni dello spazio "Matrix_Roles" che sono destinazioni di un mapping che parte dalla coppia ("Matrix_Cast_and_Crew", "Bill Pope")

oppure ancora richiedere se una locazione di uno spazio ha mapping e con quali spazi:

List<EnumerativeContext> ctxs = mappingsInspector.getMapped("Matrix_Cast_and_Crew", "Bill Pope");
//ctxs contiene tutti contesti che hanno mapping con la locazione "Bill Pope" dello spazio "Matrix_Cast_and_Crew"

È possibile anche ricevere notifiche di cambiamento dei mapping mediante il metodo subscribeMappingChange (per eliminare la sottoscrizione utilizzare il metodo unsubscribeMappingChange).

SIS Map

SIS, oltre ai meccanismi publish/subscribe su contesti spaziali, permette la creazione dei contesti spaziali stessi e di mapping fra loro. Questo, da un altro punto di vista, concretizza la possibilità di memorizzare informazioni chiave-valore all'interno del repository SIS. La chiave costituisce lo spazio ed il locator di origine di un mapping, il valore lo spazio ed i locator di destinazione.

Questo meccanismo è incapsulato all'interno di una classe SISMap che implementa un'interfaccia Map<String, List<String>> del Java Collection Framework.

Una SISMap può essere vista quindi come una mappa che memorizza i mapping tra uno spazio di origine ed uno spazio di destinazione. La chiave contiene il locator di origine di un mapping, il valore contiene la lista dei locator di destinazione.

Una SISMap quindi condivide le funzionalità di una normale mappa. Di seguito sarà illustrato qualche breve esempio, che esplicita inoltre i reali cambiamenti all'interno dell'istanza di SIS. Per un resoconto più dettagliato del comportamento dei metodi, si consulti la JavaDoc relativa.

Instanziazione di una SISMap

SISMap è anch'essa un'interfaccia. Nella versione corrente della libreria, tale interfaccia è implementata solo dalla classe SimpleSISMap, un'implementazione a basso impatto sulla memoria locale, ma con una bassa efficienza, legata principalmente al throughput di rete generato dalle chiamate ai webservice di SIS.

Di seguito è riportata l'instanziazione di una SimpleSISMap.

//creo l'istanza del client SIS mediante la factory
SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken");

//istanzio la mappa
SISMap map = new SimpleSISMap(_sis, "mpms.People", "mpms.Floor");

L'oggetto map potrà ora essere utilizzato per leggere e scrivere i mapping tra gli spazi mpms.People e mpms.Floor.

Esplorazione dei mapping

Per esplorare i mapping è sufficiente chiamare il metodo get() della SISMap con argomento il nome del locator di cui si vogliono conoscere i mapping verso lo spazio destinazione.

Ad esempio:

// mappedLocators contiene i mapping diretti dal locator "Daniela Micucci" nello spazio "mpms.Floor"
List<String> mappedLocators = map.get("Daniela Micucci");
Di seguito è riportata l'instanziazione di una `SimpleSingleSISMap`. //creo l'istanza del client SIS mediante la factory SIS _sis = SISServiceFactory.build("http://www.sal.disco.unimib.it/sis/2/", "AuthenticationToken"); //istanzio la mappa SingleSISMap map = new SimpleSingleSISMap(_sis, "mpms.People", "mpms.Floor");

FAQ e risoluzione problemi

Curiosità

La prima versione della libreria (mid 2010) è stata creata per il framework MPMS che si appoggiava a SIS versione 1 e faceva uso dei web service XML-SOAP. Tale versione fu creata da Marco Mobilio per facilitare lo sviluppo del Wrapper MPMS il quale si appoggiava a SIS per la comunicazione di comandi e la ricezione di eventi. Successivamente fu iniziato lo sviluppo del branch 2 (l'attuale SISAbstraction2) per facilitare l'integrazione con i nuovi servizi di tipo REST della seconda versione di SIS e per facilitare lo sviluppo di applicazione Android che si appoggiano a SIS per la ricezione di informazione spazialmente contestualizzate. Solo sul finire del 2012 nella libreria fu introdotto il supporto alla ricezione di notifiche push mediante WebSocket da SIS grazie all'intervento di Marco Covelli, il quale per potersi laureare ed evitare che i sistemi informativi di ateneo lo perseguitassero per i corridoi per aver generato troppo traffico di rete ha dovuto necessariamente sfruttare questa tipologia di comunicazione.