MokaByte 77 - 7mbre 2003 
Web Services e J2EE
I parte
di
Massimiliano
Bigatti
Sgombriamo il campo da un equivoco: i servizi Web non sono una modalità di sviluppo di componenti distribuiti (come gli EJB), ma solo una tecnologia di comunicazione, che consente a client eterogenei di accedere a servizi eterogenei. Dalla versione 1.4 J2EE integra le tecnologie SOAP, WSDL ed UDDI.

Introduzione
Questa estate, dopo un certo periodo di incubazione, è stata rilasciata la versione 1.4 beta 2 di J2EE. Diverse le novità, da JSP 2.0 ad EJB 2.1 - con il Timer Service, da J2EE Connector architecture che diventa bidirezionale a migliorie nelle servlet. Ma la grossa novità è l'integrazione della piattaforma J2EE con i Web Services, grazie al JSR 109, che consente di definire i requisiti di deploy per EJB che vogliono essere esposti come Web Service. Perchè si è parlato di "incubazione"? Il motivo è semplice: SUN ha atteso che venisse finalizzato il lavoro di WS-I (Web Service Interoperability Organization) sul Basic Profile 1.0, una specifica che vuole aumentare il grado di interoperabilità dei servizi Web. Da quello che aziende del calibro di Microsoft ed IBM hanno "venduto" agli sviluppatori verrebbe voglia di dare la questione del corretto funzionamento tra diverse implementazioni degli standard come assodata, ma la realtà come al solito è diversa. Due organizzazioni, la prima formale - WS-I, la seconda informale - SOAP Builders si occupano infatti di verificare che effettivamente le implementazioni dei vari produttori funzionino bene tra di loro. Da qui la scelta di SUN - attendere che le specifiche fossero finalizzate prima di rilasciare la versione definitiva; sicuramente una scelta rara nel mondo del software che sembra sempre rincorrersi a ritmi sempre più vorticosi. Ma d'altra parte non stiamo parlando di un software 1.0, ma di una robusta piattaforma, leader per le applicazioni commerciali d'impresa.

 

JAX-RPC e JAXR
La parte da leone per il supporto ai servizi Web in J2EE la fanno le due API JAX-RPC e JAXR, già note agli early-adopters dei servizi Web nella piattaforma Java o che comunque hanno deciso di implementare Web Services al di fuori del contesto J2EE ma in sintonia con gli standard della piattaforma. Come si ricorderà [1], JAX-RPC consente di supportare le tecnologie SOAP e WSDL, definendo una mappatura a due vie tra il mondo XML e Java. Con JAX-RPC è possibile generare gli stub e gli skeleton, in modo molto similare ad RMI, necessari sia alla fruizione di un servizio che alla sua implementazione. Inoltre JAX-RPC implementa una interfaccia dinamica (Dynamic Invocation Interface) che consente di invocare un servizio che non sia noto a priori - e che quindi non possiede stub generati in fase di sviluppo.
JAXR [2] supporta invece la tecnologia UDDI, ma le API sono decisamente più generiche: oltre ad implementare l'accesso a questo repository di servizi Web, l'interfaccia di SUN consente anche di accedere ai registri ebXML. Con JAXR è possibile accedere ai registri globali per eseguire ricerche incrociate, per classificazione e per introdurre ed eliminare servizi ed informazioni dai registri.
Il nuovo acronimo coniato da SUN per lo stack di tecnlogie implementate da JAX-RPC e JAXR è WUST, che stà per WSDL, UDDI, SOAP (si, mi chiedo anch'io per cosa sta la 'T').
Ad ogni modo, la buona notizia è dunque quella che gli sviluppatori non devono apprendere un nuovo insieme di API e strumenti, ma possono utilizzare quelli a loro noti (ed affrontati nei numeri precedenti di questa rubrica).

 

Arriva J2EE
La novità più importante nella versione 1.4 di J2EE è dunque l'integrazione di queste API tramite la JSR 109 ("Web Services for J2EE"); ad RMI e CORBA si aggiunge quindi un nuovo modo per implementare la programmazione distribuita, ed in particolare realizzando una via preferenziale per l'integrazione con .NET. In questo modo, i server J2EE possono essere acceduti anche da client non J2EE.
L'integrazione di JAX-RPC e JAXR avviene in J2EE in due momenti differenti. La prima consente che un componente J2EE, sia esso un EJB oppure una servlet, possa comunicare in SOAP e descriversi in WSDL; la seconda permette l'accesso ai registri di servizi Web, in modo da poter ricercare nei registri mondiali i servizi di proprio interesse, oltre che a pubblicare e rimuovere i propri.
Inoltre, la specifica JSR 151, che definisce i requisiti per J2EE 1.4, include anche requisiti per l'interoperabilità, presi appunto da WS-I. Questa specifica, tra l'altro, proprio all'interno del Basic Profile 1.0, richiede come obbligatorio il supporto ad UDDI 2.0; J2EE, in accordo a questa, implementa il supporto a JAXR sia sul lato server, in EJB e Servlet, che sul lato client.


Figura 1
- Una visione d'insieme


In figura 1 è mostrata una visione d'insieme delle tecnologie J2EE, unite alle nuove tecnologie per i servizi Web. Dalla destra si può notare il livello dei dati, composto da basi dati, applicazioni e sistemi legacy; poi si trova il livello EJB, il cuore della logica applicativa che tramite RMI/JMS dialoga con il livello Web che oltre alla gestione della consueta presentazione, si occupa da oggi anche della comunicazione con l'esterno tramite SOAP.

 

Percorso di implementazione
La documentazione di SUN suggerisce un workflow operativo per l'implementazione dei servizi Web in J2EE composto da sei passaggi.
Questi sono:

  • definizione del servizio Web;
  • implementazione del servizio;
  • produzione del pacchetto di installazione;
  • installazione del pacchetto in un server J2EE;
  • pubblicazione opzionale del servizio e delle informazioni di collegamento in un registro di Web Services;
  • risposta delle richieste da parte dei client.

I primi passaggi ricordano e ripercorrono sostanzialmente le operazioni necessarie per realizzare servizi con JAX-RPC, anche se J2EE introduce alcuni concetti nuovi. La definizione del servizio, come in JAX-RPC, passa dalla creazione di un WSDL del servizio, sia direttamente, che generandolo a partire da una interfaccia Java. Anche nel caso si parta da un WSDL esistente, sarà poi necessario generare questa interfaccia. In entrambi i casi, dunque, si avrà una interfaccia Java del servizio ed il relativo file WSDL, che andrà a far parte del pacchetto di installazione. Un esempio di interfaccia è presente nel listato 1.

Listato 1 - HelloIF.java
package helloservice;

import java.rmi.RemoteException;
import java.rmi.Remote;

public interface HelloIF extends Remote {
  public String sayHello(String name) throws RemoteException;
}


Come si nota, l'interfaccia estende java.rmi.Remote. Questo è un requisito delle specifiche, insieme al fatto che i metodi sollevino una java.rmi.RemoteException, oltre ad eventuali eccezioni applicative.

 

Implementazione del servizio
Arrivati alla fase di implementazione, come accennato, è necessario decidere con quale tipologia di componente J2EE si intende implementare il servizio. La piattaforma consente infatti di utilizzare una normale classe Java, che verrà poi esposta tramite una Servlet - e dunque ospitata all'interno del Web Container, oppure di utilizzare un normale EJB di tipo stateless session - contenuta all'interno dell'EJB Container. In entrambi i casi sussisono però dei limiti dell'uso che si può fare del linguaggio, insieme a quelli già imposti nel caso di EJB. Nello specifico:

  • EJB. Il componente deve essere un EJB session stateless che implementa tutti i metodi definiti nell'interfaccia, anche se pare non sia indispensabile implementare l'interfaccia con implements). Per questi metodi non è possibile specificare l'attributo di transazione Mandatory; non è consentito salvare alcun dato persistente da una chiamata all'altra;
  • classe Java. La classe deve essere pubblica non statica e non final. Deve dichiarare un costruttore pubblico senza argomenti per la sua creazione, mentre gli altri metodi non possono essere final. Come nel caso di EJB, devono essere implementati tutti i metodi dell'interfaccia del servizio e non devono essere salvati dati persistenti da una chiamata all'altra. Opzionalmente è possibile implementare l'interfaccia javax.xml.rpc.server.ServiceLifeCycle.


Un esempio di EJB che implementa l'interfaccia vista nel listato 1 è presente nel listato 2.

Listato 1 - HelloServiceBean.java
package helloservice;

import java.rmi.RemoteException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;

public class HelloServiceBean implements SessionBean {
  public String sayHello(String name) {
    return "Hello " + name + "from HelloServiceEJB";
  }
  public HelloServiceBean() {}
  public void ejbCreate() {}
  public void ejbRemove() {}
  public void ejbActivate() {}
  public void ejbPassivate() {}
  public void setSessionContext(SessionContext sc) {}
}

Come si nota, sono presenti tutti i metodi richiesti dalla specifica EJB, anche se in realtà questi sono vuoti.



Figura 2
- Modello di programmazione server


Ciascun metodo del componente Java od EJB ha lo scopo di implementare quella che nella terminologia dei Web Services è definita port. L'interfaccia esposta da una porta è denominata SEI (Service Endpoint Interface), ottenuta mappando gli elementi portType di un documento WSDL nel la sua rappresentazione Java, in accordo alle specifiche JAX-RPC. Ciascun SEI è dotato di un SIB (Service Implementation Bean) che implementa appunto i metodi definiti dall'interfaccia. I nuovi concetti introdotti (SEI e SIB) hanno lo scopo di disaccoppiare il componente dal modo in cui esiste nel mondo Java, rispetto a come viene percepito dai client di servizi Web nel mondo XML (Figura 2).

 

Produzione del pacchetto
Per costruire l'EAR che conterrà il servizio Web è necessario compilare alcuni file aggiuntivi, definiti dalle nuove specifiche per eseguire il collegamento, oltre che introdurre alcuni elementi aggiuntivi nei descrittori già esistenti. Nel JAR del componente, deve essere presente, nella directory META-INF, il nuovo file webservices.xml (Listato 3) che contiene la corrispondenza tra l'interfaccia ed il componente che la implementa, oltre ad altre informazioni di supporto come il display-name del componente, il riferimento al WSDL del servizio o il file di mapping per JAX-RPC (Listato 4). In particolare, le componenti principali del file webservices.xml sono:

  • component-name. Il nome della porta che non è indispensabile sia equivalente a quella definita nel WSDL;
  • service-impl-bean. Indica la classe che implementa i metodi del Web Service - è la classe del componente SIB;
  • service-endpoint-interface. Il nome completo dell'interfaccia (SEI);
  • wsdl-port. Il QName del WSDL che indica il port del WSDL;
  • jaxrpc-mapping-file. Puntamento al file che indica la mappatura tra il file WSDL alle interfacce JAX-RPC.


Questi file sono prodotti in automatico dagli strumenti di deploy del server utilizzato utilizzando le informazioni di configurazione fornite dallo sviluppatore.

Listato 3 - webservices.xml
<?xml version="1.0" encoding="UTF-8"?>
<webservices version="1.1" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/j2ee_web_services_1_1.xsd">
<webservice-description>
<display-name>MyHelloService</display-name>
<webservice-description-name>MyHelloService</webservice-description-name>
<wsdl-file>META-INF/wsdl/MyHelloService.wsdl</wsdl-file>
<jaxrpc-mapping-file>mapping.xml</jaxrpc-mapping-file>
<port-component>
<display-name>HelloIF</display-name>
<port-component-name>HelloIF</port-component-name>
<wsdl-port xmlns:wsdl-port_ns__="urn:Foo">wsdl-port_ns__:HelloIFPort</wsdl-port>
<service-endpoint-interface>
helloservice.HelloIF
</service-endpoint-interface>
<service-impl-bean>
<ejb-link>HelloServiceEJB</ejb-link>
</service-impl-bean>
</port-component>
</webservice-description>
</webservices>


Listato 4 - mapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<java-wsdl-mapping xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://www.ibm.com/webservices/xsd/j2ee_jaxrpc_mapping_1_1.xsd"
version="1.1">
<package-mapping>
<package-type>helloservice</package-type>
<namespaceURI>urn:Foo</namespaceURI>
</package-mapping>
<service-interface-mapping>
<service-interface>helloservice.MyHelloService</service-interface>
<wsdl-service-name xmlns:serviceNS="urn:Foo">serviceNS:MyHelloService</wsdl-service-name>
<port-mapping>
<port-name>HelloIFPort</port-name>
<java-port-name>HelloIFPort</java-port-name>
</port-mapping>
</service-interface-mapping>
<service-endpoint-interface-mapping>
<service-endpoint-interface>helloservice.HelloIF</service-endpoint-interface>
<wsdl-port-type xmlns:portTypeNS="urn:Foo">portTypeNS:HelloIF</wsdl-port-type>
<wsdl-binding xmlns:bindingNS="urn:Foo">bindingNS:HelloIFBinding</wsdl-binding>
<service-endpoint-method-mapping>
<java-method-name>sayHello</java-method-name>
<wsdl-operation>sayHello</wsdl-operation>
<method-param-parts-mapping>
<param-position>0</param-position>
<param-type>java.lang.String</param-type>
<wsdl-message-mapping>
<wsdl-message xmlns:wsdlMsgNS="urn:Foo">wsdlMsgNS:HelloIF_sayHello</wsdl-message>
<wsdl-message-part-name>String_1</wsdl-message-part-name>
<parameter-mode>IN</parameter-mode>
</wsdl-message-mapping>
</method-param-parts-mapping>
<wsdl-return-value-mapping>
<method-return-value>java.lang.String</method-return-value>
<wsdl-message xmlns:wsdlMsgNS="urn:Foo">wsdlMsgNS:HelloIF_sayHelloResponse</wsdl-message>
<wsdl-message-part-name>result</wsdl-message-part-name>
</wsdl-return-value-mapping>
</service-endpoint-method-mapping>
</service-endpoint-interface-mapping>
</java-wsdl-mapping>

 

Conclusioni
Nel prossimo numero procederemo all'installazione ed alla pubblicazione del servizio, completando il ciclo di sviluppo della componente server, che poi potrà essere utilizzata dai client. Come si vedrà è necessario utilizzare un piccolo trucco per poter utilizzare l'SDK di SUN - configurare Windows con impostazioni internazionali statunitensi - pena l'impossibilità di creare l'applicazione ed eseguirne il deploy.

 

Nota: dove è finito JAXM?
JAXM, la API per la messaggistica SOAP, non è parte di J2EE 1.4, ma è supportata all'interno della specifica EJB 2.1, nei message driven bean (MDB). Nell'ultima versione, infatti, questi componenti possono ricevere messaggi JAXM oltre che JMS. Inoltre, Connector 1.5 supporta l'utilizzo di diversi tipi di fornitori di messaggi, JMS provider e JAXM provider).

 

Bibliografia e riferimenti
[1] M. Bigatti -"Corso di Java Web Services, V parte: JAX-RPC", Mokabyte, giugno 2003, http://www.mokabyte.it/2003/06/jws-5.htm
[2] M. Bigatti -"Corso di Java Web Services, V Iparte: JAXR", Mokabyte, luglio 2003, http://www.mokabyte.it/2003/07/jws-6.htm
[3] Vari - "J2EE 1.4 Tutorial" - http://java.sun.com/j2ee/1.4/docs/tutorial/doc/

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it