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 questo articolo) 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:
[code lang=”xml”]
<dependency>
<groupId>asm</groupId>
<artifactId>asm-all</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.13</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.13</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.13</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
[/code]

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.

[code lang=”xml”]
<servlet>
<servlet-name>UsersDataService</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>it.test.rest.webservice</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
[/code]

Mentre nel parametro url-pattern definiamo la porzione di base dell’URL, nel nostro caso useremo il percorso “rest” per esporre il servizio.

[code lang=”xml”]
<servlet-mapping>
<servlet-name>UsersDataService</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
[/code]
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:
[code lang=”java”]
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
[/code]
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.

[code lang=”java”]
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 List<Persona> PersonaDAO() {
List<Persona> personaList = new ArrayList<Persona>();
Persona persona = new Persona(1, "Mauro", "Rossi", 40, "02/456327819", "mrossi@email.it");
personaList.add(persona);
persona = new Persona(2, "Luca", "Bianchi", 45, "02/918273645", "lbianchi@email.it");
personaList.add(persona);
persona = new Persona(3, "Franco", "Neri", 32, "02/987123546", "fneri@email.it");
personaList.add(persona);
persona = new Persona(4, "Paolo", "Verdi", 35, "02/129834765", "pverdi@email.it");
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());
}
}
[/code]

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.

[code lang=”java”]
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 List<Persona> getPeopleBrowser() {
List<Persona> people = new ArrayList<Persona>();
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<Persona> getPeople() {
List<Persona> people = new ArrayList<Persona>();
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<Persona> people = new ArrayList<Persona>();
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);
}
}
[/code]
La struttura finale dell’esempio è la seguente:
progetto
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: http://localhost:8080/testJersey/rest/persona, otterremo il risultato visualizzato dalla seguente immagine.
risultato0

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.

11 thoughts on “Webservice RESTful con Jersey”

  1. “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”
    Fallo al più presto… ti prego 😉
    Grazie, saluti e complimenti per l’eccezionale lavoro che fate.

  2. Innanzitutto complimenti per l’articolo (e anche per la controparte PrimeFaces)! Vorrei chiedere se potresti integrarlo anche con la parte di autenticazione dell’utente e magari fornire qualche accenno sulla sicurezza. Con queste aggiunte diventerebbe il top degli articoli e più vicino ad un’implementazione reale!
    Comunque grazie per il grandissimo lavoro!

    1. Ciao fly66, la tua è sicuramente una grande idea e potrebbe essere un ottimo spunto per un nuovo post. Si potrebbe pensare di implementare Spring Security nel nostro Web Service! Seguici su CNJ! Potresti trovare la sorpresa, magari sotto l’albero di Natale…

  3. Mauro grazie veramente per il bel post, hai fatto un tutorial veramente chiaro ed immediato.
    Vorrei chiederti una cosa per un’incertezza che mi è rimasta.. con quale meccanismo si discrimina tra le due possibilità di ricevere un xml o un json? puoi fare un brevissimo esempio? grazie ancora, ciao

    1. Grazie 1000 Andrea del complimento e scusami se ti rispondo solo ora! XML è un protocollo più conosciuto e più adatto se si vuole inviare dati verso un’ampia gamma di utenti con software eterogeneo. D’altro canto JSON ha una rappresentazione dei dati più compatta in termini di byte, per cui può essere utile quando si vede scambiare un’intensa mole di dati e la banda è limitata, come ad esempio per i dispositivi mobile.

  4. scusate ma è possibile trasformare un job datastage in un servizio REST. Cioè posso esporre un job come servizio? Come si può fare?

  5. Buongiorno Mauro, anzitutto complimenti per l’articolo, ho seguito il tutorial passo passo e ho messo in piedi l’applicazione.
    Ha forse infine realizzato l’altra webapp per la manipolazione dei dati?
    Saluti!
    CDP

Comments are closed.