Integration eines Transaktionsmanagers in Tomcat zur Nutzung in Spring und OpenJPA

2 Kommentare

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.

package com.atomikos.tomcat;
 
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.atomikos.icatch.jta.UserTransactionManager;
import com.atomikos.icatch.system.Configuration;
 
public class AtomikosLifecycleListener implements LifecycleListener {
 
  private static Log log = LogFactory.getLog(AtomikosLifecycleListener.class);
 
  private UserTransactionManager utm;
 
  @Override
  public void lifecycleEvent(LifecycleEvent event) {
    try {
      if (Lifecycle.START_EVENT.equals(event.getType())) {
        if (utm == null) {
          log.info("Starting Atomikos Transaction Manager " + Configuration.getVersion());
          utm = new UserTransactionManager();
        }
        utm.init();
      } else if (Lifecycle.AFTER_STOP_EVENT.equals(event.getType())) {
        if (utm != null) {
          log.info("Shutting down Atomikos Transaction Manager");
          utm.close();
        }
      }
    } catch (Exception e) {
      log.error("Exception", e);
    }
  }
}

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:

<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />

Und direkt darunter diesen Eintrag vornehmen:

<!-- Transaction Manager Lifecycle Listener -->
<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:

<!-- Default set of monitored resources -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>

Und direkt darunter diesen Eintrag vornehmen:

<!-- User Transaction Factory -->
<Transaction factory="com.atomikos.icatch.jta.UserTransactionFactory" />


Spring Konfiguration

Der JTA Transaktionsmanager kann mit einem Einzeiler in der Spring-Konfiguration bekannt gemacht werden.

<!-- Automatically pick the appropriate JTA platform transaction manager -->
<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:

<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.J2eeTransactionManager"/>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.J2eeUserTransaction"/>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
  <property name="transactionManager" ref="atomikosTransactionManager"/>
  <property name="userTransaction" ref="atomikosUserTransaction"/>
</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:

<persistence-unit name="..." transaction-type="JTA">

Die Verwendung von Atomikos bringen wir OpenJPA mit Hilfe folgender System Property bei:

-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:

<persistence-unit name="..." transaction-type="JTA">
  <properties>
    <property
        name="openjpa.ManagedRuntime"
        value="invocation(TransactionManagerMethod=com.atomikos.icatch.jta.TransactionManagerImp.getTransactionManager)" />

Autor

Andreas Fritz

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

  • März 2, 2012 von Jürgen Kürsch

    Hallo,

    warum nicht gleich TomEE verwenden (http://openejb.apache.org/) ?

    Grüße
    Jürgen

    • Hallo Jürgen,

      TomEE ist eine interessante Alternative.
      Gegen TomEE sprachen unter anderem
      1. Es existiert noch keine finale Version. Für ein hausweites Ausrollen beim Kunden ist mir eine Beta 2 zu heikel – auch wenn es nur die Entwicklungsplattform ist.
      2. Der Kunde setzt WebSphere 7 ein. Daher ist Tomcat 6 die passende Wahl. TomEE / Tomcat 7 ist von den APIs zu aktuell 😉

      Viele Grüße
      Andreas

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *