Donnerstag, 16. Juli 2009

Checkliste JBoss-> Glassfish

Ich bin derzeit damit beschäftigt, meine JavaEE Anwendung soweit zu konfigurieren und zu testen, daß sie unter mehreren, führenden Applicationservern sowie Persistence Providern deployed werden kann. Mein Vorhaben war erfolgreich, stellte sich jedoch als anfangs etwas aufwändig heraus. Ich kann hier keinen vollständigen Bericht abliefern, was alles getan werden muß, um eine in JBoss laufende Applikation in Glassfish zum Deployen zu bekommen. Jede Anwendung ist an sich auch unterschiedlich. Aber eine kleine Checkliste möchte ich niemandem vorenthalten. Meine Anwendung verwendet dabei EJB3, JPA, Hibernate unter JBoss sowie Toplink unter Glassfish, JSF und Facelets.

Die einzige Konfigurationsdatei, an die man beim Deployen in einer anderen Umgebung noch anfassen muß in meinem Fall, ist die persistence.xml. Grund hierfür ist der Persistence Provider. Dieser wird von mir verwendet, um Tabellen automatisch generieren zu lassen.

Hibernate

Create:
<property name="hibernate.hbm2ddl.auto" value="update"/>

Drop & Create:
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>

Toplink

Create:
<property name="toplink.ddl-generation" value="create-tables"/>
<property name="toplink.ddl-generation.output-mode" value="both"/>

Drop & Create:
<property name="toplink.ddl-generation" value="drop-and-create-tables"/>

EclipseLink

Create:
<property name="eclipselink.ddl-generation" value="create-tables"/>

Drop and create
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>

Eine beispielhafte persistence.xml könnte also so aussehen:


<?xml version="1.0" encoding="UTF-8"?>
<!--
(C) 2009 Frederik Mortensen
-->
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="yourPU" transaction-type="JTA">

<!-- GlassFish Configuration //-->
<provider>oracle.toplink.essentials.PersistenceProvider</provider>
<jta-data-source>jdbc/yourdb</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="toplink.ddl-generation" value="create-tables"/>
<property name="toplink.ddl-generation.output-mode" value="both"/>
</properties>
<!-- End of GlassFish configuration -->

<!-- JBoss Configuration -->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/yourdb</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
<!-- End of JBoss Configuration -->

</persistence-unit>
</persistence>


Desweiteren muß für eine Unabhängigkeit von der Implementation des Application Servers dafür gesorgt werden, daß alle benötigten .JAR-Dateien vorhanden sind. JBoss liefert einige dieser .JARs bereits mit, die beim Deployen in GlassFish leider fehlen.

Auch über das Logging muß man sich Gedanken machen. Wer in JBoss log4J nutzte, wird feststellen, daß GlassFish hier mit dem in Java mitgelieferten Logging einen anderen Weg beschreitet. Mit einer gültigen log4j.xml sowie log4j.dtd im /src - Verzeichnis kann man jedoch weiterhin auf dieses Protokollierungsverfahren bauen. Anbei ein kleines Beispiel, wie dieses Logging integriert werden kann:

Log4j.dtd
Enthalten im Log4J-Bundle (evtl. also bereits auf Ihrer Festplatte):
Log4J v1.2

Log4j.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<!--
(C) 2009: Frederik Mortensen
-->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

<appender name="yourappAppender" class="org.apache.log4j.DailyRollingFileAppender">
<param name="datePattern" value="'.'yyyy-MM-dd" />
<param name="file" value="logs/yourappWeb.log" />
<param name="Append" value="true" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{dd.MM.yyyy HH:mm:ss} %C: %m %n" />
</layout>
</appender>

<root>
<priority value="INFO" />
<appender-ref ref="yourappAppender" />
</root>

</log4j:configuration>


Hat man also dafür gesorgt, daß alle JAR-Dateien (commons-logging, log4j, RichFaces...) nun inkludiert sind und die persistence.xml angepasst wurde, sowie das Logging berücksichtig wurde, gibt es noch zwei größere Baustellen: Queries und Lookups.

Bei den EJBQueries ist darauf zu achten, daß sie wie in den Spezifikationen verwendet werden. Das heißt voll ausgeschrieben wie beispielsweise:
"SELECT alias FROM ENTITY alias WHERE..."
JBoss war da weniger restriktiv, da konnte man auch einmal nur mit der FROM clause beginnen, unter Umständen auch mal mit einem kleingeschriebenen "from" oder ähnlichem. Dies führt jedoch in Glassfish zu Problemen. Passt man seine Queries entsprechend an, laufen sie weiterhin in JBoss, aber nun auch in Glassfish.

Hier noch kurz die Unterschiede zwischen JBoss-Lookups und Glassfish-Lookups (ich arbeite daran, eine Lösung zu finden, die in JBoss und Glassfish einheitlich funktioniert). Ich habe den Lookup in eine Klasse EJBService ausgelagert:


package de.mortensenit.yourproject.web.util;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;

public class EJBService {

private static EJBService singleton;
private static Context ctx;

private EJBService() throws NamingException {

if (ctx == null) {
ctx = new InitialContext();
}
}

public static T lookup(Class ejbClassType, String name) {

if (singleton == null) {
try {
singleton = new EJBService();
} catch (NamingException ne) {
throw new RuntimeException(ne.getLocalizedMessage(), ne);
}
}

try {
InitialContext ctx = new InitialContext();
// JBoss: final Object object = ctx.lookup("yourapp/" + name);
// Glassfish: final Object object = ctx.lookup(ejbClassType.getName()); //Name of the interface
final Object object = ctx.lookup(ejbClassType.getName());

if (ejbClassType.isAssignableFrom(object.getClass())) {
return (T) object;
} else {
throw new RuntimeException("EJBService - Class found: " + object.getClass() + " cannot be assigned to type: " + ejbClassType);
}

} catch (NamingException e) {
throw new RuntimeException("EJBService - Unable to find ejb for " + ejbClassType.getName(), e);
}
}

}


Wer neu bei Glassfish ist, muß vor dem Deployen seiner Anwendung zuerst mal ein paar Dinge beachten. Es muß eine Domain erstellt werden, in der die Anwendung letztlich laufen wird. Es muß der JDBC-Treiber in diese Domain kopiert werden und ein Connection Pool angelegt werden. Wenn man also all die aufgezählten Punkte dieses Posts in einer Checkliste zusammenfassen möchte, kommt man in etwa hierzu:

- Anlegen der Domain (asadmin > create-domain --adminport 4848 yourapp)
- Kopieren von postgres/mysql... jdbc.jar in das yourdomain\lib\ext Verzeichnis
(Falls postgres genutzt wird, JDBC4 verwenden!)
- Erzeugen eines ConectionPools im JSF Admin Dialog des Glassfish AppServers (per Ping testen!)
- Erzeugen einer JDBC Connection, die den ConnectionPool benutzt (jdbc/yourapp)
- Wenn log4j verwendet wird, einbinden von log4j.dtd and log4j.xml (JBoss didnt need
- Ggf. Lookups anpassen
- persistence.xml anpassen auf den aktuellen Persistence Provider
- Falls man @mappedsuperclass verwendet hat, muß diese Annotation gelöscht und durch @Entity ersetzt werden, da @mappedsuperclass in Glassfish leider nicht funktioniert (derzeit).
- EJBQueries ggf. anpassen auf den JavaEE5 Standard, siehe oben
- Falls das Schema der Datenbanktabellen in JBoss durch Hibernate angelegt wurde, muß dieses evtl. bei früherer Verwendung von @mappedsuperclass gedroppt und neu angelegt werden. Allerdings sind die datenbankseitigen Unterschiede zwischen Hibernate und Toplink minimal.

Das wars. Viel Glück und bei Fragen wie immer einfach einen Kommentar hinterlassen ;)

Mehr Informationen zum Thema:
EclipseLink JPA
Logging Patterns

Keine Kommentare:

Kommentar veröffentlichen

Hier kann jeder (auch unregistrierte) Leser seine Meinung kundtun...