Apache Tomcat ist eine leichtgewichtige Alternative zu einem vollwertigen Applikationsserver, falls nur die Servlet API plus wenige ausgewählte Komponenten der Java EE Spezifikation verwendet werden.
In dieser Kurzanleitung erweitere ich Tomcat um einen JTA Transaktionsmanager. Ich habe mich für die Open Source Version des Atomikos Transaktionsmanagers namens Atomikos TransactionsEssentials entschieden (Lizenzierungspflichtige Variante: Atomikos ExtremeTransactions). Mögliche Alternativen wären z.B.: JOTM oder JBoss Transactions .
Anschließend binde ich den Transaktionsmanager in Spring und OpenJPA ein.
Randbedingung: Die Webanwendung soll ohne Konfigurationsänderung auch auf einem vollwertigen Applikationsserver deployt werden und dessen JTA Implementierung nutzen können. Dies ermöglicht den aufgebohrten Tomcat als Entwicklungsplattform zu verwenden, auch wenn die Anwendung in Produktion auf einem JEE-Server betrieben wird.
Verwendete Versionen
- Tomcat 6.0.35
- Atomikos TransactionsEssentials 3.7.0
- OpenJPA 2.1.1
- Spring 3.0.7
Tomcat Konfiguration
Benötigte Bibliotheken
Folgende Bibliotheken und Ressourcen müssen aus der Atomikos Distribution in den TOMCAT_HOME/lib-Ordner kopiert werden:
- AtomikosTransactionsEssentials-3.7.0/dist
- atomikos-util.jar
- transactions.jar
- transactions-api.jar
- transactions-jta.jar
- transactions-jdbc.jar
- AtomikosTransactionsEssentials-3.7.0/lib
- geronimo-jta_1.0.1B_spec.jar
- AtomikosTransactionsEssentials-3.7.0
- transactions.properties
Tomcat Lifecycle Listener
Zum Starten und Stoppen des Transaktionsmanagers wird ein Tomcat Lifecycle Listener benötigt.
1package com.atomikos.tomcat;
2
3import org.apache.catalina.Lifecycle;
4import org.apache.catalina.LifecycleEvent;
5import org.apache.catalina.LifecycleListener;
6import org.apache.commons.logging.Log;
7import org.apache.commons.logging.LogFactory;
8import com.atomikos.icatch.jta.UserTransactionManager;
9import com.atomikos.icatch.system.Configuration;
10
11public class AtomikosLifecycleListener implements LifecycleListener {
12
13 private static Log log = LogFactory.getLog(AtomikosLifecycleListener.class);
14
15 private UserTransactionManager utm;
16
17 @Override
18 public void lifecycleEvent(LifecycleEvent event) {
19 try {
20 if (Lifecycle.START_EVENT.equals(event.getType())) {
21 if (utm == null) {
22 log.info("Starting Atomikos Transaction Manager " + Configuration.getVersion());
23 utm = new UserTransactionManager();
24 }
25 utm.init();
26 } else if (Lifecycle.AFTER_STOP_EVENT.equals(event.getType())) {
27 if (utm != null) {
28 log.info("Shutting down Atomikos Transaction Manager");
29 utm.close();
30 }
31 }
32 } catch (Exception e) {
33 log.error("Exception", e);
34 }
35 }
36}
Bitte diese Klasse kompilieren, in ein JAR verpacken und ebenfalls in den TOMCAT_HOME/lib-Ordner kopieren.
Die Klasse verwendet folgende Bibliotheken:
- transactions.jar
- transactions-jta.jar
- geronimo-jta_1.0.1B_spec.jar
- commons-logging.jar (AtomikosTransactionsEssentials-3.7.0/examples/lib)
- catalina.jar (TOMCAT_HOME/lib)
Lifecycle Listener in server.xml eintragen
In der TOMCAT_HOME/conf/server.xml muss der Tomcat Lifecycle Listener eingetragen werden.
Folgenden Block suchen:
1<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" /> 2<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
Und direkt darunter diesen Eintrag vornehmen:
1<!-- Transaction Manager Lifecycle Listener --> 2<Listener className="com.atomikos.tomcat.AtomikosLifecycleListener" />
User Transaction Factory in der context.xml eintragen
In der TOMCAT_HOME/conf/context.xml muss ein JNDI-Eintrag für die User Transaction Factory angelegt werden.
Folgenden Block suchen:
1<!-- Default set of monitored resources --> 2<WatchedResource>WEB-INF/web.xml</WatchedResource>
Und direkt darunter diesen Eintrag vornehmen:
1<!-- User Transaction Factory --> 2<Transaction factory="com.atomikos.icatch.jta.UserTransactionFactory" />
Spring Konfiguration
Der JTA Transaktionsmanager kann mit einem Einzeiler in der Spring-Konfiguration bekannt gemacht werden.
1<!-- Automatically pick the appropriate JTA platform transaction manager --> 2<tx:jta-transaction-manager />
Dieser sorgt dafür, dass Spring per JNDI das JTA UserTransaction
und TransactionManager
Objekt sucht und als Bean mit dem Namen transactionManager zur Verfügung stellt. Da wir allerdings im Tomcat nur die UserTransaction
bekannt gemacht haben, kommt es zu folgender Einschränkung: Spring kann keine Transaktionen suspendieren und somit REQUIRES_NEW
und NOT_SUPPORTED
nicht umsetzen.
Dies meldet Spring mit der Fehlermeldung:
No JTA TransactionManager found: transaction suspension not available
Falls wir mit dieser Einschränkung nicht leben können, muss der Transaktionsmanager wie folgt konfiguriert werden:
1<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.J2eeTransactionManager"/> 2<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.J2eeUserTransaction"/> 3<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 4 <property name="transactionManager" ref="atomikosTransactionManager"/> 5 <property name="userTransaction" ref="atomikosUserTransaction"/> 6</bean>
Dies hat zur Folge, dass die Spring-Konfiguration nicht mehr unserer Randbedingung entspricht, die Webanwendung ohne Konfigurationsänderung auch auf einem vollwertigen Applikationsserver ausführen zu können. Wir müssten daher in der Konfiguration zwischen Tomcat und vollwertigem Applikationsserver unterscheiden. Dazu bietet Spring den PropertyPlaceholderConfigurer
oder ab Spring 3.1 die Profile.
OpenJPA Konfiguration
Als erstes sollte sichergestellt werden, dass OpenJPA JTA verwendet. Dies regelt der transaction-type in der persistence.xml:
1<persistence-unit name="..." transaction-type="JTA">
Die Verwendung von Atomikos bringen wir OpenJPA mit Hilfe folgender System Property bei:
1-Dopenjpa.ManagedRuntime=invocation(TransactionManagerMethod=com.atomikos.icatch.jta.TransactionManagerImp.getTransactionManager)
Ohne die eingangs aufgestellte Randbedingung könnten wir dies auch in der persistence.xml erledigen:
1<persistence-unit name="..." transaction-type="JTA"> 2 <properties> 3 <property 4 name="openjpa.ManagedRuntime" 5 value="invocation(TransactionManagerMethod=com.atomikos.icatch.jta.TransactionManagerImp.getTransactionManager)" />
Weitere Beiträge
von Andreas Fritz
Dein Job bei codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
Weitere Artikel in diesem Themenbereich
Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog-Autor*in
Andreas Fritz
Senior IT Consultant / Senior IT Architect
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.