[jboss-svn-commits] JBL Code SVN: r19954 - in labs/jbosstm/workspace/jhalliday: tomcat-integration and 5 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Wed May 14 12:40:29 EDT 2008


Author: jhalliday
Date: 2008-05-14 12:40:29 -0400 (Wed, 14 May 2008)
New Revision: 19954

Added:
   labs/jbosstm/workspace/jhalliday/tomcat-integration/
   labs/jbosstm/workspace/jhalliday/tomcat-integration/README.txt
   labs/jbosstm/workspace/jhalliday/tomcat-integration/build.xml
   labs/jbosstm/workspace/jhalliday/tomcat-integration/context.xml
   labs/jbosstm/workspace/jhalliday/tomcat-integration/src/
   labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/
   labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/
   labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/
   labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/
   labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TomcatTxServlet.java
   labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TransactionLifecycleListener.java
   labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TransactionalResourceFactory.java
   labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/XADataSourceWrapper.java
   labs/jbosstm/workspace/jhalliday/tomcat-integration/web.xml
Log:
Added prototype for JBossTS to Tomcat 6 integration.


Added: labs/jbosstm/workspace/jhalliday/tomcat-integration/README.txt
===================================================================
--- labs/jbosstm/workspace/jhalliday/tomcat-integration/README.txt	                        (rev 0)
+++ labs/jbosstm/workspace/jhalliday/tomcat-integration/README.txt	2008-05-14 16:40:29 UTC (rev 19954)
@@ -0,0 +1,153 @@
+#
+# JBoss, Home of Professional Open Source
+# Copyright 2008, Red Hat Middleware LLC, and individual contributors
+# as indicated by the @author tags.
+# See the copyright.txt in the distribution for a
+# full listing of individual contributors.
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU Lesser General Public License, v. 2.1.
+# This program is distributed in the hope that it will be useful, but WITHOUT A
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+# You should have received a copy of the GNU Lesser General Public License,
+# v.2.1 along with this distribution; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+# (C) 2008,
+# @author JBoss Inc.
+#
+# @author Jonathan Halliday jonathan.halliday at redhat.com
+# @since 2008-05
+#
+
+This is the README file for integration of JBossTS JTA 4.3 and Tomcat 6.
+
+It outlines how to use JBossTS to provide support for XA transactions to
+web applications running inside Tomcat.
+
+Install steps:
+
+  Edit build.xml to set the locations of JBossTS and Tomcat.
+
+  Run 'ant' to build and install the transaction integration module and test app.
+
+  Run 'ant install-jbossts-to-tomcat' to copy the JBossTS code and config files to tomcat.
+
+  Edit tomcat's conf/server.xml to include the lifecycle listener for transaction service
+  startup and shutdown:
+
+  <Server ...>
+    ...
+    <Listener className="org.jboss.jbossts.tomcat.TransactionLifecycleListener"/>
+    ...
+  </Server>
+
+  Edit tomcat's conf/server.xml to include the test XADataSource, with params appropriate to your db.
+  Such Resource must not be placed directly in a webapps's context, but rather declared globally
+  and then linked to the webapp via ResourceLink.
+
+  <GlobalNamingResources>
+        ...
+        <Resource name="jdbc/TestDBGlobal" auth="Container"
+            type="javax.sql.DataSource"
+            factory="org.jboss.jbossts.tomcat.TransactionalResourceFactory"
+            XADataSourceImpl="oracle.jdbc.xa.client.OracleXADataSource"
+            xa.setUser="username"
+            xa.setPassword="password"
+            xa.setURL="jdbc:oracle:thin:@hostname:1521:SID"
+            />
+        ...
+  </GlobalNamingResources>
+
+  Now link to webapp via conf/server.xml or webapps' META-INF/context.xml
+  Also add the UserTransaction to the webapps' JNDI via a Transaction element.
+  This context setup is already done for you in the case of the demo app.
+
+  <Context ...>
+    ...
+    <Transaction factory="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple"/>
+
+    <ResourceLink name="jdbc/TestDB"
+            global="jdbc/TestDBGlobal"
+            type="javax.sql.DataSource"/>
+    ...
+  </Context>
+
+  Copy the database drivers for your datasource into tomcat's lib dir.
+
+  Invoke the test servlet to try running a transaction:
+    http://localhost:8080/jbossts-tomcat/test
+
+  Note: If you are using a db other than oracle, you may need to edit the servlet src to use an appropriate SQL dialect.
+
+
+You can now use the supplied context.xml and Servlet code as a starting point for transactions in your own apps.
+
+
+Brief explanation of the integration points:
+
+  Integration of JBossTS and Tomcat requires the following:
+
+  Startup and shutdown of the background processes of the transaction manager.
+  To achieve this we use a server lifecycle listener (TransactionLifecycleListener)
+
+  Exposing the UserTransaction implementation to the webapps.
+  That's what the <Transaction ...> element in the context.xml is for.
+
+  (Optionally, but very useful) Automatically enrolling transactional connections.
+  JDBC connections obtained from regular datasources don't support XA Transactions.
+  That rules out using the normal connection pooling JDBC Resources that tomcat supports.
+  User applications may use tomcat's Resource elements to wire up a db driver's XADataSource
+  directly to JNDI, but a) they don't always implement ObjectFactory and
+  b) they don't manage the association between connections and transactions for you.
+
+  For ease of use we want something that looks to the user app like it's a regular DataSource
+  but which is XA aware and does all the resource association etc automatically.
+  JBossTS already has the TransactionalDriver (see JBossTS JTA docs for details).
+  The TransactionalResourceFactory provides a wrapper around the TransactionalDriver,
+  allowing it to play nice with JNDI and to manage a driver's XADataSource implementation.
+  Use the properties XADataSourceImpl (for the driver's XADataSource classname) and
+  xa.someMethodName to invoke methods on that class to set properties e.g. db URL.
+  See the javadoc of TransactionalResourceFactory for details.
+  This class manages the JNDI refs in such a way that TransactionalDrivers's crash recovery
+  support will work transparently as long as the Resource is in the server's global JNDI,
+  not the webapps. Hence the use of GlobalNamingResources and ResourceLink.
+
+  Note that the XAPool project (http://xapool.experlog.com/) provides similar functionality
+  to the TransactionalResourceFactory/TransactionalDriver. It wraps a vendor's XA aware
+  driver rather than an XADataSource.  However, it's not recommended for
+  two reasons: It does not integrate with our crash recovery support, so users would have to
+  provide their own hooks. Nor does it understand that the transaction context of a thread
+  may change at any time (e.g. background timeout+rollback) so it does not have the appropriate
+  checks for this.
+
+
+But I want transactions without proper XA.
+
+  Right now db connections fall into two groups: the ones from tomcat's regular, non-transaction
+  aware datasources, on which you must call begin/commit/rollback directly though the Connection,
+  plus XA aware ones from a db driver's XADataSource via the TransactionalResourceFactory,
+  which participate in XA transaction automatically and are committed or rolled back via UserTransaction.
+
+  However, there is a third group: regular, non-XA connections that you want to commit/rollback via
+  UserTransaction. This model provides ease of use in apps that require multiple databases but don't
+  actually need the transactional guarantees that two phase commit provides.
+
+  Because the transaction manager works only in terms of XAResources, the way to achieve this is to
+  provide it with a fake XAResource implementation for each non-XA connection. The prepare method
+  calls commit on the connection, whilst the commit on the resource does nothing. This is a variation
+  on the technique used for the last resource gambit, but relaxes the transactional guarantees further.
+
+  The bad news is, we don't currently provide a DataSource wrapper that does this magic for you.
+
+
+If you don't want to integrate JBossTS with tomcat, you could also consider:
+
+  http://www.jboss.org/auth/jbossas/  JBossAS, which has tomcat, JBossTS and a JCA already integrated.
+  http://jotm.objectweb.org/current/jotm/doc/howto-tomcat-jotm.html (JOTM)
+  http://www.onjava.com/pub/a/onjava/2003/07/30/jotm_transactions.html
+  http://www.jguru.com/faq/view.jsp?EID=531070 (Tyrex)
+  http://wiki.atomikos.org/Documentation/TomcatIntegration (Atomikos)
+

Added: labs/jbosstm/workspace/jhalliday/tomcat-integration/build.xml
===================================================================
--- labs/jbosstm/workspace/jhalliday/tomcat-integration/build.xml	                        (rev 0)
+++ labs/jbosstm/workspace/jhalliday/tomcat-integration/build.xml	2008-05-14 16:40:29 UTC (rev 19954)
@@ -0,0 +1,103 @@
+<!--
+  JBoss, Home of Professional Open Source
+  Copyright 2008, Red Hat Middleware LLC, and individual contributors
+  as indicated by the @author tags.
+  See the copyright.txt in the distribution for a
+  full listing of individual contributors.
+  This copyrighted material is made available to anyone wishing to use,
+  modify, copy, or redistribute it subject to the terms and conditions
+  of the GNU Lesser General Public License, v. 2.1.
+  This program is distributed in the hope that it will be useful, but WITHOUT A
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+  PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+  You should have received a copy of the GNU Lesser General Public License,
+  v.2.1 along with this distribution; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+  MA  02110-1301, USA.
+
+  (C) 2008,
+  @author JBoss Inc.
+
+  @author Jonathan Halliday jonathan.halliday at redhat.com
+  @since 2008-05
+-->
+<project name="jbossts-tomcat" default="install" basedir=".">
+    <description>
+        integration of JBossTS and Apache Tomcat
+    </description>
+
+    <!-- set these paths for your environment: -->
+
+    <!-- existing installation dir for tomcat version 6 -->
+    <property name="tomcat.home" value="apache-tomcat-6.0.16"/>
+    <!-- existing installation dir for JBossTS JTA 4.3 -->
+    <property name="ts.home" value="/home/jhalli/IdeaProjects/jboss/jbossts_trunk/install"/>
+
+    <!-- you should not need to change anything below here -->
+
+    <path id="build">
+        <fileset dir="${tomcat.home}/lib">
+            <include name="servlet-api.jar"/>
+            <include name="jbossjta.jar"/>
+            <include name="jbossts-common.jar"/>
+            <include name="jta-1_1-classes.jar"/>
+            <include name="catalina.jar"/>
+        </fileset>
+    </path>
+
+    <property name="src" location="src"/>
+    <property name="build" location="build"/>
+
+    <target name="install-jbossts-to-tomcat">
+        <copy file="${ts.home}/lib/jbossjta.jar" todir="${tomcat.home}/lib"/>
+        <copy file="${ts.home}/lib/ext/jta-1_1-classes.zip" tofile="${tomcat.home}/lib/jta-1_1-classes.jar"/>
+        <copy file="${ts.home}/lib/ext/jbossts-common.jar" todir="${tomcat.home}/lib"/>
+        <copy file="${ts.home}/lib/ext/concurrent.jar" todir="${tomcat.home}/lib"/>
+        <copy file="${ts.home}/lib/ext/commons-logging-1.1.jar" todir="${tomcat.home}/lib"/>
+        <copy file="${ts.home}/lib/ext/log4j-1.2.8.jar" todir="${tomcat.home}/lib"/>
+
+        <copy file="${ts.home}/etc/jbossjta-properties.xml" todir="${tomcat.home}/lib"/>
+        <copy file="${ts.home}/etc/log4j.properties" todir="${tomcat.home}/lib"/>
+    </target>
+
+    <target name="init">
+        <tstamp/>
+        <mkdir dir="${build}"/>
+    </target>
+
+    <target name="compile" depends="init"
+            description="compile the source ">
+        <javac srcdir="${src}" destdir="${build}" classpathref="build" debug="true"/>
+    </target>
+
+    <target name="install" depends="compile"
+            description="generate and install the distribution">
+
+        <!-- bundle and install the integration code -->
+        <jar basedir="${build}" jarfile="${tomcat.home}/lib/jbossts-tomcat.jar">
+            <exclude name="**/TomcatTxServlet.class"/>
+        </jar>
+
+        <!-- now install a simple demo/test webapp with one servlet: -->
+
+        <mkdir dir="${tomcat.home}/webapps/jbossts-tomcat"/>
+        <mkdir dir="${tomcat.home}/webapps/jbossts-tomcat/META-INF"/>
+        <copy todir="${tomcat.home}/webapps/jbossts-tomcat/META-INF" file="context.xml"/>
+
+        <mkdir dir="${tomcat.home}/webapps/jbossts-tomcat/WEB-INF"/>
+        <mkdir dir="${tomcat.home}/webapps/jbossts-tomcat/WEB-INF/classes"/>
+        <copy todir="${tomcat.home}/webapps/jbossts-tomcat/WEB-INF/classes">
+            <fileset dir="${build}">
+                <include name="**/TomcatTxServlet.class"/>
+            </fileset>
+        </copy>
+        <copy todir="${tomcat.home}/webapps/jbossts-tomcat/WEB-INF" file="web.xml"/>
+
+    </target>
+
+    <target name="clean"
+            description="clean up">
+        <delete dir="${build}"/>
+    </target>
+
+</project>
\ No newline at end of file

Added: labs/jbosstm/workspace/jhalliday/tomcat-integration/context.xml
===================================================================
--- labs/jbosstm/workspace/jhalliday/tomcat-integration/context.xml	                        (rev 0)
+++ labs/jbosstm/workspace/jhalliday/tomcat-integration/context.xml	2008-05-14 16:40:29 UTC (rev 19954)
@@ -0,0 +1,32 @@
+<!--
+  JBoss, Home of Professional Open Source
+  Copyright 2008, Red Hat Middleware LLC, and individual contributors
+  as indicated by the @author tags.
+  See the copyright.txt in the distribution for a
+  full listing of individual contributors.
+  This copyrighted material is made available to anyone wishing to use,
+  modify, copy, or redistribute it subject to the terms and conditions
+  of the GNU Lesser General Public License, v. 2.1.
+  This program is distributed in the hope that it will be useful, but WITHOUT A
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+  PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+  You should have received a copy of the GNU Lesser General Public License,
+  v.2.1 along with this distribution; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+  MA  02110-1301, USA.
+
+  (C) 2008,
+  @author JBoss Inc.
+
+  @author Jonathan Halliday jonathan.halliday at redhat.com
+  @since 2008-05
+-->
+<Context>
+
+    <Transaction factory="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple"/>
+
+    <ResourceLink name="jdbc/TestDB"
+                  global="jdbc/TestDBGlobal"
+                  type="javax.sql.DataSource"/>
+
+</Context>
\ No newline at end of file

Added: labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TomcatTxServlet.java
===================================================================
--- labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TomcatTxServlet.java	                        (rev 0)
+++ labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TomcatTxServlet.java	2008-05-14 16:40:29 UTC (rev 19954)
@@ -0,0 +1,97 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author JBoss Inc.
+ */
+
+package org.jboss.jbossts.tomcat;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.ServletException;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.transaction.UserTransaction;
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.Statement;
+import java.sql.ResultSet;
+
+/**
+ * Servlet that runs a basic transaction to test the JBossTS/Tomcat integration setup
+ * and act as an example of how to use it.
+ *
+ * @author Jonathan Halliday jonathan.halliday at redhat.com
+ * @version $Id$
+ * @since 2008-05
+ */
+public class TomcatTxServlet extends HttpServlet
+{
+    public void init()
+            throws ServletException
+    {
+    }
+
+    protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException
+    {
+        httpServletResponse.setContentType("text/html");
+        PrintWriter out = httpServletResponse.getWriter();
+
+        try {
+
+            // lookup the UserTransaction object that is used to manage transaction boundaries
+            // This has been placed in JNDI by the <Transaction ...> element in the webapps's Context.
+            Context initCtx = new InitialContext();
+            UserTransaction userTransaction = (UserTransaction) initCtx.lookup("java:comp/UserTransaction");
+
+            // lookup a transaction aware datasource. This gets into JNDI via a
+            // Resource element in the server's GlobalNamingResources
+            // plus a ResourceLink in the webapps's Context.
+            Context envCtx = (Context) initCtx.lookup("java:comp/env");
+            DataSource ds = (DataSource) envCtx.lookup("jdbc/TestDB");
+
+            userTransaction.begin();
+
+            // This looks like a regular connection but is transparently associated
+            // to the transaction by the datasource wrapper magic.
+            Connection conn = ds.getConnection();
+            Statement stmt = conn.createStatement();
+
+            // change this SQL statement for dbs other than oracle.
+            // For a harder test, try something that writes a table in the db
+            // (but remember to create the table outside the transaction first!)
+            ResultSet rs = stmt.executeQuery("SELECT sysdate() FROM dual");
+
+            rs.next();
+            out.print("<br/>Result (should be the current time): "+rs.getString(1));
+            rs.close();
+            stmt.close();
+            conn.close();
+
+            userTransaction.commit();
+
+            out.close();
+
+        } catch(Exception e) {
+            throw new ServletException(e);
+        }
+    }
+}
\ No newline at end of file

Added: labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TransactionLifecycleListener.java
===================================================================
--- labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TransactionLifecycleListener.java	                        (rev 0)
+++ labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TransactionLifecycleListener.java	2008-05-14 16:40:29 UTC (rev 19954)
@@ -0,0 +1,78 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author JBoss Inc.
+ */
+
+package org.jboss.jbossts.tomcat;
+
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.LifecycleEvent;
+import com.arjuna.ats.arjuna.coordinator.TransactionReaper;
+import com.arjuna.ats.arjuna.recovery.RecoveryManager;
+import com.arjuna.ats.jdbc.common.jdbcPropertyManager;
+
+import javax.naming.Context;
+
+/**
+ * Hooks JBossTS initialization into the Tomcat 6 server lifecycle.
+ * <p/>
+ * At first glance it looks like you can just use
+ * <Transaction factory="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple"/>
+ * to configure JBossTS in Tomcat 6. However, there are a couple of issues with this simplistic approach:
+ * The <Transaction...> element is valid only in a webapps <Context...>, not in the
+ * server's <GlobalNamingResources ...>, wehreas the tx service should be installed globally
+ * Second, we need to start recovery after the XADataSources are available (see TransactionalResourceFactory)
+ * but not have it depend on any webapps starting. Hence we need to tie in to the server lifecycle explicitly.
+ * <p/>
+ * Reference this class from tomcat's server.xml, in the <Server ... element:
+ * <Listener className="org.jboss.jbossts.tomcat.TransactionLifecycleListener"/>
+ *
+ * @author Jonathan Halliday jonathan.halliday at redhat.com
+ * @version $Id$
+ * @see http://tomcat.apache.org/tomcat-6.0-doc/config/
+ * @since 2008-05
+ */
+public class TransactionLifecycleListener implements LifecycleListener
+{
+    RecoveryManager recoveryManager;
+
+    public void lifecycleEvent(LifecycleEvent event)
+    {
+        if ("start".equals(event.getType()))
+        {
+            // run the reaper here so it is in the server's classloader context
+            // if we started it lazily on first tx it would run with context of the webapp using the tx.
+            TransactionReaper.create();
+
+            // recovery needs the correct JNDI settings.
+            // XADataSourceWrapper sets these too as a precaution, but we may run first.
+            jdbcPropertyManager.propertyManager.setProperty("Context.INITIAL_CONTEXT_FACTORY", System.getProperty(Context.INITIAL_CONTEXT_FACTORY));
+            jdbcPropertyManager.propertyManager.setProperty("Context.URL_PKG_PREFIXES", System.getProperty(Context.URL_PKG_PREFIXES));
+
+            // a 'start' occurs after the Resources in GlobalNamingResources are instantiated,
+            // so we can safely start the recovery Thread here.
+            recoveryManager = RecoveryManager.manager();
+            recoveryManager.startRecoveryManagerThread();
+        }
+        else if ("stop".equals(event.getType()))
+        {
+            recoveryManager.stop();
+        }
+    }
+}
\ No newline at end of file

Added: labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TransactionalResourceFactory.java
===================================================================
--- labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TransactionalResourceFactory.java	                        (rev 0)
+++ labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/TransactionalResourceFactory.java	2008-05-14 16:40:29 UTC (rev 19954)
@@ -0,0 +1,221 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author JBoss Inc.
+ */
+
+package org.jboss.jbossts.tomcat;
+
+import javax.naming.spi.ObjectFactory;
+
+import java.util.Hashtable;
+import java.util.*;
+import java.lang.reflect.Method;
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.RefAddr;
+import javax.sql.XADataSource;
+
+
+/**
+ * TransactionalResourceFactory instances are the integration point to Tomcat 6.
+ * By configuring a Resource using this as the factory, we can expose a
+ * db driver's native XADataSource to tomcat via the TransactionalDriver, thereby
+ * ensuring connections are enlisted in the transaction correctly and that
+ * recovery works.
+ * <p/>
+ * Usually wired up in Tomcat 6 via an entry
+ * in the <GlobalNamingResources> section of conf/server.xml
+ * Don't put it in a webapps local META-INF/context.xml directly, it needs the
+ * server's classloader and global JNDI for recovery to work.
+ * <p/>
+ * <Resource name="jdbc/TestDBGlobal" auth="Container"
+ * type="javax.sql.DataSource"
+ * factory="org.jboss.jbossts.tomcat.TransactionalResourceFactory"
+ * XADataSourceImpl="oracle.jdbc.xa.client.OracleXADataSource"
+ * xa.setUser="username"
+ * xa.setPassword="password"
+ * xa.setURL="jdbc:oracle:thin:@hostname:1521:SID"
+ * />
+ *
+ * @author Jonathan Halliday jonathan.halliday at redhat.com
+ * @version $Id$
+ * @see http://tomcat.apache.org/tomcat-6.0-doc/config/globalresources.html
+ * @since 2008-05
+ */
+public class TransactionalResourceFactory implements ObjectFactory
+{
+    /*
+        Implementation Note: many methods here do kludgy things to integrate with tomcat.
+        They are protected so that subclasses can override to use alternative kludges if desired.
+     */
+
+
+    /**
+     * Create a new XADataSource instance.
+     *
+     * @param obj The reference object describing the DataSource configuration
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment) throws Exception
+    {
+        Reference ref = (Reference) obj;
+        Enumeration addrs = ref.getAll();
+        HashMap<String, String> params = new HashMap();
+
+        System.out.println("TransactionalResourceFactory ");
+
+        // convert the Addresses into easier to handle String key/values
+        while (addrs.hasMoreElements())
+        {
+            RefAddr addr = (RefAddr) addrs.nextElement();
+            String attrName = addr.getType();
+            String attrValue = (String) addr.getContent();
+            params.put(attrName, attrValue);
+        }
+
+        String classname = params.get("XADataSourceImpl");
+        if (classname == null)
+        {
+            throw new NamingException("No XADataSourceImpl value specified");
+        }
+
+        // instantiate the underlying implementation and configure it with the supplied params
+        XADataSource xaDataSource = loadXADataSource(classname);
+        processParameters(xaDataSource, params);
+
+        // instantiate and return a wrapper that will intercept subsequent calls to the object.
+        String fullJNDIName = convertName(name.toString());
+        return new XADataSourceWrapper(fullJNDIName, xaDataSource);
+    }
+
+    /**
+     * The name we are provided with by tomcat is the last part of the value in the xml attribute
+     * i.e if name="jdbc/TestDB" in conf/server.xml then name here = "TestDB".
+     * What we need to pass down to the arjuna code is a fully qualified name, so we prefix it here.
+     * This will break if the actual prefix in the xml is something other than "java:/jdbc/".
+     * TODO: figure out if we can grab the fully qualified name from tomcat somehow.
+     *
+     * @param name
+     * @return
+     */
+    protected String convertName(String name)
+    {
+        return "java:/jdbc/" + name;
+    }
+
+    /**
+     * Take an unconfigured XADataSource and configure it
+     * according to the supplied parameters.
+     * <p/>
+     * Since each vendor's XADataSource implementation has a different API,
+     * this is done via. reflection in the following manner:
+     * Parameters with names beginning "xa." have this prefix stripped off their name,
+     * the remainder of which is regarded as a method name on the XADataSource.
+     * The value is passed though to the method call unmodified.
+     * e.g. "xa.setUsername"=>"myName"  maps to xaDataSource.setUsername("myName");
+     *
+     * @param xaDataSource
+     * @param params
+     * @throws NamingException
+     */
+    protected void processParameters(XADataSource xaDataSource, Map<String, String> params) throws NamingException
+    {
+        for (String key : params.keySet())
+        {
+            if ("factory".equals(key) || "scope".equals(key) || "auth".equals(key) || "XADataSourceImpl".equals(key))
+            {
+                continue;
+            }
+
+            if (!key.startsWith("xa."))
+            {
+                // TODO log warning of unknown param?
+                continue;
+            }
+
+            callMethod(xaDataSource, key.substring(3), params.get(key));
+        }
+    }
+
+    /**
+     * Use reflection to call the named method on an xaDataSource implementation.
+     * Note: subclass and override this if you need handling of non-String values.
+     *
+     * @param xaDataSource
+     * @param name
+     * @param value
+     * @throws NamingException
+     */
+    protected void callMethod(XADataSource xaDataSource, String name, String value) throws NamingException
+    {
+        try
+        {
+            Method method = xaDataSource.getClass().getMethod(name, new Class[]{java.lang.String.class});
+            method.invoke(xaDataSource, value);
+        }
+        catch (Exception e)
+        {
+            NamingException ex = new NamingException("Unable to invoke " + name + " on " + xaDataSource.getClass().getName());
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+
+    /**
+     * Load and instantiate the given XADataSource implementation class.
+     *
+     * @param classname
+     * @return
+     * @throws NamingException
+     */
+    protected XADataSource loadXADataSource(String classname) throws NamingException
+    {
+
+        Class clazz = null;
+
+        try
+        {
+            clazz = Thread.currentThread().getContextClassLoader().loadClass(classname);
+        }
+        catch (Exception e)
+        {
+            NamingException ex = new NamingException("Unable to load " + classname);
+            ex.initCause(e);
+            throw ex;
+        }
+
+        XADataSource xaDataSource = null;
+
+        try
+        {
+            xaDataSource = (XADataSource) clazz.newInstance();
+        }
+        catch (Exception e)
+        {
+            NamingException ex = new NamingException("Unable to instantiate " + classname);
+            ex.initCause(e);
+            throw ex;
+        }
+
+        return xaDataSource;
+    }
+}
+

Added: labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/XADataSourceWrapper.java
===================================================================
--- labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/XADataSourceWrapper.java	                        (rev 0)
+++ labs/jbosstm/workspace/jhalliday/tomcat-integration/src/org/jboss/jbossts/tomcat/XADataSourceWrapper.java	2008-05-14 16:40:29 UTC (rev 19954)
@@ -0,0 +1,224 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2008,
+ * @author JBoss Inc.
+ */
+
+package org.jboss.jbossts.tomcat;
+
+import javax.sql.XADataSource;
+import javax.sql.XAConnection;
+import javax.sql.DataSource;
+import javax.naming.Context;
+import java.sql.SQLException;
+import java.sql.Connection;
+import java.io.PrintWriter;
+import java.util.Properties;
+
+import com.arjuna.ats.jdbc.TransactionalDriver;
+import com.arjuna.ats.jdbc.common.jdbcPropertyManager;
+import com.arjuna.common.util.propertyservice.PropertyManager;
+
+
+/**
+ * This class provides a JNDI DataSource based approach to
+ * management of transaction aware database connections.
+ * Its got some vaguely tomcat6 specific JNDI weirdness in it,
+ * so don't expect it to work in other environments.
+ * <p/>
+ * This class serves a dual purpose. To web applications that look it up via
+ * their comp/env JNDI, it's a regular DataSource on which they can call getConnection().
+ * To JBossTS TransactionalDriver and recovery code that looks it up via
+ * the tomcat server's global JNDI context,
+ * it's a XADataSource from which they can obtain a XAResource.
+ * Hence it implements both DataSource and XADataSource.
+ *
+ * @author Jonathan Halliday jonathan.halliday at redhat.com
+ * @version $Id$
+ * @see http://tomcat.apache.org/tomcat-6.0-doc/config/globalresources.html
+ * @since 2008-05
+ */
+public class XADataSourceWrapper implements XADataSource, DataSource
+{
+    private XADataSource _theXADataSource;
+    private final TransactionalDriver _theTransactionalDriver = new TransactionalDriver();
+    private String _name;
+
+    /**
+     * Create a wrapper around the provided XADataSource implementation,
+     * which should be registered in tomcat's global JNDI with the specified name.
+     * Note: the registration is not done here, it's someone elses problem.
+     * See TransactionalResourceFactory for example usage.
+     *
+     * @param name          should be the fully qualifed JNDI name of the XADataSource, in
+     *                      tomcat's global JNDI, not a webapp specific JNDI context.
+     * @param theDataSource
+     */
+    public XADataSourceWrapper(String name, XADataSource theDataSource)
+    {
+        _theXADataSource = theDataSource;
+        _name = name;
+    }
+
+    /**
+     * Obtain a direct reference to the wrapped object. This is not
+     * recommended but may be necessary to e.g. call vendor specific methods.
+     *
+     * @return
+     */
+    public XADataSource getUnwrappedXADataSource()
+    {
+        return _theXADataSource;
+    }
+
+    ///////////////////////
+
+    // Implementation of the DataSource API is done by reusing the arjuna
+    // TransactionalDriver. Its already got all the smarts for checking tx
+    // context, enlisting resources etc so we just delegate to it.
+    // All we need is some fudging to make the JNDI name stuff behave.
+
+    /**
+     * Obtain a connection to the database.
+     * Note: Pooling behaviour depends on the vendor's underlying XADataSource implementation.
+     *
+     * @return
+     * @throws SQLException
+     */
+    public Connection getConnection() throws SQLException
+    {
+        String url = TransactionalDriver.arjunaDriver + _name;
+        // although we are not setting any properties, the driver will barf if we pass 'null'.
+        Properties properties = new Properties();
+        return getTransactionalConnection(url, properties);
+    }
+
+    /**
+     * Obtain a connection to the database using the supplied authentication credentials.
+     *
+     * @param username
+     * @param password
+     * @return
+     * @throws SQLException
+     */
+    public Connection getConnection(String username, String password) throws SQLException
+    {
+        String url = TransactionalDriver.arjunaDriver + _name;
+        Properties properties = new Properties();
+        properties.setProperty(TransactionalDriver.userName, username);
+        properties.setProperty(TransactionalDriver.password, password);
+        return getTransactionalConnection(url, properties);
+    }
+
+    /*
+     * This is where most of the tomcat specific weirdness resides. You probably
+     * want to subclass and override this method for reuse in env other than tomcat.
+     */
+    protected Connection getTransactionalConnection(String url, Properties properties) throws SQLException
+    {
+
+        // For ref, the url the TransactionalDriver expects is the arjuna driver's
+        // special prefix followed by a JNDI name.
+        // via ConnectionImple the IndirectRecoverableConnection.createDataSource method
+        // attempts to look it up in JNDI. There are two problems with this:
+
+        //  First problem,
+        // it always calls InitialContext(env), never InitalContext().
+        // This we work around by copying into the arjuna config, the system
+        // properties it needs to populate the env:
+
+        // caution: ensure the tx lifecycle listener is configured in tomcat or there will be a
+        // possible race here, as recovery needs these properties too and may start first
+        jdbcPropertyManager.propertyManager.setProperty("Context.INITIAL_CONTEXT_FACTORY", System.getProperty(Context.INITIAL_CONTEXT_FACTORY));
+        jdbcPropertyManager.propertyManager.setProperty("Context.URL_PKG_PREFIXES", System.getProperty(Context.URL_PKG_PREFIXES));
+
+        // Second problem: this method has almost certainly been called by a webapp,
+        // which has its own InitialContext. Whilst the datasource is in there, we
+        // can't be certain it's under the same name as its global name. We also
+        // don't want any hassle with the webapp classloader, which may go away
+        // whilst recovery is still active. Hence we need to temporarily set things
+        // such that we use the server's global InitialContext for the lookup
+        // instead of the webapp one. Tomcat figures out the InitialContext based
+        // on classloader, so we fool it by changing the Thread context from the
+        // webapps classloader to its parent (the server's classloader):
+        ClassLoader webappClassLoader = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(webappClassLoader.getParent());
+        Connection connection;
+        try
+        {
+            connection = _theTransactionalDriver.connect(url, properties);
+        }
+        finally
+        {
+            Thread.currentThread().setContextClassLoader(webappClassLoader);
+        }
+
+        return connection;
+    }
+
+    ///////////////////////
+
+    // Implementation of XADataSource API is just a straightforward wrap/delegate.
+    // Note that some of these methods also appear in the DataSource API.
+    // We don't really care, it's the underlying implementations problem
+    // to disambiguate them if required.
+
+    public boolean isWrapperFor(Class<?> iface) throws SQLException
+    {
+        return iface.isAssignableFrom(XADataSource.class);
+    }
+
+    public <T> T unwrap(Class<T> iface) throws SQLException
+    {
+        if(isWrapperFor(iface)) {
+            return (T)getUnwrappedXADataSource();
+        } else {
+            throw new SQLException("Not a wrapper for "+iface.getCanonicalName());
+        }
+    }
+
+    public XAConnection getXAConnection() throws SQLException
+    {
+        return _theXADataSource.getXAConnection();
+    }
+
+    public XAConnection getXAConnection(String user, String password) throws SQLException
+    {
+        return _theXADataSource.getXAConnection(user, password);
+    }
+
+    public PrintWriter getLogWriter() throws SQLException
+    {
+        return _theXADataSource.getLogWriter();
+    }
+
+    public void setLogWriter(PrintWriter out) throws SQLException
+    {
+        _theXADataSource.setLogWriter(out);
+    }
+
+    public void setLoginTimeout(int seconds) throws SQLException
+    {
+        _theXADataSource.setLoginTimeout(seconds);
+    }
+
+    public int getLoginTimeout() throws SQLException
+    {
+        return _theXADataSource.getLoginTimeout();
+    }
+}

Added: labs/jbosstm/workspace/jhalliday/tomcat-integration/web.xml
===================================================================
--- labs/jbosstm/workspace/jhalliday/tomcat-integration/web.xml	                        (rev 0)
+++ labs/jbosstm/workspace/jhalliday/tomcat-integration/web.xml	2008-05-14 16:40:29 UTC (rev 19954)
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  JBoss, Home of Professional Open Source
+  Copyright 2008, Red Hat Middleware LLC, and individual contributors
+  as indicated by the @author tags.
+  See the copyright.txt in the distribution for a
+  full listing of individual contributors.
+  This copyrighted material is made available to anyone wishing to use,
+  modify, copy, or redistribute it subject to the terms and conditions
+  of the GNU Lesser General Public License, v. 2.1.
+  This program is distributed in the hope that it will be useful, but WITHOUT A
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+  PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+  You should have received a copy of the GNU Lesser General Public License,
+  v.2.1 along with this distribution; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+  MA  02110-1301, USA.
+
+  (C) 2008,
+  @author JBoss Inc.
+
+  @author Jonathan Halliday jonathan.halliday at redhat.com
+  @since 2008-05
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+   version="2.5">
+
+    <description>
+      JBossTS/Tomcat Integration Demo/Test.
+    </description>
+    <display-name>JBossTS/Tomcat Integration Demo/Test</display-name>
+
+    <servlet>
+      <servlet-name>TomcatTxServlet</servlet-name>
+      <servlet-class>org.jboss.jbossts.tomcat.TomcatTxServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>TomcatTxServlet</servlet-name>
+        <url-pattern>/test</url-pattern>
+    </servlet-mapping>
+
+</web-app>
\ No newline at end of file




More information about the jboss-svn-commits mailing list