In questo tutorial vi mostrerò, con l’aiuto di un semplice esempio, quanto sia immediato realizzare WebService con Jersey. Con altre tecnologie, che richiedono settaggi complessi, spesso si ricorre a generatori automatici che complicano il codice e lo rendono illeggibile. In questo post, simulando l’interazione con una semplice anagrafica di persone, dimostreremo che con Jersey bastano poche annotazioni per costruire un WebService. Prima di passare all’esempio ripassiamo qualche semplice concetto di base.
Introduzione all’architettura REST
I WebService consentono l’interazione tra applicazioni sviluppate con linguaggi di programmazione differenti e che risiedono su sistemi operativi eterogenei. Attualmente esistono due approcci: SOAP (Simple Object Access Protocol) e REST (REpresentational State Transfer).
I sistemi che seguono i principi REST, spesso definiti anche “RESTful“, sono caratterizzati dalla maggiore leggerezza e facilità di implementazione del codice rispetto a SOAP.
REST mappa le tipiche operazioni CRUD (creazione, lettura, aggiornamento, eliminazione) sui metodi HTTP:
- POST – crea un nuova risorsa,
- GET – ottiene una risorsa esistente,
- PUT – aggiorna una risorsa,
- DELETE – elimina una risorsa.
La rappresentazione dei dati è fornita nei formati: testo, XML, XHTML e JSON.
Il nostro esempio
Per questo esempio introduttivo utilizzeremo Jersey, un framework open source che implementa JAX-RS, API annotation-based grazie a cui è veramente semplice creare WebService di tipo RESTFul.
Per facilitarci il lavoro utilizzeremo Apache Maven. Iniziamo definendo un nuovo progetto con archetype “maven-archetype-webapp” che chiameremo “testJersey” (per chi non avesse familiarità con la costruzione di progetti Maven su Eclipse consiglio ) e con le seguenti caratteristiche:
Group Id: it
Artifact Id: testJersey
Version: 0.0.1-SNAPSHOT
Packaging: war
Nel file pom.xml inseriamo le dipendenze richieste per creare un progetto con Jersey:
asm asm-all 3.1 com.sun.jersey jersey-json 1.13 com.sun.jersey jersey-server 1.13 com.sun.jersey jersey-servlet 1.13 javax.ws.rs jsr311-api 1.1.1 javax.servlet javax.servlet-api 3.0.1
Per attivare Jersey è necessario modificare il file web.xml aggiungendo il parametro com.sun.jersey.config.property.package
che definisce in quale pacchetto Jersey cercherà le classi del WebService.
UsersDataService com.sun.jersey.spi.container.servlet.ServletContainer com.sun.jersey.config.property.packages it.test.rest.webservice 1
Mentre nel parametro url-pattern
definiamo la porzione di base dell’URL, nel nostro caso useremo il percorso “rest” per esporre il servizio.
UsersDataService /rest/*
Passiamo ora alla definizione dei moduli applicativi. All’interno della cartella src.main.java
definiamo il pacchetto it.test.rest.pojo
nel quale inseriamo la classe POJO Persona
che contiene alcuni dati per gestire una semplice anagrafica di persone:
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlRootElement @XmlType(propOrder = { "id", "nome", "cognome", "eta", "telefono", "email" }) public class Persona { private int id; private String nome; private String cognome; private int eta; private String telefono; private String email; public Persona() {} public Persona(Integer Id, String Nome, String Cognome, Integer Eta, String Telefono, String Email) { this.id = Id; this.nome = Nome; this.cognome = Cognome; this.eta = Eta; this.telefono = Telefono; this.email = Email; } // i getter e setter non sono riportati
All’interno della classe Persona sono presenti annotazioni secondo lo standard JAXB.
JAXB (Java Architecture for XML Binding) è uno standard che definisce come gli oggetti Java vengono convertiti da e verso XML e JSON grazie all’uso delle annotazioni.
Nello specifico le annotazioni che abbiamo usato sono:
-
@XmlRootElement
: esegue automaticamente il bind della classe nei formati XML o JSON, -
@XmlType(propOrder = {})
: definisce l’ordine di visualizzazione dei campi.
Per simulare l’accesso ai dati di un database utilizzeremo la classe PersonaDao
, su questo aspetto non ci soffermeremo molto per non perdere il focus sull’argomento.
Definiamo un pacchetto it.test.rest.dao
nel quale creiamo la classe PersonaDao, che contiene 2 metodi:
- il primo carica una lista di oggetti persona da esporre nelle chiamate GET del servizio,
- il secondo simula il caricamento di un oggetto Persona a fronte di una chiamata POST.
package it.test.rest.pojo; package it.test.rest.dao; import it.test.rest.pojo.Persona; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class PersonaDAO { public ListPersonaDAO() { List personaList = new ArrayList (); Persona persona = new Persona(1, "Mauro", "Rossi", 40, "02/456327819", ""); personaList.add(persona); persona = new Persona(2, "Luca", "Bianchi", 45, "02/918273645", ""); personaList.add(persona); persona = new Persona(3, "Franco", "Neri", 32, "02/987123546", ""); personaList.add(persona); persona = new Persona(4, "Paolo", "Verdi", 35, "02/129834765", ""); personaList.add(persona); return personaList; } public void addPersona(Persona persona) { // Eseguo l'inserimento nel Database System.out.println("Dettagli persona inserita: " + persona.getId() + "; " + persona.getNome() + "; " + persona.getCognome() + "; " + persona.getEta() + "; " + persona.getTelefono() + "; " + persona.getEmail()); } }
A questo punto creiamo il WebService che consiste in una semplice classe con metodi annotati con @GET
, @POST
, @PUT
e @DELETE
già visti in precedenza.
Ogni metodo rappresenta un’interfaccia per l’elaborazione richiesta. I primi due metodi restituiscono la lista delle persone in formato testo (utilizzata per chiamate da browser) o XML/JSON (per chiamate applicative). Il terzo metodo calcola l’età media ed espone all’esterno il risultato in formato stringa.
L’ultimo metodo esegue il salvataggio di nuovi record a fronte di una chiamata POST attraverso un form di inserimento dati. L’annotazione @Path
indica la URI a cui la risorsa sarà raggiungibile, e può essere posta a livello di metodo o della dichiarazione della classe. L’annotazione @Produces
specifica uno o più MIME-Type con cui la risorsa può essere trasferita al client che ne fa richiesta. Questo ci permette di definire il tipo di chiamata da effettuare.
package it.test.rest.webservice; import it.test.rest.dao.PersonaDAO; import it.test.rest.pojo.Persona; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Request; import javax.ws.rs.core.UriInfo; @Path("/persona") public class PersDataService { / / Restituzione lista degli persone in formato testo. @GET @Produces(MediaType.TEXT_XML) public ListgetPeopleBrowser() { List people = new ArrayList (); PersonaDAO PersDao = new PersonaDAO(); people.addAll(PersDao.PersonaDAO()); return people; } // restituzione lista in formato XML/JSON per chiamate applicative. @GET @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public List getPeople() { List people = new ArrayList (); PersonaDAO PersDao = new PersonaDAO(); people.addAll(PersDao.PersonaDAO()); return people; } // Questo metodo calcola ed espone l’età media delle persone @GET @Path("etamedia") @Produces(MediaType.TEXT_PLAIN) public String getEta() { List people = new ArrayList (); PersonaDAO perdao = new PersonaDAO(); people.addAll(perdao.PersonaDAO()); int toteta = 0; for (Persona pers : people) { toteta += pers.getEta(); } int etamedia = toteta / people.size(); return Integer.toString(etamedia); } // Metodo di salvataggio di nuovi record a fronte di una chiamata POST @POST @Produces(MediaType.TEXT_HTML) @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public void nuovaPersona( @FormParam("id") String id, @FormParam("nome") String nome, @FormParam("cognome") String cognome, @FormParam("eta") String eta, @FormParam("telefono") String telefono, @FormParam("email") String email, @Context HttpServletResponse servletResponse ) throws IOException { Persona persona = new Persona(new Integer(id),nome,cognome, Integer.parseInt(eta),telefono,email); PersonaDAO perdao = new PersonaDAO(); perdao.addPersona(persona); } }
La struttura finale dell’esempio è la seguente:
A questo punto è possibile testare il progetto. Per chi come me utilizza Eclipse, è necessario eseguire il comando “Run As, Maven install”, che attiva una serie di fasi del processo di build tra cui compilazione, esecuzione di unit test. Se tutto è stato configurato correttamente, eseguendo l’applicazione e richiamando l’URL: testJersey/rest/persona
, otterremo il risultato visualizzato dalla seguente immagine.
Conclusioni
Eccoci arrivati al termine, con tre semplici classi annotate abbiamo creato un WebService! Per chiudere il cerchio è importante capire come utilizzare i servizi appena creati. Nel prossimo articolo creeremo una Web Application con PrimeFaces e vedremo come estrarre, manipolare e inserire i dati grazie all’interazione con il WebService appena definito.
Il progetto testJersey è interamente consultabile su GitHub.