Nachdem die theoretischen Grundlagen zu einer SOA im Beitrag SERVICEORIENTIERTE
ARCHITEKTUR - THE BUZZWORD STRIKES AGAIN" geschaffen wurden,
soll es nun um eine konkrete Implementierung mit Apache
Axis2 und Ant
gehen.
Sonntag, 20. Juli 2008
SOA II: Automatisierte Erstellung eines Web Service mit Apache Axis2 und Ant
Voraussetzungen
- Servlet Container Apache Tomcat
- JDK >= 1.5
- Apache Ant
- Apache Axis2, Empfehlung: Standard Binary Distribution
Auf die Tasten, fertig, los
Die Vorraussetzungen sind erfüllt, der Tomcat läuft, ein ant
-version und java -version
bringen auf der Konsole aussagekräftige und vor allem positive
Meldungen hervor.
Vorbereitung des Tomcat
- Die Binary
Distribution wird beispielsweise nach
/axis2-1.4entpackt. - Wir wechseln nach
/axis2-1.4/webappund finden dort eine build.xml vor. In dem Ordner führen wir Ant aus und erhalten im Ordner/axis2-1.4/distdas Archivaxis2.war axis2.warkopieren wir nach$CATALINA_HOME/webapps/- Nach erfolgreichem Deployment erreicht man unter http://localhost:8080/axis2/ die Begrüßungsseite der Axis2 Webapp. Durch einen Klick auf den Link "Validate" kann man den Erfolg des Deployments verifizieren.
Der Tomcat ist vorbereitet um Webservices anzubieten und man kann
sich nun daran machen, den ersten eigenen Service zu erstellen.
Hierzu bietet Axis2 mehrere Möglichkeiten:
- Von Grund auf oder besser gesagt "from Scratch"
- Auf Basis eines POJO
- Auf Basis einer WSDL
Generierung des Service mit Ant
Es existiert allerdings noch eine vierte Vorgehensweise deren Prozedur
nicht offensichtlich auf der Homepage des Axis2 Projekt zu finden
ist. Der Webservice wird auf Grundlage eines (Java) Interface erstellt.
Das Interface repräsentiert den Service, die Methoden die möglichen
Operationen des Service. Der Code wird kompiliert, anhand des Codes die
entsprechende WSDL erstellt und letztendlich aus der WSDL der Service
erstellt. Diese Schritte lassen sich sehr gut mit einem Ant Skript
realisieren und ermöglichen so eine automatisierte Erstellung eines
Webservice.
Für den Webservice nehmen wir das Beispiel aus dem ersten
Teil der SOA Reihe: Eine Bäckerei bietet ihren Kunden einen
Service zur Abfrage des Bestellstatus. Es wird ein Interface BakeryService
erstellt mit der Methode whereIsMyOrder().
Die Methode hat als Rückgabewert ein Objekt vom Typ Order
(POJO).
Interface:
package bakery;
import bakery.bean.Order;
public interface BakeryService {
public Order whereIsMyOrder();
}
POJO Order:
package bakery.bean;
public class Order {
private long statusId;
private String statusText;
public long getStatusId() {
return statusId;
}
public void setStatusId(long statusId) {
this.statusId = statusId;
}
public String getStatusText() {
return statusText;
}
public void setStatusText(String
statusText) {
this.statusText = statusText;
}
}
Viel
geheimnisvolles gibt es hier nicht zu entdecken. Alle Kraft sollte in
das folgende Ant Skript gesteckt werden, das die Arbeit erledigen wird.
Ant Skript:
<?xml version="1.0" encoding="UTF-8"?>
<project name="axisServiceGen" default="all" basedir=".">
<property name="AXIS.HOME"
value="/axis2-1.4"/>
<property name="axis.wsdl"
value="BakeryService.wsdl"/>
<property name="src"
value="../src"/>
<property name="build"
value="../build"/>
<property name="build.wsdl"
value="${build}/wsdl"/>
<property name="build.java"
value="${build}/java"/>
<property name="build.java.src"
value="${build.java}/src"/>
<property
name="build.java.classes" value="${build.java}/classes"/>
<property name="ws.name"
value="bakery"/>
<property name="dist"
value="../dist"/>
<property name="lib"
value="../lib"/>
<target name="all"
depends="clean, gen.wsdl2java" />
<path id="axis.classpath">
<fileset dir="${AXIS.HOME}/lib" includes="**/*.jar"/>
</path>
<!-- init -->
<target name="init">
<mkdir dir="${build}" />
<mkdir dir="${build.wsdl}" />
<mkdir dir="${build.java}" />
<mkdir dir="${build.java.classes}" />
<mkdir dir="${dist}" />
</target>
<!-- Compile the sources
-->
<target name="gen.java2wsdl.pre"
depends="init">
<javac srcdir="${src}" destdir="${build.wsdl}" />
</target>
<!-- Generate the WSDL
file-->
<target name="gen.java2wsdl"
depends="gen.java2wsdl.pre">
<java classpathref="axis.classpath"
classname="org.apache.ws.java2wsdl.Java2WSDL" failonerror="true">
<arg value="-o" />
<arg value="${dist}" />
<arg value="-of" />
<arg value="${axis.wsdl}" />
<arg value="-l" />
<arg value="http://localhost:8080/axis2/services/BakeryService"
/>
<arg value="-cn" />
<arg value="bakery.BakeryService" />
<arg value="-cp" />
<arg value="${build.wsdl}" />
</java>
</target>
<!-- Generate server- and
clientside source code -->
<target name="gen.wsdl2java"
depends="gen.java2wsdl">
<java
classname="org.apache.axis2.wsdl.WSDL2Java" failonerror="true">
<classpath refid="axis.classpath"/>
<arg value="-uri"/>
<arg file="${dist}/${axis.wsdl}"/>
<arg value="-ss"/>
<arg value="-g"/>
<arg value="-sd"/>
<arg value="-o"/>
<arg file="${build.java}"/>
<arg value="-p"/>
<arg value="bakery"/>
</java>
</target>
<target name="gen.axis.classes"
depends="gen.wsdl2java">
<javac debug="on" destdir="${build.java.classes}"
srcdir="${build.java.src}">
<classpath refid="axis.classpath"/>
</javac>
</target>
<target name="clean">
<delete dir="${build}" />
<delete dir="${dist}" />
</target>
</project>
Was macht das Skript? Task init
legt die benötigten Ordnerstrukturen an, Verzeichnispfade sind in den
entsprechenden Properties angegeben. Im nächsten Schritt wird der Task gen.java2wsdl.pre
durchlaufen. Dieser Task kompiliert das Interface und das Objekt Order
in class-Dateien. Mit dem Task gen.java2wsdl
wird dann die WSDL unter Zuhilfenahme der Klasse org.apache.ws.java2wsdl.Java2WSDL
erstellt und sollte auf Grund seiner vielen
Parameter genauer erläutert werden.
Task gen.java2wsdl
| Argument | Bedeutung |
| -o | Output Verzeichnis der WSDL |
| -of | Dateiname der WSDL |
| -l | Endpunkt unter dem der Service erreichbar sein wird |
| -cn | Name der Klasse / Interface die den Service repräsentiert |
| -cp | Classpath. Hier unsere kompilierten Sourcen (Interface + POJO) |
Nachdem der Task durchlaufen wurde, sollte sich im Ordner "dist" die
WSDL BakeryService.wsdl befinden und
der nächste Task gen.wsdl2java kann
durchlaufen werden. Auch hier sollten die Parameter genauer erläutert
werden.
Task gen.wsdl2java
| Argument | Bedeutung |
| -uri | Ort der WSDL Datei |
| -ss | Generierung von serverseitigem Code |
| -g | Generierung
aller Klassen |
| -sd | Generierung
des Service Deskriptor services.xml |
| -o | Verzeichnis der generierten Java Klassen |
| -p | Package der Klassen (optional) |
Nach diesem Schritt, befindet sich in dem
unter
dem mit dem Argument -o spezifizierten Ordner
der generierte Code. Aufällig ist, dass ebenfalls eine
build.xml
generiert wurde, die für die weiteren Schritte notwendig ist.
Theoretisch hätte ein weiterer Task den generierten Code kompilieren
und verpacken können aber es fehlt natürlich noch etwas fundamental
wichtiges: die Geschäftslogik.
Implementierung der Geschäftslogik
Die Logik wird in der Skeleton Datei implementiert. In unserem Fall
ist es die Klasse bakery.BakeryServiceSkeleton,
Methode whereIsMyOrder:
package bakery;
import bakery.bean.xsd.Order;
public class BakeryServiceSkeleton {
public bakery.WhereIsMyOrderResponse
whereIsMyOrder() {
WhereIsMyOrderResponse response = new WhereIsMyOrderResponse();
Order order = new Order();
order.setStatusId(123);
order.setStatusText("Your order is on the way");
response.set_return(order);
return (response);
}
}
Art und Qualität der Logik sollen hier nicht zur Debatte stehen... Im
Anschluß kann die generierte build.xml ausgeführt werden und man erhält
im Ordner build/lib die Archive BakeryService.aar und
BakeryService-test-client.jar. Das aar-Archiv ist unser Service, das
jar-Archiv ist für die clientseitige Kommunikation mit dem
BakeryService notwendig.
Deployen des Service, erstellen eines Client
In der Axis2
Adminkonsole hat
man nun die Möglichkeit den Service zu deployen. Die Adminkonsole
erreicht man unter http://localhost:8080/axis2/axis2-admin/,
Standardbenutzer ist admin, Passwort axis2. Unter Upload
Service lädt man nun das erstellte aar-Archiv in den Tomcat
und sollte wenige Sekunden später den Bakery Service unter Available
Services sehen.
Gemäß des gewählten Beispiels hat die Bäckerei nun ihren Service zur
Verfügung gestellt, das Hotel darf und kann die Abfrage nun
implementieren.
Für den ersten Test reicht eine simple Java-Klasse, benötigt wird im
Classpath die generierte BakeryService-test-client.jar sowie die
entsprechenden Archive aus AXIS_HOME/lib.
BakeryServiceClient:
package bakery.client;
import bakery.BakeryServiceStub;
import bakery.WhereIsMyOrderResponse;
import bakery.bean.xsd.Order;
import java.rmi.RemoteException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.axis2.AxisFault;
public class BakeryServiceClient {
public static void main(String[] args) {
BakeryServiceStub stub;
WhereIsMyOrderResponse response;
try {
stub = new BakeryServiceStub(null,
"http://localhost:8080/axis2/services/BakeryService");
response = stub.whereIsMyOrder();
Order order = response.get_return();
if (order != null) {
long statusId = order.getStatusId();
String statusMsg = order.getStatusText();
System.out.println("Status Id: " + statusId);
System.out.println("Status Msg: " + statusMsg);
} else {
System.out.println("There is no order for you.");
}
} catch (AxisFault ex) {
Logger.getLogger(BakeryServiceClient.class.getName()).log(Level.SEVERE,
null, ex);
} catch (RemoteException ex) {
Logger.getLogger(BakeryServiceClient.class.getName()).log(Level.SEVERE,
null, ex);
}
}
}
Die Kommunikation wird über den sogenannten Stub initiert. Nach
erfolgreicher Initierung stehen uns alle Operationen des Service zur
Verfügung und die Abfrage nach dem Bestellstatus sollte auf der Konsole
folgendes hervorbringen:
Status Id: 123
Status Msg: Your order is on the way
Ausblick
Die gezeigte Implementierung ist auf einem recht niedrigen und
hoffentlich gut nachvollziehbarem Level gehalten. Alleine die
Möglichkeiten von Axis2, der technische Background sind nur kurz
angerissen. Wichtige Schlagworte für ein weiteres Studium der Materie
sind
- ESB (Enterprise Service Bus) - technische Middleware-Integrationsinfrastruktur zur Vernetzung der Services
- Business Process Execution Language (BPEL) - Beschreibungssprache zur Orchestrierung von Services
Weitere Links
W3C - SOAP Specification
Torsten Horn - SOA
IBM - Develop asynchronous Web services with
Axis2
Golem.de
- Web-Service-Framework Apache Axis2 veröffentlicht
buildblog - Webservices mit Java und Apache
Axis 2 - Teil 2 - Sofort loslegen!
Axis2 Web Service Support in NetBeans IDE 6.1
IBM: Java Web Services, Part 3: Axis2 Data Binding
java.net: Invoking Web Services using Apache Axis2
Axis 1.x or Axis2?
Using JSR181 annotations with Axis2
Nachdem es in den letzten Jahren in der Fachpresse etwas ruhiger um SOA geworden ist, taucht der Begriff in letzter Zeit wieder öfters auf. Folgender Artikel soll einen Überblick darüber geben, was eine "SOA" in fachlicher und technischer Hinsicht eigentl
Aufgenommen: Jul 21, 10:26
Es haben sich doch vermehrt neue Beiträge von mir im Naxos Software & Technology Blog angesammelt: Know your enemy - Negative CAPTCHA SOA I: Serviceorientierte Architektur - The Buzzword strikes again Installation von Bea Weblogic Server 9....
Aufgenommen: Jul 23, 10:46


Zum diesem Thema würde ich gerne Apache CXF evaluieren.