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>
OutOfMemory ist eines der am häufigsten auftretenden Probleme bei Java-basierten Webapplikationen. Die Ursachen dafür sind genau so vielschichtig wie die Möglichkeiten der Analyse. Ziel dieses Artikels ist es, sowohl die Grundlagen wie auch mögliche Lösungswege zu zeigen.
Bevor man jetzt leichtfertig die Software umprogrammiert (da man ja genau weiß, wo das Problem liegt), sollte man mit einer ausführlichen Analyse beginnen. Nicht selten kommt es vor, dass Software (Parameter der JVM) oder sogar das Betriebssystem nicht korrekt konfiguriert sind.
Die Process Size ist der maximal verfügbare Speicher für den Prozess. Dieser ist abhängig von der Hardware und vom Betriebssystem. In einer 32 Bit-Architektur kann die Process Size maximal 4 GB sein. Für einen Java-Prozess gibt es folgende Bereiche, die im Speicher des Prozesses liegen (siehe Abbildung 1):

Abbildung 1: Schematischer Aufbau der Speicherverteilung eines Java-Prozesses
Der Speicher für den Native Code ist nicht explizit einstellbar. Er ergibt sich aus der Differenz zwischen Process Size, Heap und Permanent Generation. Dieser Speicher darf nicht zu knapp bemessen sein, denn es kann auch OutOfMemory im Native Code geben.
Die Java Heap Size gibt die Größe des Speichers an, der für die Erzeugung und Verwaltung der Java-Objekte verwendet werden kann. Die Größe des Heaps ist begrenzt. Wird keine Größe explizit angegeben, wird die Defaulteinstellung verwendet. Diese variiert von Betriebssystem zu Betriebssystem. Wenn man wirklich sicher gehen möchte, stellt man die Größe explizit ein:
-Xms[Bytes]m : initiale Größe des Heaps beim Starten des Prozesses-Xmx[Bytes]m : maximale Größe des Heaps über den gesamten LebenszeitraumDer Heap teilt sich in zwei große Bereiche: der New Generation und der Old Generation (siehe Abbildung 2).
Wie die Namen schon vermuten lassen, befinden sich in der New Generation die ganzen neuen, jungen und kurzlebigen Java-Objekte und in der Old Generation die langlebigen Java-Objekte.

Abbildung 2: Aufteilung der Speicherbereiche im Heap
Das Verhältnis zwischen New und Old Generation ist aus Speichersicht der größte Unterschied zwischen einer Desktop- und einer Serveranwendung. Eine Desktopanwendung läuft in der Regel einen Arbeitstag, also ca. 8 h. Eine Serveranwendung läuft 24 h, 7 Tage die Woche und das mehrere Monate. Daran kann man schon erkennen, dass es bei einer Desktopanwendung mehr jüngere Objekte und bei einer Serveranwendung mehr ältere Objekte gibt. Mit dem Servermodus stellt man das Verhältnis zwischen New und Old Generation auf 1:3 ein.
-server :Servermodus für die VMDie Permanent Generation wird von der JVM für das Laden der Klassen durch den ClassLoader verwendet. Normalerweise ist die Defaulteinstellung der Größe ausreichend. Wenn allerdings viele Klassen dynamisch durch die Applikation geladen werden (z.B. durch Reflection), kann die Defaulteinstellung zu Problemen führen. Auch die Verwendung von Persistenz- oder Caching-Frameworks führt zu größerem Speicherverbrauch in der Permanent Generation.
Über folgende Parameter kann die Defaultgröße der Permanent Generation angepasst werden:
-XX:PermSize=[Bytes]m : initiale Größe der Permanent Generation-XX:MaxPermSize=[Bytes]m : maximale Größe der Permanent GenerationMan unterscheidet bei der Garbage Collection in die („einfache“) Garbage Collection und in die Full Garbage Collection. Bei der Garbage Collection werden nicht mehr referenzierte Objekte aus der New Generation freigegeben. Des Weiteren werden noch „lebende“ Objekte, die durch mehrere Garbage Collection nicht freigegeben wurden, in die Old Generation kopiert. Bei einer Full Garbage Collection werden auch Objekte aus der Old Generation aufgeräumt.
Eine Full Garbage Collection ist sehr zeitintensiv (bei 2 GB Heap Size = mehrere Sekunden). Während dieser Zeit reagiert die Applikation nicht mehr.
Ist die Heap Size zu gering eingestellt, findet eine Full Garbage Collection nach der anderen statt. Dies führt zu einer sehr schlechten Performance und zu langen Antwortszeiten.
In einer Multiprozessor-Maschine ist es deshalb ratsam, die Garbage Collection („einfache“ und Full) parallel arbeiten zu lassen.
-XX:+UseParallelGC: parallele Garbage Collection und Full Garbage CollectionNachdem in diesem Artikel die Grundlagen erläutert wurden, werden im nächsten Artikel konkrete Analyse-Möglichkeiten besprochen.
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 [...]