Samstag, 28. August 2010

SOAP in fifteen minutes

JAX-WS und der I-Stack unterstützen optimal die Programmierung von SOAP-basierten Java Web Services. Die Entwicklung eines Web Services mit Hilfe von Annotationen ist spielend leicht. Ist der Web Service programmiert kann auf Basis der WSDL mit "wsimport" der clientseitige Quellcode zum Ansprechen des Web Services erzeugt werden.

Die I-Stack Technologien sind integraler Bestandteil von Java SE 6, sodass JAXP, JAXB 2.0 und JAX-WS inklusive der Werkzeuge zum Generieren von Quellcode und Schemata (wsimport und wsgen) auch ohne Applikationsserver in Java SE 6 Umgebungen anwendbar sind.

Wie der Titel des Blogeintrages bereits verspricht, ist ein einfacher SOAP-basierter Web Service als Full Stack Java EE Anwendung in fünfzehn Minuten programmiert. Als Basis für die Programmierung dient das JBoss Developer Studio 3.0 GA und der JBoss AS 6 (Milestone 3). 

Der SOAP-basierte Web Service Endpoint, der nachfolgend programmiert wird, beinhaltet eine Operation zum Anlegen und Lesen eines Blogeintrages.

Zunächst ist ein Java EE Projekt anzulegen (EAR inklusive eines EJB-JAR) mit dem Namen "SoapBlogProject". Im Verzeichnis /ejbModule/META-INF ist eine persistence.xml zu hinterlegen, die auf eine konfigurierte Datenquelle verweist. Danach sind, wie nachfolgend beschrieben, schrittweise die folgenden Packages und Java Klassen anzulegen.

Anlegen der Klasse BlogEntry im Package ccd.jee.ws.soap.domain

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class BlogEntry {

    @Id
    @GeneratedValue
    private int id;
   
    private String title;
    private String entry;
   
    public int getId() {
        return id;
    }
   
    public String getTitle() {
        return title;
    }
   
    public String getEntry() {
        return entry;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setEntry(String entry) {
        this.entry = entry;
    }

    @Override
    public String toString() {
        return "BlogEntry [entry=" + entry + ", title=" + title + "]";
    }
}

Anlegen der lokalen Schnittstelle BlogServiceDao im Package ccd.jee.ws.soap.domain.dao

import javax.ejb.Local;
import ccd.jee.ws.soap.domain.BlogEntry;

@Local
public interface BlogServiceDao {

    public void store(String title, String entry);
    public BlogEntry getById(Integer id);
}

Anlegen des DAOs BlogServiceDaoBean im Package ccd.jee.ws.soap.domain.dao

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import ccd.jee.ws.soap.domain.BlogEntry;

@Stateless
public class BlogServiceDaoBean implements BlogServiceDao {

   @PersistenceContext
   private EntityManager em;

   public void store(String title, String entry) {

       BlogEntry blogEntry = new BlogEntry();
       blogEntry.setTitle(title);
       blogEntry.setEntry(entry);

       em.persist(blogEntry);
    }

    public BlogEntry getById(Integer id) {

       return em.find(BlogEntry.class, id);
    }
}

Anlegen der Klasse BlogEntryService im Package ccd.jee.ws.soap.service

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import ccd.jee.ws.soap.domain.BlogEntry;
import ccd.jee.ws.soap.domain.dao.BlogServiceDao;

@WebService(name = "BlogEntry",
            serviceName = "BlogEntryService",
            targetNamespace= "http://service.soap.ws.jee.ccd")           
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT,
             use = SOAPBinding.Use.LITERAL,
             parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
@Stateless           
public class BlogEntryService {

    @EJB private BlogServiceDao blogServiceDao;
  
    @WebMethod(operationName = "getBlogEntry")
    @WebResult(name = "BlogEntryResponse",
            targetNamespace = "service.soap.ws.jee.ccd")             
    public BlogEntry getBlogEntry(
            @WebParam(name = "id",
                        targetNamespace= "http://service.soap.ws.jee.ccd",
                        mode = WebParam.Mode.IN) Integer id) {

        return blogServiceDao.getById(id);
    }
      
    @WebMethod
    @Oneway
    public void storeBlogEntry(           
            @WebParam(name = "title",
                      targetNamespace= "http://service.soap.ws.jee.ccd",
                      mode = WebParam.Mode.IN) String title,
                    
            @WebParam(name = "entry",
                              targetNamespace= "http://service.soap.ws.jee.ccd",
                              mode = WebParam.Mode.IN) String entry) {
      
        blogServiceDao.store(title, entry);
    }
}

Das war es schon! Die Klasse BlogEntryService beinhaltet den SOAP-basierten Web Service, der den Document/Wrapped Parameterstyle verwendet. Dieser Style ist zwar nicht WS-I Basic Profile konform, hat aber den wesentlichen Vorteil, wie ein RPC basierter Web Service im SOAP-Body aufzutreten.

Die Operation des Web Service Endpoints zum Anlegen eines Blogeintrages ist als OneWay-Operation ausgelegt. Für OneWay-Operationen wird keine SOAP-Response erzeugt. Die Operation zum Lesen eines Blogeintrages liefert einen Blogeintrag mit den beiden Attributen Titel und Eintrag als SOAP-Response zurück.

Der Web Service kann nun auf dem JBoss AS 6 (Milestone 3) deployed werden. Ist das Deployment erfolgreich verlaufen, schaltet man einen Browser auf und lässt sich die Liste der deployten Web Services anzeigen:  

http://localhost:<PORT>/jbossws/services

Web Service Eintrag


Die WSDL des Web Services schaut man sich durch die folgende Eingabe in der Browser Adresszeile an:

http://localhost:<PORT>/SoapBlogProjectEJB/BlogEntry?wsdl

Der Eintrag <PORT> ist durch den lokal konfigurierten Port des JBoss AS zu ersetzen.

Zum Testen des Web Services verwendet man den Web Service Explorer des JBoss Developer Studios 3.0. In der Open WSDL Ansicht gibt man die obenstehende WSDL-Referenz ein.Nach Betätigung der Go-Schaltfläche wird der Web Service mit seinen beiden Operationen angezeigt. Zunächst wählt man die Operation "storeBlogEntry" aus und definiert zwei Parameter mit der jeweiligen Add-Schaltfläche. Nach der Parameterdefinition betätigt man wieder die Go-Schaltfläche. Der Web Service wird ausgeführt und der Blogeintrag gespeichert.

Web Services Explorer: storeBlogEntry

Den angelegten Blogeintrag kann man mit einem ID-Parameter abfragen. Dazu verwendet man wiederum den Web Services Explorer und die Operation "getBlogEntry". Die nachfolgende Abbildung zeigt den SOAP-Request und die dazugehörige SOAP-Response der "getBlogEntry" Operation.

Request-/Response-Message der Operation "getBlogEntry"

Der Web Service ist nun programmiert und rudimentär getestet. SOAP Web Services sind mit dem Java First Ansatz zügig umsetzbar. SOAP ist nach wie vor das geeignete Mittel um Interoperabilitätsprobleme zu lösen, weil mit dem WSDL First Ansatz, XML Schemata und der Standardisierung durch das WS-I Basic Profile eine stabile Umgebung für den Austausch von Daten in unterschiedliche Betriebssysteme und Programmierumgebungen vorhanden ist.


Der Rechtshinweis des Java Blog für Clean Code Developer
ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.