Web Service in Java

Di seguito un breve esempio e Tutorial, su come creare un Web Service in Java in pochi passi, utilizzando il framework JAX-WS [ Java API for XML Web Services ] ( https://jax-ws.java.net/ ), conosciuto per la sua semplicità di utilizzo.

Il risultato di ciò che si andrà a sviluppare, può essere visto visitando l'applicazione web :

http://services.occhioinformatico.it/javaWS/

Introduzione ai Web Service

Nell'era in cui le applicazioni Desktop sono diventate applicazioni Web e dove le applicazioni stand-alone sono diventate applicazioni distribuite, composte da diversi moduli software e distribuiti su più computer, una delle tecnologie informatiche più diffuse sono i Web Service, diventati il "collante" di comunicazione tra tutti i moduli software in cui sono divise le applicazioni. Essi sono applicazioni Web che offrono uno specifico servizio ( https://it.wikipedia.org/wiki/Web_service ) .

Qualsiasi tecnologia o linguaggio di programmazione che più ci aggrada, fornisce un framework per svilupparli e sfruttando il protocollo di comunicazione SOAP [ Simple Object Access Protocol ] permettono a chiunque di utilizzarli senza sapere come sono stati sviluppati o con quale tecnologia. Basandosi sul modello client-server, il Web Service è il server, fornisce un contratto di servizio al client attraverso lo standard WSDL [ Web Services Description Language ] , il quale potrà implementare l'invocazione del Web Service attraverso di esso.

Con la diffusione delle reti informatiche ed i protocolli di comunicazione HTTP [ HyperText Transfer Protocol ] e HTTPS [ HyperText Transfer Protocol over Secure Socket Layer ] i Web Service hanno una larga diffusione.

Alla domanda, ma cos'è un Web Service si potrebbe rispondere:

Pensando alla programmazione ad oggetti un Web Service è una classe di metodi invocabili da un client per ottenere dei risultati in uno specifico contesto definito dal servizio offerto”.

Come sviluppare un Web Service

Java ( https://www.java.com ) , è uno dei linguaggio più conosciuti nello sviluppo di applicazioni Web e multi piattaforma. Di seguito la spiegazione per creare un Web Service utilizzando il framework JAX-WS.

Si ipotizzi di voler creare un Web Service con un unico metodo che restituisca la localizzazione di un IP [ Internet Protocol ] in versione 4.

Per esempio dato l'IP 8.8.8.8

il metodo restituisce:

Nazione: United States

Regione: Colorado

Città: Broomfield

Latitudine: 39.9206

Longitudine: -105.086

Si pensi all'utilizzo del modello software architetturale MVC [ Model View Controller ].

Il parametro da passare e l'oggetto restituito è la parte "Model", l'IP può essere rappresentato da una stringa e la localizzazione restituita, da una classe composta da 5 campi rappresentanti le 5 informazioni definite come localizzazione sopra descritti.

La parte "Controller" sarà l'implementazione del calcolo fatto per poter trovare la localizzazione associata all'IP.

Si può quindi dire che il Web Service è il modo in cui si usa il controller e quindi è la parte "View" del modello.

I passi da fare per lo sviluppo saranno:

  1. Definire il modello dei dati da rappresentare

  2. Implementare il controller utilizzando il modello dei dati già sviluppato

  3. Definire e implementare il modo in cui utilizzare il controller, ad esempio un Web Service

Esempio d'implementazione di un Web Service

Il Modello

Considerando la localizzazione da rappresentare di seguito la classe in Java che si potrebbe utilizzare.

 

public class StatsIp2locationDb5 implements java.io.Serializable {

private Long ipFrom;

private Long ipTo;

private String countryCode;

private String countryName;

private String regionName;

private String cityName;

private Double latitude;

private Double longitude;

private Set statsRecordses = new HashSet(0);

public StatsIp2locationDb5() {

}

public StatsIp2locationDb5(Long ipFrom) {

this.ipFrom = ipFrom;

}

public StatsIp2locationDb5(Long ipFrom, Long ipTo, String countryCode,

String countryName, String regionName, String cityName,

Double latitude, Double longitude, Set statsRecordses) {

this.ipFrom = ipFrom;

this.ipTo = ipTo;

this.countryCode = countryCode;

this.countryName = countryName;

this.regionName = regionName;

this.cityName = cityName;

this.latitude = latitude;

this.longitude = longitude;

this.statsRecordses = statsRecordses;

}

public Long getIpFrom() {

return this.ipFrom;

}

public void setIpFrom(Long ipFrom) {

this.ipFrom = ipFrom;

}

public Long getIpTo() {

return this.ipTo;

}

public void setIpTo(Long ipTo) {

this.ipTo = ipTo;

}

public String getCountryCode() {

return this.countryCode;

}

public void setCountryCode(String countryCode) {

this.countryCode = countryCode;

}

public String getCountryName() {

return this.countryName;

}

public void setCountryName(String countryName) {

this.countryName = countryName;

}

public String getRegionName() {

return this.regionName;

}

public void setRegionName(String regionName) {

this.regionName = regionName;

}

public String getCityName() {

return this.cityName;

}

public void setCityName(String cityName) {

this.cityName = cityName;

}

public Double getLatitude() {

return this.latitude;

}

public void setLatitude(Double latitude) {

this.latitude = latitude;

}

public Double getLongitude() {

return this.longitude;

}

public void setLongitude(Double longitude) {

this.longitude = longitude;

}

public Set getStatsRecordses() {

return this.statsRecordses;

}

public void setStatsRecordses(Set statsRecordses) {

this.statsRecordses = statsRecordses;

}

public StatsIp2locationDb5 factoryClass(){

return new StatsIp2locationDb5();

}

}

 

Nell'esempio reale volutamente la classe ha più campi in quanto, più IP potrebbero essere localizzati nello stesso luogo avendo lo stesso fornitore.

La classe deriva anche dalla classe java.io.Serializable in modo da poter essere serializzata (per esempio in una stringa XML [ eXtensible Markup Language ] ).

L'approccio utilizzato per definire il modello, è stato di tipo bottom-up. Dalla struttura della tabella sul database contenente tutte le localizzazioni associate agli IP, attraverso il framework Hibernate ( http://hibernate.org/ ) e gli strumenti Hibernate Tools ( http://hibernate.org/tools/ ), facenti parte dei più generici JBoss Tools ( http://tools.jboss.org/ ) , si è fatto il reverse engineering e creato il codice Java, le classi per aprire la sessione di comunicazione verso il database e la classe sopra, in gergo la classe "bean". Per chi sviluppa in ambiente Microsoft, potrà usare Nhibernate ( http://nhforge.org/ ).

Il Controller

Si ipotizzi di avere a disposizione una libreria (o più librerie) che svolgono il lavoro di ricerca della localizzazione di un IP. Per poter normalizzare le varie implementazioni, una “best practice” è creare una interfaccia per definire l'accesso alle classi che svilupperanno la ricerca attraverso le librerie disponibili.

Di seguito un esempio dell'interfaccia.

 

public interface ILocalizerIP {

public StatsIp2locationDb5 getGeoFromIP4(String ip);

}

 

E una classe d'implementazione

 

public class LocalizerIP implements ILocalizerIP {

...

public StatsIp2locationDb5 getGeoFromIP4(String ip) {

...

}

...

}

 

Il Web Service

Un servizio software è una applicazione che rende disponibile un insieme di metodi che un'altra applicazione può utilizzare conoscendo l'interfaccia d'implementazione del servizio. L'interfaccia rappresenta quindi il "contratto di servizio" che viene standardizzato nei Web Service con una rappresentazione attraverso un XML chiamato WSDL e reso disponibile dal Web Service ai client che potranno invocarlo.

Nel Web, il servizio viene utilizzato come se fosse un sito, attraverso il protocollo SOAP il client invoca i vari metodi. L'indirizzo del sito da invocare è la classe che li contiene, normalmente viene definito End-Point del servizio.

La struttura di una Web Application in Java è ben definita, di seguito i passi da seguire per creare un Web Service con JAX-WS 2.0/2.1/2.2 (JSR 224) ( https://jax-ws-commons.java.net/ ) .

  1. Creare una nuova Web Application utilizzando per esempio il noto ambiente di sviluppo Eclipse ( https://www.eclipse.org/ ), creando un nuovo progetto Web

  2. Importare le librerie del framework JAX-WS, è possibile scaricarle dal sito oppure utilizzare il gestore di librerie maven ( http://search.maven.org/ ).

    I jar di base da utilizzare sono :

     

    axis.jar

    commons-discovery-0.2.jar

    jaxrpc.jar

    saaj.jar

    wsdl4j.jar

    commons-logging.jar

    gmbal-api-only.jar

    javax.xml.soap-api.jar

    javax.annotation-api.jar

    jaxb-api.jar

    jaxb-core.jar

    jaxb-impl.jar

    jaxws-api.jar

    jaxws-rt.jar

    management-api.jar

    policy.jar

    resolver.jar

    streambuffer.jar

    stax-ex.jar

    ha-api.jar

     

  3. Definire il contratto di servizio, di seguito i passi da seguire

    La standardizzazione SOAP prevede che lo scambio di messaggi tra client e server ( composto da stringhe XML ), dovranno appartenere ad un name-space per essere validati formalmente da quanto definito nel contratto. Si progetti quindi i nomi e name-space del proprio servizio.


    Esempio:

     

    Si ipotizzi il metodo: StatsIp2locationDb5 getGeoFromIP4(String ip)

    Definisco, Concetto : Valore

    NameSpace del servizio (potrebbe essere uguale al nome del sito o simile), "la root dell'XML" : http://services.mynickname.org/LocalizerIP

    Porta del servizio : LocalizerIPWSPort

    Nome del servizio : LocalizerIPWSService

    Nome dell'elemento XML rappresentante i parametri di ingresso del metodo : ipInput

    NameSpace dell'elemento XML rappresentante i parametri di ingresso dal metodo : http://services.mynickname.org/LocalizerIP/type

    Nome dell'elemento XML rappresentante i risultati restituiti dal metodo : StatsIp2locationDb5Result

    NameSpace dell'elemento XML rappresentante i risultati restituiti dal metodo : http://services.mynickname.org/LocalizerIP/type

    Nome del metodo, anche detta operazione o azione : getGeoFromIP4

     

  4. Il framework per creare il WSDL e per validare le chiamate SOAP, si basa sulla lettura dei meta dati definiti dallo sviluppatore attraverso le Java Annotations. Partendo dal modello, modificare il "Bean" per poter definire l'oggetto XML rappresentante il risultato restituito dal metodo invocato che sarà utilizzato nei messaggi scambiati.

     

    @XmlRootElement(name="StatsIp2locationDb5Result",namespace="http://services.mynickname.org/LocalizerIP/type")

    @XmlAccessorType(XmlAccessType.FIELD)

    @XmlType(name = "StatsIp2locationDb5"

    ,propOrder = {

    "countryName",

    "regionName",

    "cityName",

    "latitude",

    "longitude"

    }

    ,namespace="http://services.mynickname.org/LocalizerIP/type"

    )

    public class StatsIp2locationDb5 implements java.io.Serializable {

    @XmlTransient

    private Long ipFrom;

    @XmlTransient

    private Long ipTo;

    @XmlTransient

    private String countryCode;

    private String countryName;

    private String regionName;

    private String cityName;

    private Double latitude;

    private Double longitude;

    @XmlTransient

    private Set statsRecordses = new HashSet(0);

    ...

     

    Si noti:

    • @XmlAccessorType : il modo in cui vengono definiti i campi all'interno del risultato

    • @XmlTransient : per NON restituire alcune property nel risultato all'utente finale

    • i namespace impostati

    • i nomi delle property impostate con un certo ordine

  5. Successivamente implementare la classe che sarà invocata quando il Web Service sarà invocato da qualche client. Essa richiamerà la classe già definita LocalizerIP nel controller.

     

    package org.mynickname.ws.ip2location;

    ...

    @WebService(

    portName = "LocalizerIPWSPort",

    serviceName = "LocalizerIPWSService",

    targetNamespace="http://services.mynickname.org/LocalizerIP"

    )

    @SOAPBinding(style=SOAPBinding.Style.DOCUMENT,use=SOAPBinding.Use.LITERAL,parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)

    public class LocalizerIPWS implements ILocalizerIP {

    public final String portName = "LocalizerIPWSPort";

    public final String serviceName = "LocalizerIPWSService";

    public String folderHome = "";

    @WebMethod(exclude=true)

    public void Setup(String folderHomeSetting)

    {

    folderHome = folderHomeSetting;

    }

    @WebMethod(operationName="getGeoFromIP4")

    @WebResult(name="StatsIp2locationDb5Result",targetNamespace="http://services.mynickname.org/LocalizerIP/type")

    public StatsIp2locationDb5 getGeoFromIP4(

    @WebParam(name=" ",targetNamespace="http://services.mynickname.org/LocalizerIP/type")

    String ip) {

    LocalizerIP ipctr = new LocalizerIP();

    return ipctr.getGeoFromIP4(ip);

    }

    }

     

    Si noti :

    • @WebService : per definire la configurazione del Web Service

    • @WebMethod(exclude=true) : per escludere alcuni metodi dall'invocazione, ma necessari per il ciclo di vita della classe

    • @WebParam : per definire l'oggetto XML dei parametri richiesti

    • @WebResult : per definire l'oggetto XML del risultato restituito


    Il risultato fino ad ora degli sviluppi fatti e delle annotazioni impostate, sarà che il Web Service si aspetterà delle chiamate SOAP così formate :

     

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:loc="http://services.mynickname.org/LocalizerIP" xmlns:type="http://services.mynickname.org/LocalizerIP/type">

    <soapenv:Header/>

    <soapenv:Body>

    <loc:getGeoFromIP4>

    <type:ipInput>8.8.8.8</type:ipInput>

    </loc:getGeoFromIP4>

    </soapenv:Body>

    </soapenv:Envelope>

     

    e risponderà con delle risposte così composte :

     

    <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">

    <S:Body>

    <ns2:getGeoFromIP4Response xmlns:ns3="http://services.mynickname.org/LocalizerIP/type" xmlns:ns2="http://services.mynickname.org/LocalizerIP">

    <ns3:StatsIp2locationDb5Result>

    <countryName>United States</countryName>

    <regionName>Colorado</regionName>

    <cityName>Broomfield</cityName>

    <latitude>39.9206</latitude>

    <longitude>-105.086</longitude>

    </ns3:StatsIp2locationDb5Result>

    </ns2:getGeoFromIP4Response>

    </S:Body>

    </S:Envelope>

     

  6. Nel progetto importare le librerie che contengono le classi del modello e quelle del controller (modificate nei passi precedenti). La strada più semplice è metterle nel class path del progetto, oppure se sviluppate in progetti separati, farne una copia nella cartella /WEB-INF/lib, prima di creare il package WAR [ Web application ARchive ] per fare il deploy del sito

  7. Il Web Service fatto in JAX-WS di fatto è la creazione di una applicazione in ascolto di chiamate a cui rispondere, inglobata in un sito web. Aggiungere quindi nel sito la configurazione di essa attraverso il file descrittore /WEB-INF/sun-jaxws.xml

     

    <?xml version="1.0" encoding="UTF-8"?>

    <endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">

    <endpoint

    name="LocalizerIPWSService"

    implementation="org.mynickname.ws.ip2location.LocalizerIPWS"

    url-pattern="/LocalizerIPWSService" />

    </endpoints>

     

    Si noti :

    • il nome definito in passato

    • la classe implementata

    • la url a cui rispondere

  8. Effettuare l'installazione del sito creando prima il package WAR, ipotizzando di avere un application server in locale, si potrà invocare il servizio contattando la uri :

    http://localhost:8080/NomeApplicazione/LocalizerIPWSService

    il WSDL( da comunicare ai client ) sarà disponibile alla uri :

    http://localhost:8080/NomeApplicazione/LocalizerIPWSService?wsdl

     

Conclusioni

Capita l'importanza di questa tecnologia, il risultato di ciò che si è sviluppato, può essere visto visitando l'applicazione web :

http://services.occhioinformatico.it/javaWS/

Si troverà sia il link al WSDL per utilizzare il Web Service, sia una web form per effettuare ricerche delle localizzazioni di IP attraverso l'uso di una Java Servlet che utilizza il controller ed il modello sviluppato per il Web Service.

Tecnologia analoga per lo sviluppo di Web Service nel mondo Microsoft sono i WCF [ Windows Communication Foundation ] ( http://msdn.microsoft.com/en-us/library/ms731082%28v=vs.110%29.aspx ) , il livello di trasporto può essere diverso dell'HTTP, per esempio attraverso l'uso di un socket se più "servizi" comunicano sulla stessa macchina.

Molti strumenti di sviluppo creano il codice del client attraverso la lettura del WSDL in modo semi automatico, lasciando allo sviluppatore la rifinitura degli ultimi dettagli.