Beliebte Suchanfragen
//

Integration eines Transaktionsmanagers in Tomcat zur Nutzung in Spring und OpenJPA

2.3.2012 | 3 Minuten Lesezeit

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)" />

Beitrag teilen

//

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.