Advanced   Java   Services Web Services 1 Back Next Up Home


Web Services

Web Services stellen die dritte Stufe der Kommunikation zwischen einem Server und einem Client dar. Die erste Stufe stellen Servlets und Java Server Pages dar. Hier werden im wesentlichen (HTML-)Texte und Bilder gesendet. Auf Serverseite braucht man dazu HttpServer, die über einen ServletContainer verfügen wie Apacher Tomcat oder Jetty. Enterprise Java Beans (EJB) stellen die zweite Stufe dar. Dazu braucht man einen AppliaktionServer wie etwa JBoss oder Glassfish. Diese verfügen neben dem Servletcontainer zusätzlich über einen Beancontainer. Ein Beancontainer kann EJB's veröffentlichen (deployen). Ein Javaclient kann sich dann über einen Namensdienst Javaobjekte schicken lassen. Der Client kennt zwar nicht den genauen Typ des Objekts, jedoch hat er die Information, daß die zugehörige Klasse ein Interface implementiert hat, das ihm bekannt ist. Dadurch ist der Client in der Lage die im Interface vereinbarten Methoden aufzurufen. Bei EJB's ist man allerdings an die Sprache gebunden. EJB und Client sind in Java geschrieben.

Mit Web Services gelingt es, sich von dieser Einschränkung zu befreien. Ein Web Service bietet unter einer bestimmten Internetadresse einen Dienst an. Der Dienst besteht (u.a.) aus Objekten einer Klasse, die ein Client von dieser Adresse anfordern kann. Der Client kennt zwar nicht den genauen Typ des Objekts, jedoch hat er die Information, daß die zugehörige Klasse ein Interface implementiert hat, das ihm bekannt ist. Dadurch ist der Client in der Lage die im Interface vereinbarten Methoden aufzurufen. Dies ist im wesentlichen noch das Konzept der von EJB und jeder Application Server (etwa JBoss oder GlassFish) bietet die Möglichkeit einen Web Service aufzusetzen. Der Unterschied zu EJB ist jedoch, daß der Client nicht in Java geschrieben sein muß. Der Web Service bietet die Information über die Methoden und die Schnittstellen in einem sprachenneutralen xml-Format an. Auf diese Weise kann daher ein Service in Java geschrieben sein und man kann mit C# darauf zugreifen oder auch umgekehrt. Ein Web Service ist aber keineswegs nur an diese Sprachen gebunden. Mit allen Sprachen, mit denen man Netzverbindungen programmieren kann, können Web Services eingerichtet werden.

Wir stellen zunächst noch einige Begriffe vor.


SOAP (Simple Object Access Protocol)

Diese Technik wurde 1998 von Microsoftprogrammierern entwickelt und in überarbeiteter Form vom W3-Consortium 2003 standardisiert. Das oben angeführte ursprüngliche Akronym wird heute eigentlich nicht mehr verwendet, da mit diesem Protokoll Daten nicht nur in Form von Objekten übertragen werden können. Das SOAP-Protokoll betseht im wesentlichen darin, die zu übertragende Information in Form einer XML-Datei zwischenzuspeichernn und diese mit Hilfe eines StandardProtokolls (HTTP etc.) zu übertragen. Im Falle der Übertragung eines Objektes kann der Empfänger das Objekt aus der XML-Datei erzeugen und dann Methoden dazu aufrufen. Dies geschieht über RPC.

SOAP (deutsche Wikipedia)


RPC (Remote Procedure Call)

Mit Enterprise Java Beans und Web Services können Objekte verschickt werden. Diese remote Objekte erscheinen im Programm wie lokale Objekte. Werden dann Methoden aufgerufen so müssen diese im Hintergund auf dem Server ausgeführt werden. Dies geschieht über RPC. Über RPC wird damit ein Client-Server-Modell implementiert.


JAX-WS (Java API for XML Web Services)

JAX-WS ist die Java-Implementierung von Web Services die Daten im XML-Format anbieten. Mit JAX-WS können sowohl RPC-orientierte Web Services als auch Message-orientierte Web Services realisiert werden.


WSDL (Web Service Definition/Description Language)

WSDL ist die plattform-, programmiersprachen- und protokollunabhängige Beschreibungssprache mit der ein Web Service beschreibt welche Dienste er anbietet. Sie wird in Form einer XML-Datei realisiert. Die WSDL ist sozusagen das Herzstück eines jeden Web Services. Wir werden weiter unten eine WSDL zeigen.

siehe Java EE 5 Tutorial

Die Bestandteile eines Web Services

Ein Web Service besteht im wesentlichen aus drei Teilen.

Endpoint publisher kann ein Servlet sein, das von einem Application Server deployed wird oder im einfachsten Fall die Klasse Endpoint, die die Java API im Package javax.xml.ws zur Verfügung stellt.


Dynamic Web Project in Eclipse

Alle Beispiele in diesem Kapitel werden über ein Dynamic Web Project in Eclipse realisiert. Ein Vorteil u.a. bei dieser Art von Projekten ist die Möglichkeit sich über File -> Export eine WAR-Datei generieren zu lassen. Für die Beispiele, die mit der Klasse Endpoint arbeiten brauchen wir keine WAR-Datei. Bei den Beispielen mit JBoss lassen wir uns eine WAR-Datei generieren, werden diese aber selbst in JBoss deployen.

Des weiteren stellt Eclipse für ein Dynamic Web Project einen Web Service Wizard zur Verfügung. Dieser Wizard wird für die Projekte verwendet, die Web Services mit GlassFish vorstellen. Auch in diesen Fällen werden wir uns eine WAR-Datei generieren lassen und diese wiederum selbst in GlassFish deployen.


Ein einfacher Web Service mit der Klasse Endpoint

Das Beispiel ist eine Erweiterung des Beispiels von Martin Kalin in "Java Web Services Up and Running" (O'Reilly, 2009). Das Service Endpoint Interface vereinbart zwei Methoden, die das aktuelle Datum auf dem Server liefern, einmal als Anzahl der Millisekunden seit 1970, getUnixTime(), die sog. Unixzeit und einmal als String mit der Methode getServerTime(String loc). Letzterer Methode kann ein String übergeben werden mit dem man eine Ländereinstellung auswählen kann. Es werden die deutsche, die französische und die englische Schreibweise unterstützt, default ist englische Schreibweise.

Die Annotation läßt die für style die Werte Style.RPC oder Style.DOCUMENT zu. Falls man primitive Datentypen oder String verwendet reicht Style.RPC. Für eigene Datentypen oder komplexe datentypen aus der API bracht man Style.DOCUMENT. In diesem Fall muß man mit dem Konsoltool wsgen die sogenannten Artefakte generieren, also zusätzliche Klassen, die die eigenen Datentypen in der WSDL (siehe unten) beschreiben helfen.


Das Service Endpoint Interface (SEI)
package timeservice;

import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

@WebService
@SOAPBinding(style = Style.RPC)  // heißt es gibt keine selbstentworfenen oder komplexen Datentypen
public interface DayTimeService
{
   @WebMethod
   public String getServerTime(String loc);
   @WebMethod
   public long getUnixTime();

}

Die Service Implementation Bean (SIB)
package timeservice;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import javax.jws.WebService;

@WebService(endpointInterface = "timeservice.DayTimeService", serviceName = "TheDayTimeService")
public class DayTimeServiceImpl implements DayTimeService
{
   private static final long serialVersionUID = 1L;

   public DayTimeServiceImpl()
   {
   }

   @Override
   public String getServerTime(String loc)
   {
      Locale locale;
      switch(loc)
      {
         case "ge": locale = Locale.GERMAN;
            break;
         case "fr": locale = Locale.FRANCE;
            break;
         default:   locale = Locale.US;
      }
      DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM, locale);
      return df.format(new Date());
   }

   @Override
   public long getUnixTime()
   {
      return new Date().getTime();
   }
}

Das Beispiel ist eine Abwandlung des Beispiels von Martin Kalin in "Java Web Services Up and Running" (O'Reilly, 2009)


Den Web Service aufsetzen

Um den Service anzubieten verwenden wir hier die Klasse Endpoint.

package timeservice;

import java.util.Scanner;
import javax.xml.ws.Endpoint;

public class DayTimeServiceEndpoint
{
   public static void main(String[] args)
   {
      String serviceUrl = "http://127.0.0.1:5001/timeservice";  // Packagename muß angegeben werden
      DayTimeService ts = new DayTimeServiceImpl();

      Endpoint ep=null;
      try
      {
         ep = Endpoint.publish(serviceUrl, ts);
         //com.sun.xml.internal.ws.transport.http.server.EndpointImpl
         System.out.println("DayTimeService started");
         System.out.println("listening on " + serviceUrl);

         System.out.print("Stop server j/n ");
         Scanner sc = new Scanner(System.in);
         String erg = sc.next();
         if (erg.toLowerCase().equals("j"))
         {
            ep.stop();
            System.out.println("server stopped");
         }
      }
      catch(Exception ex)  // BindException
      {
         System.out.println(ex);
      }
   }
}

Natürlich muß der Server zuerst gestartet werden.

Falls der Server bereits gestartet ist und versucht wird, ihn erneut zu starten erhält man die folgende Fehlermeldung: com.sun.xml.internal.ws.server.ServerRtException: Server Runtime Error: java.net.BindException: Address already in use: bind

Der gestartet Server meldet sich in der Eclipse Konsole.

webservice-01.ipg

Der gestartet Server meldet sich inm Browser.

webservice-02.ipg


Der WebClient

Hinweis:
Der in der Annotation der Implementierungsklasse (SIB) angegebene serviceName
@WebService(endpointInterface = "timeservice.DayTimeService", serviceName = "TheDayTimeService")
wird im Client im Konstruktor von QName als localPart eingetragen
QName qname = new QName("http://timeservice/", "TheDayTimeService"); // slash am Ende notwendig

import java.io.IOException;
import java.net.URL;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceException;
import javax.xml.namespace.QName;
import java.net.MalformedURLException;

import timeservice.DayTimeService;

public class DayTimeServiceClient
{
   public static void main(String[] args) throws IOException
   {
      System.out.println("DayTimeServiceClient");

      try
      {
         URL serviceURL = new URL("http://localhost:5001/timeservice?wsdl");
         QName qname = new QName("http://timeservice/", "TheDayTimeService");  // slash am ende notwendig
         Service service = Service.create(serviceURL, qname);

         DayTimeService ts = service.getPort(DayTimeService.class);
         System.out.println(ts.getServerTime("ge"));
         System.out.println("Sekunden seit 1970: " + ts.getUnixTime());
      }
      catch(MalformedURLException ex)
      {
         System.out.println(ex);
      }
      catch(WebServiceException ex)
      {
         System.out.println(ex);
      }
   }
}

Falls der Server nicht bereit ist, gibt es die folgende Fehlermeldung javax.xml.ws.WebServiceException: Failed to access the WSDL at: http://localhost:5001/timeservice?wsdl. It failed with: Got Connection refused: connect while opening stream from http://localhost:5001/timeservice?wsdl.

Der gestartet Client meldet sich in der Eclipse Konsole.

webservice-03.ipg


WSDL-datei

Die WSDL ist die xml-Datei, die der Web Service anbietet und auf die die Clients zugreifen. Mit Hilfe dieser Beschreibung gelingt es der Klasse javax.xml.ws.Service ein Objekt vom Typ DayTimeservice zu erstellen.

<?xml version="1.0" encoding="UTF-8"?>
<!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. -->
<!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. -->

<definitions
      xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
      xmlns:wsp="http://www.w3.org/ns/ws-policy"
      xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy"
      xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
      xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
      xmlns:tns="http://timeservice/"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns="http://schemas.xmlsoap.org/wsdl/"
      targetNamespace="http://timeservice/"
      name="DayTimeService">

<types></types>

<message name="getUnixTime"></message>
<message name="getUnixTimeResponse">
<part name="return" type="xsd:long"></part>
</message>
<message name="getServerTime">
<part name="arg0" type="xsd:string"></part>
</message>
<message name="getServerTimeResponse">
<part name="return" type="xsd:string"></part>
</message>

<portType name="DayTimeService">
<operation name="getUnixTime">
<input wsam:Action="http://timeservice/DayTimeService/getUnixTimeRequest" message="tns:getUnixTime"></input>
<output wsam:Action="http://timeservice/DayTimeService/getUnixTimeResponse" message="tns:getUnixTimeResponse"></output>
</operation>
<operation name="getServerTime">
<input wsam:Action="http://timeservice/DayTimeService/getServerTimeRequest" message="tns:getServerTime"></input>
<output wsam:Action="http://timeservice/DayTimeService/getServerTimeResponse" message="tns:getServerTimeResponse"></output>
</operation>
</portType>

<binding name="DayTimeServiceImplPortBinding" type="tns:DayTimeService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"></soap:binding>
<operation name="getUnixTime">
<soap:operation soapAction=""></soap:operation>
<input>
<soap:body use="literal" namespace="http://timeservice/"></soap:body>
</input>
<output>
<soap:body use="literal" namespace="http://timeservice/"></soap:body>
</output>
</operation>
<operation name="getServerTime">
<soap:operation soapAction=""></soap:operation>
<input>
<soap:body use="literal" namespace="http://timeservice/"></soap:body>
</input>
<output>
<soap:body use="literal" namespace="http://timeservice/"></soap:body>
</output>
</operation>
</binding>

<service name="DayTimeService">
<port name="DayTimeServiceImplPort" binding="tns:DayTimeServiceImplPortBinding">
<soap:address location="http://localhost:5001/timeservice"></soap:address>
</port>
</service>

</definitions>
Valid XHTML 1.0 Strict top Back Next Up Home