[jboss-svn-commits] JBL Code SVN: r29929 - in labs/jbosstm/workspace/whitingjr/trunk/performance/src/main/java/org/jboss/jbossts: tomcat and 1 other directory.
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Tue Nov 3 13:21:16 EST 2009
Author: whitingjr
Date: 2009-11-03 13:21:16 -0500 (Tue, 03 Nov 2009)
New Revision: 29929
Added:
labs/jbosstm/workspace/whitingjr/trunk/performance/src/main/java/org/jboss/jbossts/tomcat/
labs/jbosstm/workspace/whitingjr/trunk/performance/src/main/java/org/jboss/jbossts/tomcat/TransactionalResourceFactory.java
labs/jbosstm/workspace/whitingjr/trunk/performance/src/main/java/org/jboss/jbossts/tomcat/XADataSourceWrapper.java
Log:
Pulled in classes to integrate TransactionalDriver so Connections are associtated with global transactions.
Added: labs/jbosstm/workspace/whitingjr/trunk/performance/src/main/java/org/jboss/jbossts/tomcat/TransactionalResourceFactory.java
===================================================================
--- labs/jbosstm/workspace/whitingjr/trunk/performance/src/main/java/org/jboss/jbossts/tomcat/TransactionalResourceFactory.java (rev 0)
+++ labs/jbosstm/workspace/whitingjr/trunk/performance/src/main/java/org/jboss/jbossts/tomcat/TransactionalResourceFactory.java 2009-11-03 18:21:16 UTC (rev 29929)
@@ -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/whitingjr/trunk/performance/src/main/java/org/jboss/jbossts/tomcat/XADataSourceWrapper.java
===================================================================
--- labs/jbosstm/workspace/whitingjr/trunk/performance/src/main/java/org/jboss/jbossts/tomcat/XADataSourceWrapper.java (rev 0)
+++ labs/jbosstm/workspace/whitingjr/trunk/performance/src/main/java/org/jboss/jbossts/tomcat/XADataSourceWrapper.java 2009-11-03 18:21:16 UTC (rev 29929)
@@ -0,0 +1,229 @@
+/*
+ * 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;
+ public final TransactionalDriver theTransactionalDriver = new TransactionalDriver();
+ protected 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", properties.getProperty(Context.INITIAL_CONTEXT_FACTORY));
+ //jdbcPropertyManager.propertyManager.setProperty("Context.URL_PKG_PREFIXES", properties.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 = this.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();
+ }
+
+ public TransactionalDriver getTransactionalDriver()
+ {
+ return theTransactionalDriver;
+ }
+}
More information about the jboss-svn-commits
mailing list