Im Rahmen eines Projektes sollten XML Mockup Dateien in einer Datenbank gehalten und über eine Webservice-Schnittstelle ausgelesen werden. Für gewöhnlich habe ich aus der XSD per JAXB Java Klassen generiert und in Entity Beans gemappt, die anschließend in die Datenbank geschrieben werden. Um dieses, aus meiner Sicht recht aufwendige und fehleranfällige Mapping in die Entity Beans zu umgehen, habe ich mich nach einem alternativen Framework umgesehen und bin mit Hyperjaxb3 fündig geworden. Hyperjaxb3 geht einen Schritt weiter als JAXB, in dem es die generierten Klassen per Annotation zu vollständigen JPA Entitybeans macht. Aktuell steht Hyperjaxb3 in der Version 0.5.4 unter https://hyperjaxb3.dev.java.net zum Download bereit. Unterstützt werden Hibernate und Oracle TopLink, wobei es mit einigen Anpassungen auch mit OpenJPA und EclipseLink funktioniert. Aus meiner Sicht ist aber der Einsatz von OpenJPA (1.2.2) mit Hyperjaxb3 nicht zu empfehlen, da es zu Problemen mit den Relations kam (Es werden keine bidirektionalen Beziehungen unterstützt). Ein weiteres Problem sind die unterschiedlichen Datentypen, da JAXB und JPA nicht vollständig kompatibel sind. Beispiele kann man auf der z.Z. noch etwas lückenhaften Wiki Seite von Hyperjaxb3 finden. http://confluence.highsource.org/display/HJ3/Home
Der große Vorteil dieses Frameworks ist, dass die Erzeugung der Klassen sehr leicht ins Maven eingebunden werden kann und das das Persistieren nur wenige Zeilen Code nötig macht.
Zum erzeugen der Beans ist eigentlich nur die Dependency für Hyperjaxb3 der bestehenden pom.xml hinzuzufügen:
Der Aufruf der Codegenerierung sollte sinnvollerweise in einem Profile-Zweig erfolgen, damit nicht bei jedem Compile die Klassengenerierung neu angestoßen wird.
Die Generierung kann dann per Aufruf des Profiles erfolgen: mvn install -Phyperjaxb
In der Anwendung kann dann das Objekt aus dem XML mithilfe des Unmarshallers erzeugt …
und vom Entitymanager persistiert werden:
Optional kann am Unmarshaller noch die Validierung eingeschaltet werden:
Hyperjaxb3 kann bei der Erzeugung von komplexen Datenmodellen aus einer vogegebenen XSD sehr hilfreich sein. Es werden alle Relations, die persistence.xml mit allen nötigen Konfigurationen und die ObjectFactory erzeugt.
Auch die zweite Veranstaltung der Java User Group Saxony kann als voller Erfolg bezeichnet werden. Shaun Smith, Project Lead des EclipseLink Projektes und Produktmanager für Oracle TopLink, gab am 09.07.2008 einen Überblick über die Features und die Funktionsweise von EclipseLink.
Die Firma ubigrate hat diese Veranstaltung an der Informatik Fakultät der TU-Dresden in Zusammenarbeit mit dem JUG Saxony Team für die hiesige User Group organisiert und somit mehr als 50 Teilnehmern einen informativen und unterhaltsamen Abend ermöglicht.
Shaun Smith ging während seines Vortrages detailliert auf die Java Persistence API und die Referenzimplementierung EclipseLink ein. Dabei ist insbesondere die Vielseitigkeit des Frameworks hervorzuheben. Neben Objekt-Relationalem Mapping (ORM) stellt es Funktionen zum Objekt-XML Mapping (in Version 1.0 noch nicht 100% JAXB 2.0 kompatibel) sowie Unterstützung für Service Data Objects (SDO) 2.1 zur Verfügung. Sollte dies noch nicht genügen kann EclipseLink leicht um weitere Mapping Ziele erweitert werden wie Shaun Smith anhand eines JSON Beispiels demonstriert hat.
Neben der Vorstellung von technischen Details und der ein oder anderen Anekdote aus dem Projektgeschäft (wer sich dafür interessiert sollte die kommenden JUG Veranstaltungen nicht verpassen
) wurden auch die verschiedenen Mappings und das Zusammenspiel mit den Eclipse Dali JPA Tools im Rahmen von Live Demonstrationen vorgeführt.
Am Tag unserer Veranstaltung wurde EclipseLink 1.0 veröffentlicht und somit für den Produktiveinsatz freigegeben. Einen genaueren Blick ist es auf alle Fälle wert.
Das nächste Treffen der JUG Saxony ist für Oktober 2008 geplant. Das Thema lautet Rich Client Platform (RCP) Entwicklung mit Java.
Informationen zur kommenden Veranstaltung gibt es demnächst auf der Webseite der JUG Saxony unter http://www.jugsaxony.de. Die Folien zum EclipseLink Vortrag sind ebenfalls auf dieser Seite zu finden.
Einen Bericht zum Treffen in englischer Sprache gibt es im ubigrate Blog.
In einem existierenden Java2 1.4 Projekt sollen generierte XFire-Clientklassen verwendet werden. Der XFire-Codegenerator verwendet JAXB2 und ist damit auf einige Java2 5.0-Features wie Annotations angewiesen. Der Umstieg auf Java2 5.0 ist in dem Projekt keine Option und deshalb habe ich eine Möglichkeit gebraucht, die generierten Klassen mit Java2 1.4 zu verwenden.
Nach ein paar Recherchen habe ich mich für die Verwendung des freien Retrotranslator-Tools entschieden und eine Proof-Of-Concept-Implementierung realisiert. Retrotranslator kann sowohl zur Compile-Zeit als auch zur Laufzeit den Java2 5.0 Bytecode in Java2 1.4 Bytecode umwandeln. Für den eigenen Code habe ich mich für die Umwandlung zur Compilezeit entschieden und für verwendeten Code Dritter für die Umwandlung zur Laufzeit (Just in Time Compilierung).
Nun zur praktischen Umsetzung mit Maven2:
Das POM des Codegenerierungsprojektes sieht folgendermaßen aus:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.communardo.techblog</groupId>
<artifactId>demoxfiregen</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Generierter XFire-Code fuer Communardo Techblog</name>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>xfire-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>wsgen</goal>
</goals>
</execution>
</executions>
<configuration>
<package>
de.communardo.techblog.services
</package>
<profile />
<binding />
<outputDirectory>
${project.build.directory}/generated-sources/xfire
</outputDirectory>
<wsdls>
<wsdl>
${basedir}/src/main/resources/TechblogDemoService.wsdl
</wsdl>
</wsdls>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>retrotranslator-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>translate-project</goal>
</goals>
<configuration>
<classifier>jdk14</classifier>
<attach>true</attach>
<embed>net.sf.retrotranslator</embed>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.codehaus.xfire</groupId>
<artifactId>xfire-jaxb2</artifactId>
<version>1.2.5</version>
</dependency>
</dependencies>
</project>
Der erste Plugin-Aufruf setzt die Java-Version auf 1.5, der Zweite generiert den Java-Code aus einer WSDL und der dritte generiert in der package-Phase von Maven ein Java2 1.4 kompatibles JAR-Archiv mit dem Classifier jdk14. Mit
mvn install
wird alles gebaut und ins lokale Maven-Repository installiert.
Im Java2 1.4-Projekt wird das generierte JAR in den Dependencies so referenziert:
<dependency>
<groupId>de.communardo.techblog</groupId>
<artifactId>demoxfiregen</artifactId>
<version>1.0-SNAPSHOT</version>
<classifier>jdk14</classifier>
</dependency>
Um alle Abhängigkeiten, die von XFire und den JAXB2-Klassen benötigt werden zu erfüllen und den Retrotranslator Just-In-Time-Compiler (JIT) vefügbar zu machen, wurden noch folgende Dependencies in das Ziel-POM eingetragen:
<dependency>
<groupId>net.sf.retrotranslator</groupId>
<artifactId>retrotranslator-transformer</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>javax.xml.parsers</groupId>
<artifactId>jaxp-api</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.sun.org.apache</groupId>
<artifactId>jaxp-ri</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.8.1</version>
</dependency>
Damit das Ganze dann zur Laufzeit funktioniert muss beim Start der Applikation der Retrotranslator JIT genutzt werden, das geschieht in dem man die JVM-Kommandozeile folgendermaßen aufbaut:
java <JVM-Parameter> net.sf.retrotranslator.transformer.JITRetrotranslator <Main-Klasse> <Anwendungsparameter>
Nach dem gestrigen Erfolg folgte heute eine Anforderung, die den Wechsel zu einem anderen Maven2-Plugin und einer neuen Mapping-Datei führte.
Die neue Anforderung war, dass Attribute aus der DTD auf andere Typen als String gemappt werden sollen. Da DTDs keine Typisierung wie XML-Schema kennen, muss dies mit einem Mapping-File geschehen. Das Maven2-Plugin aus dem gestrigen Artikel war leider mit dem Mapping der Attribute überfordert.
Jetzt wurde das Maven2-JAXB-Plugin von java.net verwendet, damit ändert sich die pom.xml folgendermaßen:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.mms-dresden</groupId>
<artifactId>hsp-search-gen</artifactId>
<name>Hilfe- und Support-Portal generierter Code fuer die Suche</name>
<version>0.1-SNAPSHOT</version>
<parent>
<groupId>de.mms-dresden</groupId>
<artifactId>hsp</artifactId>
<version>0.1-SNAPSHOT</version>
</parent>
<description>Generierter Code fuer die Suche im Hilfeportal</description>
<build>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<generateDirectory>${project.build.directory}/generated-sources/jaxb</generateDirectory>
<extension>true</extension>
<schemaLanguage>DTD</schemaLanguage>
<schemaIncludes>
<schemaInclude>*.dtd</schemaInclude>
</schemaIncludes>
<bindingIncludes>
<bindingInclude>*.jaxb</bindingInclude>
</bindingIncludes>
<verbose>true</verbose>
<args>
<arg>-Xinject-listener-code</arg>
</args>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.jvnet.jaxb2-commons</groupId>
<artifactId>property-listener-injector</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>maven2-repository.dev.java.net</id>
<url>http://download.java.net/maven/2/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>maven2-repository.dev.java.net</id>
<url>http://download.java.net/maven/2/</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
</dependencies>
</project>
Bei der Angabe der Repository-Pfade muss man aufpassen, weil die Angaben auf der Projektseite des Plugins inzwischen nicht mehr stimmen. Stattdessen mussten die Pfade aus der Repositoryseite von java.net verwendet werden.
Die Dependency-Versionen sind jetzt im Parent-POM definiert, entsprechen aber den Versionen aus dem gestrigen Beitrag.
Das Mapping mappt jetzt das Attribut HITCOUNT des Elements NAVIGATIONENTRY auf den Java-Typ int (genauer gesagt den Wrapper Integer um auch null-Werte darstellen zu können) und sieht jetzt folgendermaßen aus:
<?xml version="1.0" ?>
<!--
The syntax of the binding file for DTD is defined in the JAXB EA.
See vendorSchemaLangs.html for details.
-->
<xml-java-binding-schema xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:ci="http://jaxb.dev.java.net/plugin/listener-injector">
<!-- specify the package name for the generated code -->
<options package="de.mms_dresden.mip.hsp.base.services.search" />
<xjc:serializable/>
<element name="NAVIGATIONENTRY" type="class">
<attribute name="HITCOUNT" convert="int" />
</element>
</xml-java-binding-schema>
Heute mussten aus einer existierenden DTD ein paar JAXB2-Binding-Klassen generiert werden. Nach einigem probieren ist dies mit folgender pom.xml und einem passenden Binding-File gelungen.
Hier die pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
>
<modelVersion>4.0.0</modelVersion>
<groupId>de.mms-dresden</groupId>
<artifactId>hsp-search-gen</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>Generierter Code fuer die Suche im Hilfeportal</description>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaDirectory>${basedir}/src/main/dtd</schemaDirectory>
<dtd>true</dtd>
<xmlschema>false</xmlschema>
<schemaFiles>FastSearchResult.dtd</schemaFiles>
<strict>false</strict>
<verbose>true</verbose>
<explicitAnnotation>true</explicitAnnotation>
<packageName>de.mms_dresden.mip.hsp.base.services.search</packageName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-xjc</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies>
</project>
und hier noch das passende File FastSearchResult.xjb:
<?xml version="1.0" encoding="UTF-8"?> <xml-java-binding-schema> <options package="de.mms_dresden.mip.hsp.base.services.search" /> <element name="SEGMENTS" type="class" root="true" /> </xml-java-binding-schema>
Die generierten Klassen landen damit in target/generated-sources/jaxb
Als Ergänzung zu dem Beitrag “XFire-WebService-Client mit Maven2 generieren” möchte ich heute noch eine Implementierung für den Service vorstellen, damit man das Beispiel aus dem ersten Beitrag lokal nachvollziehen kann.
Die Implementierung benutzt den XFireServer, der auf Jetty basiert und damit recht leichtgewichtig ist. Bei Interesse werde ich in einem weiteren Beitrag noch die Integration mit Tomcat und Spring beschreiben.
Als Basis für dieses Beispiel dient die WSDL und der generierte Java-Code aus dem ersten Artikel. Die benötigten Dateien befinden sich in der Datei xfirefun-service.zip. Die Implementierung liefert angeforderte Dateien aus und benutzt die JaxbServiceFactory für die eigentliche Servicegenerierung.
Releaseparty at Atlassian? Confluence 3.2 BETA and 3.1.2 with soms bugfixes were released yesterday. [...]
Tino Schmidt's Vortrag zu Enterprise Mashups auf der webciety, 4.3 Remix the Web http://bit.ly/d26rtA [...]
neuer Blogpost: February Cumulative Update (2010) http://bit.ly/cwxZGE [...]
Webinar am 16.03.: „Communote Enterprise Microblogging - Funktionen und Einsatzbereiche im Unternehmen“ http://bit.ly/96eexF [...]