[NoSQL] Implementare un Document Store NoSQL con Oracle 12c e le SODA API

Avete intenzione di implementare una schemaless application, memorizzando le informazioni in modo “dinamico” e flessibile, secondo il cosiddetto paradigma “NoSQL style document store”?

Anche Oracle DB, a partire dalla versione 12.1.0.2, fornisce il supporto per la memorizzazione, l’indicizzazione e la ricerca di documenti in formato JSON.

Oracle12c

Più dettagliatamente, Oracle DB 12c permette, senza necessità di installare plugin aggiuntivi, l’implementazione di un Document Store e fornisce il seguente set di API, progettate per garantire il supporto allo sviluppo di schemaless application:

  • SODA for Java: interfaccia di programmazione document-store per sviluppatori Java che usano JDBC per comunicare con il database. SODA for Java consiste di un set di semplici classi che rappresentano il database, una collezione di documenti e un documento. I metodi di queste classi forniscono tutte le funzionalità richieste per gestire ed interrogare documenti e collezioni di documenti memorizzati in un database Oracle;
  • SODA for REST: interfaccia REST-based document-store implementata come Java servlet e distribuita come parte dell’Oracle REST Data Services (ORDS) 3.0. Le applicazioni basate su SODA for REST usano il protocollo HTTP per comunicare con la Java Servlet. SODA for REST Servlet può anche essere eseguito su un HTTP Server nativo del database (esistono versioni “bundle” di Web Server, come TomCat e JBoss opportunamente configurati per accedere al DB Oracle tramite API SODA). I metodi HTTP come PUT, POST, GET e DELETE mappano le operazioni eseguite sui documenti JSON. Fornendo API di tipo REST è possibile integrare la soluzione con web application esterne per esporre i dati memorizzati nella base dati Oracle.

Riferimento: http://www.oracle.com/technetwork/database/appdev-with-soda-2606220.pdf

Modello relazionale VS. Modello No-SQL. Volendo comparare un database relazionale con un DB NoSQL “document-store”, è possibile dire che:

  • Una collezione di documenti è una tabella
  • Un documento è una riga di una tabella
  • Un campo del documento è una colonna della tabella

I documenti in formato JSON vengono memorizzati con un ID univoco all’interno di una collezione. Per ciascun documento è possibile recuperare metadati, come data di creazione, dati di aggiornamento, owner e versione del documento, ecc.

Le funzionalità offerte da un document store includono:

  • Creazione e cancellazione di una collezione
  • Creazione, ricerca, aggiornamento o cancellazione di un singolo documento in base al suo ID
  • Recupero dei documenti in una collezione
  • Ricerca di una collezione, tipicamente utilizzando Query By Example (QBE) metaphor
  • Creazione e cancellazione di indici

Dato questo semplice livello di funzionalità fornito da un document store, l’API diventa semplice, particolarmente quando comparato con le tradizionali API SQL-based come JDBC.

Il DBMS Oracle già forniva dalla versione 9 il supporto alla memorizzazione, alla ricerca e all’indicizzazione di documenti XML. Oracle Database 12c estende tale funzionalità ai documenti JSON, introducendo le due implementazioni dell’interfaccia SODA, denominate, come suddetto, SODA for REST e SODA for JAVA, e ponendosi sul mercato come valida alternativa tra i NoSQL-style Document Store.

Oracle NoSQL-style Document Store Capabilities. In Oracle DB 12c, i documenti vengono memorizzati, indicizzati e ricercati senza che il database ne conosca la struttura (schemaless). Ciò lascia agli sviluppatori la libertà di modificare la struttura dei documenti JSON in base alle esigenze. Non esiste un datatype dedicato per memorizzare i documenti JSON, ma gli stessi vengono memorizzati con i tipi standard VARCHAR2, CLOB e BLOB. Viene introdotto il nuovo constraint “IS JSON”, utilizzato per assicurare che il contenuto di una colonna sia un JSON valido, fornendo pieno supporto al trattamento avanzato dei JSON, come disaster recovery, replication, compression ed encryption.

Inoltre, è possibile eseguire delle query SQL direttamente sulle tabelle di documenti JSON del database utilizzando le JSON Path Expressions. Tali espressioni sono equivalenti al linguaggio xPath in XML e sono sintatticamente simili a JavaScript. Si riportano di seguito degli esempi:

JsonPathExpressions.png

SODA API. SODA fornisce un set di API semplice da utilizzare per lavorare con i documenti memorizzati in un Oracle Database. L’oggetto Database, che è richiesto per interagire con le Collections, viene istanziato usando un database connection con le API SQL standard di Oracle. La versione corrente di SODA adotta una strategia di optimistic locking, ma quella di pessimistic locking èsarà probabilmente disponibile nelle future release.

La specifica SODA definisce un set di metodi che forniscono le seguenti funzionalità:

  • Stabilire una connessione ad un Oracle Database Document Store
  • Creare e cancellare una collezione
  • Creare, ricerca, aggiornare e cancellare un documento
  • Elencare i contenuti di una collezione
  • Ricercare una collezione di documenti che “matchino” una espressione Query By Example (QBE)
  • Operazioni di “bulk insert” in una collezione
  • Creazione e cancellazione di indici

Di seguito, riporto alcune caratteristiche dell’implementazione “SODA for JAVA”, tralasciando “SODA for REST” (utile nel caso ci si voglia interfacciare direttamente con il Document Store con il paradigma REST).

SODA for JAVA. Consiste di un set di semplici classi che rappresentano un database, una collezione di documenti e il documento stesso. I metodi che queste classi forniscono permettono di gestire e ricercare le collezioni e i documenti JSON memorizzati. Utilizza una connessione JDBC standard e SQL*NET per comunicare con il database: ciò significa che le API sono transazionali e una serie di operazioni SODA può generare una singola transazione. Poiché SODA utilizza una connessione JDBC, è possibile utilizzare sia le API di SODA for JAVA che quelle tradizionali JDBC.

Di seguito, si riportano le principali classi di “SODA for JAVA” con relativa descrizione:

Classe Descrizione Commenti
OracleClient Classe client generica SODA. L’entry point di SODA per i JSON.
OracleRDBMSClient La classe Client dell’Oracle Database Usata per recuperare l’oggetto OracleDatabase
OracleDatabase Rappresenta un Document Store, il quale consiste di uno o più collezioni. Usato per accedere alle collezioni.
OracleDatabaseAdmin Usato per creare e cancellare collezioni
OracleCollection Rappresenta una collezione di un Document Store
OracleCollectionAdmin Usato per creare e cancellare indici
OracleDocument Rappresenta un documento in un Document Store Aggiorna (o crea) il documento con un dato ID

Struttura di un documento di una collezione. Di seguito, si riporta la struttura SQL di una collezione rappresentata su una tabella Oracle e contenente il JSON in corrispondenza di una colonna CLOB:

Name                                             Null?   Type

—————————————– ——– —————————-

ID                                              NOT NULL VARCHAR2(255)

CREATED_ON                                      NOT NULL TIMESTAMP(6)

LAST_MODIFIED                                   NOT NULL TIMESTAMP(6)

VERSION                                         NOT NULL VARCHAR2(255)

JSON_DOCUMENT                                     CLOB

Le colonne della tabella rappresentano quanto segue:

 ID ID autogenerato del singolo record
 JSON_DOCUMENT Contenuto del documento in JSON
 CREATED_ON Timestamp (autogenerato) di inserimento del record
 LAST_MODIFIED Timestamp (autogenerato) di modifica del record
 VERSION Versione del documento (incrementato automaticamente quando viene modificato)

Per dettagli sulle API di SODA e sulla potenza espressiva delle Query By Example per la ricerca dei documenti di una collezione: http://docs.oracle.com/cd/E63251_01/doc.12/e58124/soda.htm#ADSDA107

Memorizzazione dei documenti JSON (codifica e datatype). Un documento JSON può essere considerato un dato semi-strutturato, ossia non conforme alla struttura formale dei modelli di dato associato con le basi di dati relazionali. Esso, comunque, contiene etichette o altri marcatori per separare gli elementi semantici e rafforzare le gerarchie di record e campi all’interno del dato. E’ anche conosciuto come “dato senza schema” o “dato con struttura autodescritta”.

Oracle raccomanda di memorizzare tali dati utilizzando datatype di tipo LOB, in quanto la quantità di caratteri può essere elevata (maggiore di 4000 byte e, dunque, della massima capacità di un datatype VARCHAR2). I datatype raccomandati per i contenuti testuali sono Characted Large Object (CLOB) e National Character Large Object (NCLOB).

Il datatype CLOB è raccomandato per la memorizzazione di stringhe o documenti di lunghezza fissa. Invece, il datatype NCLOB è raccomandato per quelli a lunghezza variabile.

Riferimento: https://docs.oracle.com/database/121/ADXDB/json.htm#ADXDB6252

Per quanto riguarda il character encoding, conviene adottare quello AL16UTF16 o AL32UTF8. In particolare, Oracle raccomanda l’uso di AL32UTF8 per memorizzare i dati con caratteri Unicode.

Riferimenti:
https://docs.oracle.com/cd/E11882_01/appdev.112/e18294.pdf
https://docs.oracle.com/database/121/NLSPG/ch2charset.htm#NLSPG179

Sicurezza dei dati. Al fine di salvaguardare la sicurezza e l’integrità dei dati,è possibile sfruttare il meccanismo di Oracle Secure Files, il quale consente anche le compressione dei dati memorizzati all’interno di datatype LOB.

Riferimento: https://docs.oracle.com/database/121/ADLOB/toc.htm

Protetto: [BigData&NoSQL] Log Management: un caso d’uso di Big Data e di Operational Intelligence

Il contenuto è protetto da password. Per visualizzarlo inserisci di seguito la password:

[JSON] La specifica JSON-RPC 2.0: alcuni client iOs


masthead-jsonrpc2base
Esiste una specifica alquanto diffusa per la distribuzione di servizi RESTful, con l’ovvio formato di interscambio JSON, nota come JSON-RPC 2.0.

Pe le caratteristiche di dettaglio di questa specifica vi rimando al sito ufficiale: http://www.jsonrpc.org/specification 

In breve, JSON-RPC non è altro che un protocollo per la chiamata di procedure remote, dove è possibile passare al servizio anche oggetto complessi ed avere in output dati multipli. La descrizione del servizio (una sorta di WSDL) è ben definita, come anche i codici di errore e di successo restituiti dalla chiamata. In sintesi, non è altro che un “modo” per la scrittura di servizi RESTful secondo un certo standard.

La cosa interessante è che il servizio remoto viene interrogato tramite i comuni protocolli HTTP o TCP/IP e la serializzazione degli oggetti di output avviene in formato JSON. Una richiesta di servizio è una chiamata a un metodo specifico e che contiene le seguenti proprietà:

  • method – una stringa con il nome del metodo da invocare.
  • params – un array di oggetti da passare come parametri al metodo definito.
  • id un valore di qualsiasi tipo, che viene utilizzato per abbinare la risposta alla richiesta

Esempio:  {“jsonrpc”: “2.0”, “method”: “subtract”, “params”: [42, 23], “id”: 1}

json-rpc2.0 Per poter richiamare questi servizi da un’app mobile iOs, ho provato i seguenti client:

Esiste anche la possibilità di richiamare i servizi JSON-RPC in “modalità nativa”, come descritto da questo tutorial:

[iOs] Paginare i dati di una UITableView

Vi condivido il codice che ho messo su una repo di GitHub, utile per la paginazione dei dati caricati in una UITableView. Questa feature è molto comoda, quando dovete caricare troppi dati da un vostro servizio online e, dunque, la fase costituita dalla chiamata remota stessa, il fetching dei dati e il popolamento delle celle della vostra UITableView potrebbe risultare alquanto onerosa, se non addirittura far crashare la vostra app.

Ecco il link al progettino: https://github.com/fficetola/PaginationScroller

Prima di entrare nel vivo del codice, vi scrivo qui cosa viene fatto nell’app di esempio:

  • viene inizializzata una UITableView e nel metodo loadData, viene fatta una chiamata remota ad un servizio PHP di test (che vi ho anche condiviso nella cartella php-example-paging);
  • l’output in JSON del servizio di test lo potere vedere qui: http://francescoficetola.it/ios-test/test-paging.php?idx_last_element=0&block_size_paging=2. Nel servizio PHP di test, vengono passati due parametri: idx_last_element, ossia l’indice del primo elemento della lista di dati da cui si vuole partire, e block_size_paging, ossia il numero massimo di elementi che si vuol far restituire a partire dall’indice richiesto (nell’esempio precedente, si richiedono i primi 20 risultati dal primo elemento della lista, con indice 0). Il servizio PHP genera 100 elementi e, dunque, se si richiedono 20 elementi per volta, il numero massimo di pagine restituite sarà 5 (total_pages, è il numero massimo di pagine calcolate e restituite per la paginazione, current_page è la pagina corrente del blocco di dati richiesti)
  • Lato app, si intercetta il JSON di risposta e si “fetchano” i dizionari in singoli oggetti di tipo Object. 
  • Quando si scrolla la UITableView fino all’ultima cella, viene visualizzata una UIActivityIndicatorView, che scompare soltanto quando sono stati caricati altri blocchi di dati (si richiama, infatti, il metodo loadData).

Ecco il metodo della UITableView in cui vi è la vera e propria logica di paginazione:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

    //check last cell in uitable
    if(indexPath.row==self.objects.count-1){

        //Create and add the Activity Indicator to splashView
        UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        activityIndicator.alpha = 1.0;
        //activityIndicator.center = CGPointMake(160, 360);
        activityIndicator.hidesWhenStopped = NO;
        [activityIndicator startAnimating];

        self.tableView.tableFooterView = activityIndicator;

        //arrest condition
         if(_currentPage<_totalPages){

             _lastIndex = indexPath.row+1;
             //timer...
             [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(loadData) userInfo:nil repeats:NO];

         }
         else{
             [activityIndicator stopAnimating];
             self.tableView.tableFooterView = nil;
         }

         [activityIndicator release];

    }
}

Il metodo willDisplayCell scatta per tutte le celle della UITableView, ogniqualvolta ognuna di queste viene visualizzata. Poiché a noi interessa rilevare soltanto quando è stata visualizzata l’ultima cella della tabella, vi è il seguente controllo:

if(indexPath.row==self.objects.count-1)

In tal caso, viene aggiunto nel footer della UITableView un UIActivityIndicatorView e, se non è verificata la condizione di arresto della paginazione, viene fatta una chiamata al metodo loadData in cui si richiama di nuovo il servizio remoto per la restituizione di un nuovo blocco di dati a partire dal lastIndex aggiornato.

Per determinare la condizione di arresto della paginazione, si va a controllare se la pagina corrente caricata (currentPage) sia l’ultima (totalPage):

if(_currentPage<_totalPages)

Se non si verifica tale condizione, viene eliminato lo spinner dal footer e viene terminata la paginazione (non viene più richiamato il metodo loadata).

UITableView pagination

Creative Commons License
This work by Francesco Ficetola is licensed under a Creative Commons Attribution 4.0 International License.
Based on a work at www.francescoficetola.it.
Permissions beyond the scope of this license may be available at http://www.francescoficetola.it/2013/03/31/ios-paginare-i-dati-di-una-uitableview/.

[iOS] Come effettuare chiamate asincrone

La modalità sicuramente più performante per richiamare dei servizi online di back-end da una vostra app è quella delle chiamate asincrone.

1° METODO (con dispatch_async). Il classico metodo in iOS, che definirei “nativo”, è quello che utilizza il dispatch_async:

// Add to top of file
#import <;dispatch/dispatch.h>;

// Use it
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     NSData* responseData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://tuo-sito/servizio"]];

/* ... your code here ...*/

});
}

Non occorre importare nessuna libreria. Nella responseData vi è la risposta HTTP, da cui potete recuperare i vostri dati. Tale metodo richiama il Grand Central Dispatcher per eseguire la chiamata e ricevere una risposta (asincrona) dal server in background. Intanto, la vostra app continua ad eseguire sul thread principale il lavoro, senza bloccarsi in attesa dei dati del servizio richiamato.

2° METODO (con ASIHTTPRequest ). Il metodo che, invece, preferisco è quello che utilizza l’utilissima libreria ASIHTTPRequest, che ci permette di effettuare in modo semplice chiamate HTTP (sincrone e asincrone) e interagire con servizi REST (vedi l’articolo “La filosofia REST“, su questo blog). E’ una libreria particolarmente utile per effettuare anche upload e download di file, chiamate con autenticazione sicura HTTPS, e molto altro … e vi consiglio vivamente di provarla.

La potete scaricare direttamente dal sito ufficiale: ASIHTTP-request downloads

Come si spiega qui: Setup-Instructions, per installarla nel vostro progetto, occorre importare le seguenti librerie/framework (che trovate direttamente in xCode – click sulla root di progetto >> Build Phases >> Link Binary With Libraries):

  • CFNetwork,
  • SystemConfiguration
  • MobileCoreServices
  • libz.dylib (attualmente c’è libz.1.2.5.dylib)
Ecco un esempio di chiamata in POST ad un servizio REST che restituisce un JSON:
//import libs
#import "ASIFormDataRequest.h"

/* ... your code in controller ...*/

- (void)fetchedData:(NSData *)responseData {

    NSArray* json = [NSJSONSerialization
                     JSONObjectWithData:responseData
                     options:kNilOptions error:nil];

    NSMutableArray *postTMP = [[NSMutableArray alloc] initWithCapacity:[json count]];

    for (NSDictionary *dict in json) {
        Post *post = [[Post alloc] init];
        post.titolo = [dict objectForKey:@"titolo"];
        post.articolo = [dict objectForKey:@"articolo"];
        post.snippet = [dict objectForKey:@"snippet"];
        post.urlImage = [dict objectForKey:@"urlImage"];
        post.urlThumb = [dict objectForKey:@"urlThumb"];
        [postTMP addObject:post];
    }

    //mi assicuro che l'array non possa essere modificato.
    self.posts = [postTMP copy];
    NSLog(@"posts count: %d",[self.posts count]);

    [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];
}

- (void)requestFinished:(ASIHTTPRequest *)request {

    NSLog(@"Response %d ==>; %@", request.responseStatusCode, [request responseString]);
    NSData *responseData = request.responseData;

    [self performSelectorOnMainThread:@selector(fetchedData:) withObject:responseData waitUntilDone:YES];

}

- (void)requestFailed:(ASIHTTPRequest *)request
{
    //[progressAlert release];
    NSError *error = [request error];
    NSLog(@"ERROR %@",error);

    if ([[request error] code] == ASIConnectionFailureErrorType  ||
        [[request error] code] == ASIRequestTimedOutErrorType){
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ERROR", nil) message:NSLocalizedString(@"CONNECTION_FAILED",nil)
            delegate:self cancelButtonTitle:NSLocalizedString(@"OK",nil) otherButtonTitles:nil];
        [alert show];
        [alert release];
        //return;

    }
    else{
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ERROR",nil) message:
        NSLocalizedString(@"GENERIC_ERROR",nil)
            delegate:self cancelButtonTitle:NSLocalizedString(@"OK",nil) otherButtonTitles:nil];
        [alert show];
        [alert release];
        //return; 
    }  
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSURL *url = [NSURL URLWithString:@"http://francescoficetola.it/services/test.php"];

    ASIFormDataRequest *myRequest = [ASIFormDataRequest requestWithURL:url];

    [myRequest setDefaultResponseEncoding:NSUTF8StringEncoding];
    [myRequest setResponseEncoding:NSUTF8StringEncoding];
    [myRequest setRequestMethod:@"POST"];
    [myRequest setPostValue:@"valueMyParameter" forKey:@"myParameter"];

    [myRequest addRequestHeader:@"Accept" value:@"application/json"];
    [myRequest addRequestHeader:@"Content-Type" value:@"application/json; charset=UTF-8;"];
    [myRequest setDelegate:self];

    [myRequest startAsynchronous];

}
Nel metodo viewDidLoad, si prepara la richiesta asincrona in POST, settando i valori da inserire nell’header HTTP, come il Content-Type, lo User-Agent e la codifica da utilizzare. Con il comando startAsynchronous, la richiesta viene inviata al servizio specificato nella URL. Per effettuare una chiamata in GET, occorre modificare in setRequestMethod:@”GET”

 

Occorre definire poi i due metodi di success ed error callback:
  • requestFailed: è il metodo che viene richiamato se la risposta non è andata a buon fine. E’ possibile intercettare anche il relativo status code, per visualizzare i relativi messaggi di errore all’utente;
  • requestFinished: è il metodo che viene richiamato in caso di ricezione con successo della risposta, raccolta dalla variabile responseData. L’elaborazione di quest’ultima viene effettuata con un metodo (che non fa parte della libreria ASIHTTPRequest) che, in un thread di background, estrae dal JSON una lista di oggetti (in questo esempio, i post di un forum), ricaricandoli poi in una UITableView (con il metodo nativo reloadData).
In alternativa, è possibile dichiarare le funzioni di callback di successo ed errore direttamente prima della chiamata asincrona, definendo dei blocks:
/*  ...  */   

   [request setDelegate:self];
   [request setCompletionBlock:^{         
        NSString *responseString = [request responseString];
        NSLog(@"Response: %@", responseString);

        /* your code here in success case */

    }];
    [request setFailedBlock:^{
        NSError *error = [request error];
        NSLog(@"Error: %@", error.localizedDescription);

       /* your code here in error case */

    }];

   [request startAsynchronous];

/*  ...  */
Direi che entrambi i metodi sono abbastanza semplici, ma quello con la ASIHTTPRequest offre sicuramente maggiore controllo e un ricco set di funzionalità per la connettività ai nostri servizi. Eccole tutte: http://allseeing-i.com/ASIHTTPRequest/How-to-use
Creative Commons License
This work by Francesco Ficetola is licensed under a Creative Commons Attribution 4.0 International License.
Based on a work at www.francescoficetola.it.
Permissions beyond the scope of this license may be available at http://www.francescoficetola.it/2012/11/03/ios-come-effettuare-chiamate-asincrone/.

[REST] La filosofia REST: il Web ha già tutto quello di cui si ha bisogno!

L’argomento stavolta è REST (REpresentational State Transfer), vera e propria filosofia informatica. Infatti, la prima volta di cui se ne parlò fu nel 2000, nel capitolo 5 della tesi di PhD in Filosofia Informatica di Roy Fielding, uno dei principali autori delle specifiche HTTP, dal titolo Architectural Styles and the Design of Network-based Software Architectures.

Ma cosa è veramente REST? Uno standard, un modello o cosa? Purtroppo non è ancora un vero e proprio standard e, forse, non è neanche giusto che lo sia. Definisce un modello di progettazione di architetture software o, elegantemente, uno “stile architetturale”. L’idea di base del REST è quella di vedere il Web (ma anche altri “sistemi”)  come una piattaforma per l’elaborazione distribuita dei dati. E il Web ha già tutto quello di cui si ha bisogno, ossia l’infrastruttura che si basa sul protocollo HTTP e le informazioni, che diventano vere e proprie risorse. Non ha senso inserire un overhead ulteriore e, dunque, uno strato software come avviene con i Web Services in SOAP.

Riporto un passo estratto dalla tesi di Fielding in cui si introduce il senso di questo stile:

The Representational State Transfer (REST) style is an abstraction of the architectural elements within a distributed hypermedia system. REST ignores the details of component implementation and protocol syntax in order to focus on the roles of components, the constraints upon their interaction with other components, and their interpretation of significant data elements. It encompasses the fundamental constraints upon components, connectors, and data that define the basis of the Web architecture, and thus the essence of its behavior as a network-based application. […]

REST emphasizes scalability of component interactions, generality of interfaces, independent deployment of components, and intermediary components to reduce interaction latency, enforce security, and encapsulate legacy systems. I describe the software engineering principles guiding REST and the interaction constraints chosen to retain those principles, contrasting them to the constraints of other architectural styles.

 

Questo stile è diventato molto in voga negli ultimi anni per la realizzazione di Web Services altamente scalabili ed efficienti. Per approfondire l’argomento e per esempi pratici, ecco il link alle lezioni di HTML.it: RESTful Web Services – La guida, da cui ho estratto gran parte delle informazioni qui di seguito riportate.

Ecco i 5 principi su cui si basa il REST:

  • identificazione delle risorse
  • utilizzo esplicito dei metodi HTTP
  • risorse autodescrittive
  • collegamenti tra risorse
  • comunicazione senza stato
Una risorsa è qualsiasi elemento oggetto su cui è possibile effettuare operazioni ed è identificabile univocamente (attraverso una URI – Uniform Resource Identifier).
Il primo passo consiste nell’individuare le risorse da esporre tramite il Web Service, dove la loro rappresentazione “esterna” verso il client non sempre rispecchia l’implementazione o il modello interno dell’applicazione web.
Gli URI devono essere autoesplicativi, per far capire di che risorsa si tratta. Dopo aver trovato il modo di identificare le risorse, occorre definire quali operazioni effettuare su di esse. Si sfrutta quello che già si ha, ossia i metodi HTTP come GET, POST, PUT e DELETE.

 

Semplici regole nella definizione delle URI:
  • preferire l’utilizzo di nomi a quello di verbi
  • contenere la lunghezza degli URI
  • preferire uno schema posizionale, ossia una struttura gerarchica di un percorso piuttosto che la presenza di più argomenti: http://www.mionegozio.com/ordini/2011/07/01 al posto di http://www.mionegozio.com/ordini/?anno=2001&mese=07&giorno=0
  • evitare le estensioni, che legano un Web Service alla sua implementazione: ad esempio, è da preferire un URI del tipo:
http://www.mionegozio.com/ordini/?id=123
ad uno del tipo:
http://www.mionegozio.com/ordini/ordine.php?id=123

 

REST, quindi, non fa altro che mappare uno a uno le tipiche operazioni CRUD (creazione, lettura, aggiornamento, eliminazione di una risorsa) e i metodi HTTP:
  • POST –> Create (Crea un nuova risorsa)
  • GET –> Read (Ottiene una risorsa esistente)
  • PUT –> Update (Aggiorna una risorsa o ne modifica lo stato)
  • DELETE –> Delete (Elimina una risorsa)
Il tipo di rappresentazione di una risorsa inviata dal Web Service al client è indicato nella stessa risposta HTTP tramite un tipo MIME e il client stesso può anche richiedere una risorsa in uno specifico formato sfruttando l’attributo Accept di una richiesta HTTP di tipo GET. Grazie alla possibilità di rappresentazioni multiple possiamo progettare una applicazione web offrendo un gli output di un servizio in formati differenti a seconda del client. Formati più utilizzati sono XML, (X)-HTML e JSON. Su quest’ultimo trovate anche un articolo su questo sito: [JSON] Introduzione al diffuso formato di scambio dei dati
Alcuni framework sono in grado di gestire in automatico la cosiddetta content negotiation, cioè la fornitura di una rappresentazione nel formato richiesto dal client.

 

Le interazioni tra client e server devono essere senza stato, rispettando la comunicazione stateless nativa del protocollo HTTP. La gestione dello stato (se necessaria) avviene sul client, ottimizzando le prestazioni del server che non deve curare lo stato di una sessione e può essere effettuato con l’uso di chiavi di sessione, come i cookie.
Il fatto che i principi REST escludano la gestione dello stato della comunicazione non deve però far pensare che i Web Service RESTful siano senza stato. L’acronimo REST sta per REpresentational State Transfer, sottolineando proprio la centralità della gestione dello stato in un sistema distribuito. Lo stato che REST prende in considerazione è però quello delle risorse e dell’intera applicazione.

  • Lo stato delle risorse è dato dall’insieme dei valori delle caratteristiche di una risorsa in un dato momento. Un Web Service è responsabile della gestione dello stato delle risorse. Un client può accedere allo stato di una risorsa tramite le diverse rappresentazioni della risorsa stessa e contribuire a modificarlo per mezzo dei metodi PUT, POST e DELETE dell’HTTP.
  • Lo stato del client è rappresentato dall’insieme del contesto e delle risorse ottenute in uno specifico momento. Il server può influenzare le transizioni di stato del client inviando differenti rappresentazioni in risposta alle sue richieste.
  • Lo stato dell’applicazione, cioè del risultato dell’interazione tra client e server, è dato dallo stato del client e delle risorse gestite dal server. Lo stato dell’applicazione determina le modalità di modifica dello stato delle risorse e del client.

A differenza di quanto avviene in buona parte delle applicazioni Web, dove lo stato dell’applicazione viene spesso mantenuto dal server insieme allo stato della comunicazione, lo stato dell’applicazione in un’architettura RESTful è il frutto della collaborazione di client e server, ciascuno con i propri ruoli e responsabilità.

Come da acronimo, una applicazione passa da uno stato all’altro, principio noto come HATEOAS, usando i collegamenti ipertestuali: una applicazione è intesa come rete di risorse in cui un client naviga seguendo i collegamenti ammissibili tra una risorsa e l’altra, come una macchina a stati finiti.

Un bel articolo sulle differenze tra SOAP e RESTful, lo potete leggere sempre su HTML.it:

RESTful VS. SOAP. REST propone una visione del Web incentrata sul concetto di risorsa mentre i SOAP Web Service mettono in risalto il concetto di servizio.

  • Un Web Service RESTful è custode di un insieme di risorse sulle quali un client può chiedere le operazioni canoniche del protocollo HTTP
  • Un Web Service basato su SOAP espone un insieme di metodi richiamabili da remoto da parte di un client

L’approccio dei SOAP Web service ha mutuato un’architettura applicativa denominata SOAService Oriented Architecture, a cui si è recentemente contrapposta l’architettura ROAResource Oriented Architecture, ispirata ai principi REST. SOAP utilizza HTTP come protocollo di trasporto, ma non è limitato né vincolato ad esso, dal momento che può benissimo usare altri protocolli di trasporto. SOAP non sfrutta a pieno il protocollo HTTP, utilizzandolo come semplice protocollo di trasporto. REST invece sfrutta HTTP per quello che è, un protocollo di livello applicativo, e ne utilizza a pieno le potenzialità.

È evidente che l’approccio adottato dai Web Service basati su SOAP è derivato dalle tecnologie di interoperabilità esistenti al di fuori del Web e basato essenzialmente su chiamate di procedura remota, come DCOM, CORBA e RMI. In sostanza questo approccio può essere visto come una sorta di adattamento di queste tecnologie al Web.

Inoltre i Web Service basati su SOAP prevedono lo standard WSDLWeb Service Description Language, per definire l’interfaccia di un servizio. Questa è un’ulteriore evidenza del tentativo di adattare al Web l’approccio di interoperabilità basato su chiamate remote. Infatti il WSDL non è altro che un IDL (Interface Description Language) per un componente software. Da un lato l’esistenza di WSDL favorisce l’uso di tool per creare automaticamente client in un determinato linguaggio di programmazione, ma allo stesso tempo induce a creare una forte dipendenza tra client e server.

REST non prevede esplicitamente nessuna modalità per descrivere come interagire con una risorsa. Le operazioni sono implicite nel protocollo HTTP. L’approccio REST tende a conservare e ad esaltare le caratteristiche intrinseche del Web evidenziandone la predisposizione ad essere una piattaforma per l’elaborazione distribuita. Quindi, non è necessario aggiungere nulla a quanto è già esistente sul Web per consentire ad applicazioni remote di interagire.

[NO-SQL] Il Teorema di CAP e il No-SQL data model

Volendo continuare la serie sulla “filosofia” NO-SQL e sulle prerogative per cui è necessaria (vedi l’articolo riportato su questo blog, “NO-SQL e introduzione a MongoDB“), estrapolo qui le motivazioni e le caratteristiche lette negli articoli riportati su BigDataNERD:

Perché NO-SQL?

Per poter rispondere alla domanda occorre esaminare il “teorema di CAP” e ricordare che un concetto fondamentale degli RDBMS è quello delle “ACID transactions” (A=Atomicity, C=Consistency, I=Isolation, D=Durability)

  • A come Atomicità. La transazione è indivisibile, ossia o tutti gli statements che la compongono vengono applicati ad un database o nessuno.
  • C come Consistenza. Il database deve rimanere in uno stato consistente prima e dopo l’esecuzione di una transazione, quindi non devono verificarsi contraddizioni (inconsistency) tra i dati archiviati nel DB.
  • I come Isolamento. Quando transazioni multiple vengono eseguite da uno o più utenti simultaneamente, una transazione non vede gli effetti delle altre transazioni concorrenti.
  • D come Durabilità. Quando una transazione è stata completata (con un commit), i suoi cambiamenti diventano persistenti, ossia non vengono più persi.

I più comuni e diffusi RDBMS supportano le transazioni ACID e i principi su esposti sembrerebbero bastare. Ma dov’è il problema?

Nell’era del Web 2.0, le applicazioni devono lavorare su bilioni e trilioni di dati ogni giorno e la scalabilità è un concetto che in tale ambito ricopre un ruolo importantissimo. Per questo motivo i database hanno bisogno di essere distribuiti sulla rete per realizzare una scalabilità orizzontale. Esaminiamo qui il concetto di “CAP theorem” per valutare le caratteristiche di un siffatto sistema di storage distribuito dei dati.

Il teorema di CAP fu teorizzato da Eric Brewer nel 2000 (C=Consistency, A=Availability, P=Partition-Tolerance). Per maggiori informazioni si veda il seguente articolo: ACID vs. BASE il Teorema di CAP (di Stefano Pedone).

Nell’ambito di un sistema di storage distribuito, i 3 principi hanno la seguente definizione:

Consistency: se viene scritto un dato in un nodo e viene letto da un altro nodo in un sistema distribuito, il sistema ritornerà l’ultimo valore scritto (quello consistente).

Availability: Ogni nodo di un sistema distribuito deve sempre rispondere ad una query a meno che non sia indisponibile.

Partition-Tolerance: è la capacità di un sistema di essere tollerante ad una aggiunta o una rimozione di un nodo nel sistema distribuito (partizionamento) o alla perdita di messaggi sulla rete.

Secondo la teoria CAP, è impossibile garantire tutti e tre i principi del teorema di CAP. Infatti, si consideri un sistema distribuito e si supponga di aggiornare un dato su un nodo 1 e di leggerlo da un nodo 2, si verificheranno le seguenti conseguenze:

  1. Il nodo 2 deve ritornare l’ultima “miglior” versione del dato (quella consistente) per non violare il principio della Consistenza
  2. Si potrebbe attendere la propagazione del dato modificato nel nodo 2 e, quest’ultimo, potrebbe mettersi in attesa della versione più aggiornata, ma, in un sistema distribuito, si ha un’alta possibilità di perdita del messaggio di aggiornamento e il nodo 2 potrebbe attenderlo a lungo. Così non risponderebbe alle query (indisponibilità), violando il principio dell’Availability
  3. Se volessimo garantire sia la Consistency che l’Availability, non dovremmo partizionare la rete, violando il principio del Partition-Tolerance

Le web applications progettate in ottica Web 2.0, caratterizzate da architetture altamente scalabili e profondamente distribuite con politiche di prossimità geografica, puntano in maniera forte sulla garanzia di alte prestazioni ed estrema disponibilità dei dati. Si trovano quindi ad essere altamente scalabili e partizionate. La filosofia NO-SQL è sbilanciata verso la ridondanza dei nodi e la scalabilità.

Di seguito, si riporta una tabella dei maggiori database NO-SQL del momento:

Il NoSQL data model può essere implementato seguendo differenti approcci a seconda delle strutture dati con cui si rappresentano i record di dato.

  • Famiglie di colonne o “wide column store” (come Cassandra e HBase): le informazioni sono memorizzate in colonne, in coppie chiave/valore. Tipicamente sono usati nell’ambito della memorizzazione distribuita dei dati;
  • Document Store (come CouchDB, MongoDB): le informazioni sono organizzate in “Document“, rappresentati in XML, JSON o BSON, e l’accesso ai dati avviene attraverso API che interrogano veri e propri dizionari. Sono molto usati nello sviluppo di attuali web applications;
  • Key/Value Store (come Membase, Redis): la struttura dati di memorizzazione è una Hash Table (coppia chiave-valore). Indicate per sistemi di caching o Hash Table distribuite;
  • Graph Data Store (come Neo4J, InfoGrid, OrientDB). Il modello di programmazione è il grafo e l’accesso ad archi e nodi avviene attraverso algoritmi di ricerca su grafo. Tipicamente si usano nei social network.

 

[JSON] Introduzione al diffuso formato di scambio dei dati

JSON (JavaScript Object Notation) è un semplice formato per lo scambio di dati. Per le persone è facile da leggere e scrivere, mentre per le macchine risulta facile da generare e analizzarne la sintassi. Si basa su un sottoinsieme del Linguaggio di Programmazione JavaScript, Standard ECMA-262 Terza Edizione – Dicembre 1999.

JSON è un formato di testo completamente indipendente dal linguaggio di programmazione, ma utilizza convenzioni conosciute dai programmatori di linguaggi della famiglia del C, come C, C++, C#, Java, JavaScript, Perl, Python, e molti altri. Questa caratteristica fa di JSON un linguaggio ideale per lo scambio di dati.

JSON è basato su due strutture:

  • Un insieme di coppie nome/valore. In diversi linguaggi, questo è realizzato come un oggetto, un record, uno struct, un dizionario, una tabella hash, un elenco di chiavi o un array associativo.
  • Un elenco ordinato di valori. Nella maggior parte dei linguaggi questo si realizza con un array, un vettore, un elenco o una sequenza.

Queste sono strutture di dati universali. Virtualmente tutti i linguaggi di programmazione moderni li supportano in entrambe le forme. E’ sensato che un formato di dati che è interscambiabile con linguaggi di programmazione debba essere basato su queste strutture.

In JSON:

  • Un oggetto è una serie non ordinata di nomi/valori. Un oggetto inizia con { (parentesi graffa sinistra) e finisce con } (parentesi graffa destra). Ogni nome è seguito da : (due punti) e la coppia di nome/valore sono separata da , (virgola).

  • Un array è una raccolta ordinata di valori. Un array comincia con [ (parentesi quadra sinistra) e finisce con ] (parentesi quadra destra). I valori sono separati da , (virgola).

  • Un valore può essere una stringa tra virgolette, o un numero, o vero o falso o nullo, o un oggetto o un array. Queste strutture possono essere annidate.

  • Una stringa è una raccolta di zero o più caratteri Unicode, tra virgolette; per le sequenze di escape utilizza la barra rovesciata. Un singolo carattere è rappresentato come una stringa di caratteri di lunghezza uno. Una stringa è molto simile ad una stringa C o Java.

  • Un numero è molto simile ad un numero C o Java, a parte il fatto che i formati ottali e esadecimali non sono utilizzati.

I caratteri di spaziatura possono essere inseriti in mezzo a qualsiasi coppia di token.

A parte alcuni dettagli di codifica, questo descrive totalmente il linguaggio.

Fonte: http://www.json.org/json-it.html