[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