Artikel mit ‘Java’ getagged

WebServices mit JBossWS realisieren

Dienstag, 14. Dezember 2010

Zur Zeit befasse ich mit mit der Realisierung von WebServices mit JBossWS in einem JBoss AS 4.2.3.GA. Hier möchte ich kurz zusammenfassen, was ich beachtet habe und wie ich bei der Service-Implementierung vorgegangen bin.

JBoss AS und Java6

Betrieben wird ein JBoss AS 4.2.3.GA (und ein 4.2.2.GA; Java 5 compiled binaries) in einer Java 6 Umgebung. Auf die Gründe, wesghalb kein JBoss AS 5.x eingesetzt wird möchte ich nicht näher eingehen und wir nehmen die Umgebung als gegeben hin. In dieser Umgebung gilt es allerdings folgenden Konfigurationshinweis aus der 4.2.3.GA Distribution zu beachten, die analog für Version 4.2.2.GA gilt:

JBossAS 4.2.3.GA can be compiled with both Java5 & Java6. The Java5 compiled binary is our primary/recommended binary distribution. It has undergone rigorous testing and can run under both a Java 5 and a Java 6 runtime. When running under Java 6 you need to manually copy the following libraries from the JBOSS_HOME/client directory to the JBOSS_HOME/lib/endorsed directory, so that the JAX-WS 2.0 apis supported by JBossWS are used:

  • jboss-jaxrpc.jar
  • jboss-jaxws.jar
  • jboss-jaxws-ext.jar
  • jboss-saaj.jar
  • jaxb-api.jar

JAX-WS und JAX-RPC

“JAX-WS” und “JAX-RPC” laufen einem immer wieder über den Weg, sobald man sich mit WebServices in der Java-Welt beschäftigt. Anfangs stellte sich mir die Frage, was sich hinter diesen Begriffen verbirgt und was dabei der Unterschied ist. Ich fand hierzu einen interessanten Artikel von Vamshi Rapolu in dessen Blog und ich verzichte an dieser Stelle näher auf die Beantwortung meiner Frage einzugehen. Weitere Informationen findet der interessierte Leser in diesem Artikel und sicherlich auch über die Suchmaschine seines Vertrauens.

Warum nicht Axis2?

Warum ich mich für JBossWS als WebService Framework entschieden habe und nicht für Apache Axis2 hat folgende Gründe:

Axis2 eignet sich vor allem für den Einsatz in einem Servlet-Container wie z.B. einem Apache Tomcat - ohne den Rattenschwanz eines Applikationsservers wie dem JBoss (Was ist der Unterschied?). Axis2 wird einfach als eigenständige Applikation “deployed” und steht danach als WebService-Container zur Verfügung, in den wiederum die Services “deployed” werden. Für meine Zwecke war dies eher ungeeignet, da sich der WebService zusammen mit anderen Applikationskomponenten in einem *.ear-Archiv veröffentlicht werden soll. Zwar gibt es Wege auch Axis2 als weitere Applikationskomponente in das Archiv zu packen, doch das erschien mir alles irgendwie “zusammengeflickt”. Außerdem bringt der JBoss AS selbst ein WebService-Framework mit: JBoss WS. Und warum soll man nicht auf vorhandenes zurückgreifen? Ein weiterer Vorteil ist, dass WebServices direkt mit EJB3 Stateless Session Beans implemntiert werden können und innerhalb des Services per Dependency Injection auf vorhandene EJBs zugegriffen werden kann. Dies war schließlich ausschlaggebend für die Entwicklung eines JAX-WS WebServices und damit der Verwendung von JbossWS.

Zur weiteren Lektüre gibt es bei predic8.de einen Vergleich von Axis2, CXF und der JAX-WS Referenzimplementierung.

Das Service-Interface

MyService.java

package de.budisantoso.examples.jaxws;

import javax.ejb.Remote;
import javax.jws.WebService;

@Remote
@WebService()
public interface MyService {
    public static final String ENDPOINT_INTERFACE = "de.budisantoso.examples.jaxws";
    public static final String NAMESPACE = "http://jaxws.examples.budisantoso.de/";
    public static final String SERVICE_NAME = "MyService";

    public String echo(String input);
    public void foo();
}

Im Interface habe ich ein paar Konstanten definiert, um sie bequem an zentraler Stelle konfigurieren zu können. Die @Remote-Annotation gibt an, dass es sich um ein EJB-Remote-Interface handelt und die @WebService-Annotation gibt an, dass dieses Interface einen WebService definiert.

Die Stateless Session Bean

MyServiceBean.java

package de.budisantoso.examples.jaxws;

import javax.ejb.Stateless;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

@Stateless
@WebService(endpointInterface = MyService.ENDPOINT_INTERFACE, serviceName = MyService.SERVICE_NAME)
@SOAPBinding(style = javax.jws.soap.SOAPBinding.Style.DOCUMENT)
public interface MyServiceBean implements MyService {
    public static final String ENDPOINT_INTERFACE = "de.budisantoso.examples.jaxws";
    public static final String NAMESPACE = "http://jaxws.examples.budisantoso.de/";
    public static final String SERVICE_NAME = "MyService";

    @EJB
    private AnotherBeanRemote anotherBean;

    @Override
    @WebMethod()
    public String echo(String input) {
        return input;
    }

    @Override
    @WebMethod()
    public void foo() {
        anotherBean.bar();
    }
}

Die Implementierung des Interface erhält drei Annotationen:

  • @Stateless - EJB-Annotation, die angibt, dass es sich um eine Stateless Session Bean handelt
  • @WebService(…) - gibt an, dass es sich bei dieser Klasse um eine WebService-Implementierung handelt
  • @SOAPBinding(…) - hierüber wird das Mapping des WebService auf das SOAP Message Protocol definiert

Weitere Infos zu den WebService-Annotationen gibt es im JBoss Community Wiki bzw. direkt in der JSR-181.

Deployment

Das Service-Interface und die Service-Implementierung können zusammen mit weiteren Applikationskomponenten in einer *.ear-Datei gepackt werden. Auf den genauen Build-, Assembly-und Deploy-Prozess und die Projekt- und Archivstruktur möchte ich jedoch nicht weiter eingehen. Hat man schließlich das *.ear-Archiv erstellt, “schiebt” man es in das Deploy-Verzeichnis des JBoss, z.B. [JBOSS_HOME]/server/default/deploy und startet den Applikationsserver. Zugriff auf die automatisch generierte WSDL erhält man dann über die JBossWS-Webapplikation, die beispielsweise (bei Standardinstallation eines JBoss auf dem lokalen Rechner) unter folgender URL erreichbar ist:
http://localhost:8080/jbossws/

Kurz erwähnen und empfehlen möchte ich an dieser Stelle noch das Tool “soap-UI“, mit dem man WebServices schnell und einfach testen kann.

Der Service-Client

Die Implementierung des Service Client kann auf mehrere Wege erfolgen. Zum einen kann der WebService über JAX-WS Direktzugriff angesprochen werde. Daneben gibt es die Möglichkeit, generierte “Stubs” zu verwenden (z.B. über “wsconsume” oder auch über das Axis2-Tool “WSDL2Java”).

Hier ein Beispiel für den direkten Zugriff:

MyServiceClient.java

package de.budisantoso.examples.jaxws;

import java.net.URL;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;

public class MyServiceClient {

    private static final String _wsdl = "http://localhost:8080/examples/MyServiceBean?wsdl";

    public static void main(String[] args) {
        try {
            URL wsdlLocation = new URL(_wsdl);
            QName serviceName = new QName(MyService.NAMESPACE, MyService.SERVICE_NAME);

            // Load the service implementation class
            Service remoteService = Service.create(wsdlLocation, serviceName);

            // Load a proxy for our class
            MyService myService = (MyService) remoteService.getPort(MyService.class);

            // Execute a WebMethod
            String echo = myService.echo("Hello WebService-World!");
            System.out.println(echo);

            // Execute another WebMethod
            myService.foo();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Weitere Informationen

Kommentare und Verbesserungsvorschläge zu meinem Artikel sind natürlich sehr willkommen. Und auch falls es Fragen gibt, werde ich versuchen diese zu beantworten - dabei liegt die Betonung auf “versuchen” ;)

JAX2010 - ich komme

Donnerstag, 25. März 2010

Im Mai findet die diesjährige JAX statt und ich habe das Vergnügen einen Workshop-Tag und einen, evtl. sogar zwei Hauptkonferenztage besuchen zu dürfen. Wer die JAX nicht kennt, sei mit folgender Wikipedia-Erläuterung aufgeklärt:

Die JAX ist eine Fachkonferenz für Softwareentwicklung und zusammen mit der OOP eine der größten Informationsveranstaltungen der IT-Branche zu diesem Thema in Deutschland. Sie findet aktuell in Mainz statt. Veranstalter der Konferenz ist der Software & Support Verlag. Der Name JAX ist ein Akronym für Java, Apache und XML.

 Auf meiner Agenda stehen:

Ich weiß, das ist für einen Tag Hauptkonferenz ein sehr ambitioniertes Programm, aber solange die Vorträge interessant sind … :)

Auf jeden Fall werde ich einen zusammenfassenden Artikel zu meinem JAX2010-Besuch verfassen und vielleicht ist auch der ein oder andere Vortrag einen eigenen Artikel wert.

Konzept zur Programmierung

Donnerstag, 26. Februar 2009

In den letzten Tagen habe ich mir nach den bisher gewonnenen Erkenntnissen unter anderem ein grobes Konzept überlegt, wie eine Programmierung der Transformationen in Java aussehen könnte, das ich im Folgenden vorstellen möchte.

Package "views"

Package"views"

Sehen wir uns zunächst den Inhalt des von mir modellierten Package “views” an:

Zu Grunde liegt eine abstrakte Klasse Generation, deren Instanzen das Ergebnis aus von dem SUM ausgehenden Transformationen (-> ViewGenerations) repräsentieren. In den Attribute dieser Klasse werden zum einen die Repräsentation der .uml-Datei (umlFile) und zum anderen die Information, ob diese Datei verändert wurde, gespeichert (isModified).

Diese Klasse wird wiederum von einer abstrakten Klasse View erweitert, die die Informationen zu der Darstellung der Daten, die in einer .umldi-Datei gespeichert werden, kapselt (umldiFile).

Schließlich wird die eben genannte Klasse nochmals erweitert. Die erweiternden Klassen repräsentieren schließlich die tatsächlichen Sichten (z.B. SpecificationClassServiceView und SpecificationClassTypeView).

Package "transformation"

Package"transformation"

Die eben vorgestellten Elemente werden in dem zweiten modellierten Package “transformation” verwendet, das etwas komplizierter aufgebaut ist:

Ausgangselement dieses Package ist das generische Interface ViewGenerator. In Abhängigkeit des Parameters TGeneration, der die Klasse Generation erweitern muss, wird über die Methode generate() eine View aus dem SUM erzeugt. Als Parameter muss hierbei die Repräsentation der Zieldatei übergeben werden. Das Gegenstück zu dieser Methode, die die Transformation von SUM zu View repräsentiert, ist merge(). Hier wird die als Methodenparameter übergebene View wieder mit dem SUM zusammengeführt.

Dieses Interface wird von einer abstrakten Klasse AbstractViewGenerator “quasi-implementiert”, d.h., dass diese Klasse alle gemeinsamen Attribute und Methoden der verschiedenen View-Generatoren beherbergen soll, wie z.B. die (protected) Methode selectSubject(), die anhand des Klassennamens das entsprechende Element aus dem SUM selektieren und ggf. mit dem vorgesehenen Stereotyp “subject” versehen soll. Der Rückgabewert ist derzeit noch vom Typ org.eclipse.uml2.uml.Class, doch kann es sein, dass dies noch geändert werden muss.

Die konkreten Erweiterungen des (ebenfalls generischen) abstrakten Generators sind schließlich die einzelnen View-Generatoren, z.B. SpecificationClassServiceViewGenerator und SpecificationClassTypeViewGenerator.

Soweit so gut :) Doch ich habe mich entschieden, das Ganze noch etwas zu erweitern, in dem ich das “factory method pattern” (http://en.wikipedia.org/wiki/Factory_method_pattern) anwende.

Zu verwenden ist die daraus entstandene ViewGeneratorFactory folgendermaßen: Über den Konstruktor wird der Factory-Instanz mitgeteilt, welche Datei das SUM repräsentiert. Die Methode getViewGenerator(), der man als Parameter die Klasse der zu erzeugenden View übergibt liefert über ein Mapping stets den passenden ViewGenerator.

Durch die Anwendung dieses Pattern ergeben zwei offensichtliche Vorteile: Zum einen ist es nur noch bei der Erzeugung einer Factory-Instanz notwendig, die SUM-Datei anzugeben. Zum anderen entsteht dadurch ein Transformation-Framwork, das einfach um weitere Views erweitert werden kann. Dazu müssen nur zwei Klassen implementiert werden: eine, die die Klasse Generation (oder View) und eine, die die Klasse AbstractViewGenerator erweitert. Dann muss nur noch die Klasse der ViewGenerator-Implementierung und die zugehörige View-Klasse in der Factory registriert werden.

org.eclipse.uml2 - Erste Schritte und Erfolge

Donnerstag, 19. Februar 2009

Mir war einige Zeit unklar, wie ich beginnen soll, auf programmatischem Weg eine .uml-Datei mittels des UML2-Plugins von Eclipse zu erzeugen.

(more…)

XML-Datei parsen und XPath-Selektion erfolgreich

Montag, 16. Februar 2009

Wie verwende ich XPath in einem Java-Programm? Diese Frage stellte ich zunächst Tante Gurgel und fand den IBM-Artikel, den ich im vorigen Post bereits erwähnt hatte.
(more…)

Model-Transformation mittels Java und XPath?

Montag, 16. Februar 2009

Meine Idee für die programmatische Transformation aus dem Single-Underlying-Model (SUM) in eine View (z.B. Structural Specification) ist eine Kombination aus Java und XPath. Für die Rücktransformation habe ich mir bis dato aber noch keine Gedanken gemacht.

Unter der Annahme, dass das SUM im XMI-Format gespeichert ist, könnte ich über XPath die Teile des XML-Baums filtern, die ich für die Transformation benötige. Dies kann dann per Java-Code weiterverarbeitet werden und daraus ein neues Model erzeugt werden, das dann wiederum in eine XMI-Datei serialisiert wird.

http://www.eclipse.org/modeling/mdt/uml2/docs/articles/Getting_Started_with_UML2/article.html

http://www.ibm.com/developerworks/library/x-javaxpathapi.html

Ich werde diesen Ansatz testen, indem ich zunächst versuchen werde, aus einer XMI-Datei mit zwei Klassen eine zu selektieren und diese in die neue .uml-Datei zu schreiben.

@Dietmar, Benjamin: Was haltet ihr davon?