Add in DataSource supprot
authorfhanik <fhanik@13f79535-47bb-0310-9956-ffa450edef68>
Fri, 20 Nov 2009 21:39:36 +0000 (21:39 +0000)
committerfhanik <fhanik@13f79535-47bb-0310-9956-ffa450edef68>
Fri, 20 Nov 2009 21:39:36 +0000 (21:39 +0000)
Add in XA support

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@882723 13f79535-47bb-0310-9956-ffa450edef68

modules/jdbc-pool/.classpath
modules/jdbc-pool/doc/jdbc-pool.xml
modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java
modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java
modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java

index ef54a92..575cb2b 100644 (file)
@@ -7,5 +7,6 @@
        <classpathentry kind="var" path="TOMCAT_LIBS_BASE/tomcat6-deps/dbcp/tomcat-dbcp.jar" sourcepath="/TOMCAT_LIBS_BASE/tomcat6-deps/dbcp/src/java"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
        <classpathentry kind="lib" path="/development/tomcat/trunk/trunk/modules/jdbc-pool/includes/h2/bin/h2-1.1.115.jar"/>
+       <classpathentry kind="lib" path="/development/tomcat/trunk/trunk/modules/jdbc-pool/includes/mysql-connector-java-5.1.7-bin.jar"/>
        <classpathentry kind="output" path="bin"/>
 </classpath>
index 83e99b7..325d8f3 100644 (file)
@@ -88,6 +88,9 @@
       <li>Get JMX notifications and log entries when connections are suspected for being abandoned. This is similar to
           the <code>removeAbandonedTimeout</code> but it doesn't take any action, only reports the information. 
           This is achieved using the <code>suspectTimeout</code> attribute.</li> 
+      <li>Connections can be retrieved from a <code>java.sql.Driver</code> or a <code>javax.sql.DataSource</code>
+          This is achieved using the <code>dataSource</code> and <code>dataSourceJNDI</code> attributes.</li>
+      <li>XA connection support</li>
     </ol>
   </p>
 
            logged and a JMX notification gets sent once. 
       </p>
     </attribute>
+    <attribute name="dataSource" required="false">
+      <p>(javax.sql.DataSource)  
+      </p>
+    </attribute>
+    <attribute name="dataSourceJNDI" required="false">
+      <p>(String) 
+      </p>
+    </attribute>
   </attributes>  
   </subsection>
 </section>
index f44c0a5..37d0976 100644 (file)
@@ -277,7 +277,7 @@ public class ConnectionPool {
         }
 
         try {
-            getProxyConstructor(con.getConnection() instanceof XAConnection);
+            getProxyConstructor(con.getXAConnection() != null);
             //create the proxy
             //TODO possible optimization, keep track if this connection was returned properly, and don't generate a new facade
             Connection connection = (Connection)proxyClassConstructor.newInstance(new Object[] { handler });
index fbfc005..60066d3 100644 (file)
@@ -104,6 +104,10 @@ public class DataSourceFactory implements ObjectFactory {
     protected static final String PROP_USE_EQUALS = "useEquals";
     protected static final String PROP_USE_CON_LOCK = "useLock";
     
+    protected static final String PROP_DATASOURCE= "dataSource";
+    protected static final String PROP_DATASOURCE_JNDI = "dataSourceJNDI";
+    
+    
     public static final int UNKNOWN_TRANSACTIONISOLATION = -1;
     
     public static final String OBJECT_NAME = "object_name";
@@ -147,7 +151,9 @@ public class DataSourceFactory implements ObjectFactory {
         OBJECT_NAME,
         PROP_ABANDONWHENPERCENTAGEFULL,
         PROP_MAXAGE,
-        PROP_USE_CON_LOCK
+        PROP_USE_CON_LOCK,
+        PROP_DATASOURCE,
+        PROP_DATASOURCE_JNDI,
     };
 
     // -------------------------------------------------- ObjectFactory Methods
@@ -425,6 +431,16 @@ public class DataSourceFactory implements ObjectFactory {
             poolProperties.setUseLock(Boolean.parseBoolean(value));
         }
         
+        value = properties.getProperty(PROP_DATASOURCE);
+        if (value != null) {
+            //this should never happen
+            log.error("Can't set dataSource property as a string, this must be a javax.sql.DataSource object.");
+        }
+        
+        value = properties.getProperty(PROP_DATASOURCE_JNDI);
+        if (value != null) {
+            poolProperties.setDataSourceJNDI(value);
+        }
         return poolProperties;
     }
 
index 1db507d..91f6b0d 100644 (file)
@@ -1011,4 +1011,33 @@ public class DataSourceProxy implements PoolConfiguration {
         getPoolProperties().setName(name);
     }
     
+    /** 
+     * {@inheritDoc}
+     */
+    public void setDataSource(javax.sql.DataSource ds) {
+        getPoolProperties().setDataSource(ds);
+    }
+    
+    /** 
+     * {@inheritDoc}
+     */
+    public javax.sql.DataSource getDataSource() {
+        return getPoolProperties().getDataSource();
+    }
+    
+    
+    /** 
+     * {@inheritDoc}
+     */
+    public void setDataSourceJNDI(String jndiDS) {
+        getPoolProperties().setDataSourceJNDI(jndiDS);
+    }
+    
+    /** 
+     * {@inheritDoc}
+     */
+    public String getDataSourceJNDI() {
+        return getPoolProperties().getDataSourceJNDI();
+    }
+    
 }
index d325323..0368a6c 100644 (file)
@@ -717,5 +717,39 @@ public interface PoolConfiguration {
      */
     public int getSuspectTimeout();
     
+    /**
+     * Injects a datasource that will be used to retrieve/create connections.
+     * If a data source is set, the {@link PoolConfiguration#getUrl()} and {@link PoolConfiguration#getDriverClassName()} methods are ignored
+     * and not used by the pool. If the {@link PoolConfiguration#getUsername()} and {@link PoolConfiguration#getPassword()}
+     * values are set, the method {@link javax.sql.DataSource#getConnection(String, String)} method will be called instead of the
+     * {@link javax.sql.DataSource#getConnection()} method.
+     * If the data source implements {@link javax.sql.XADataSource} the methods 
+     * {@link javax.sql.XADataSource#getXAConnection()} and {@link javax.sql.XADataSource#getXAConnection(String,String)}
+     * will be invoked.  
+     * @param ds the {@link javax.sql.DataSource} to be used for creating connections to be pooled.
+     */
+    public void setDataSource(javax.sql.DataSource ds);
+    
+    /**
+     * Returns a datasource, if one exists that is being used to create connections.
+     * This method will return null if the pool is using a {@link java.sql.Driver}
+     * @return the {@link javax.sql.DataSource} to be used for creating connections to be pooled or null if a Driver is used.
+     */
+    public javax.sql.DataSource getDataSource();
+    
+    /**
+     * Configure the connection pool to use a DataSource according to {@link PoolConfiguration#setDataSource(javax.sql.DataSource)}
+     * But instead of injecting the object, specify the JNDI location.
+     * After a successful JNDI look, the {@link PoolConfiguration#getDataSource()} will not return null. 
+     * @param jndiDS -the JNDI string @TODO specify the rules here.
+     */
+    public void setDataSourceJNDI(String jndiDS);
+    
+    /**
+     * Returns the JNDI string configured for data source usage.
+     * @return
+     */
+    public String getDataSourceJNDI();
+    
 
 }
\ No newline at end of file
index 62b31c5..3b6c86b 100644 (file)
@@ -73,8 +73,10 @@ public class PoolProperties implements PoolConfiguration {
     protected int abandonWhenPercentageFull = 0;
     protected long maxAge = 0;
     protected boolean useLock = false;
-    private InterceptorDefinition[] interceptors = null;
+    protected InterceptorDefinition[] interceptors = null;
     protected int suspectTimeout = 0;
+    protected javax.sql.DataSource dataSource = null;
+    protected String dataSourceJNDI = null;
     
     
     /** 
@@ -870,6 +872,36 @@ public class PoolProperties implements PoolConfiguration {
         this.useLock = useLock;
     }
     
+    
+    /** 
+     * {@inheritDoc}
+     */
+    public void setDataSource(javax.sql.DataSource ds) {
+        this.dataSource = ds;
+    }
+    
+    /** 
+     * {@inheritDoc}
+     */
+    public javax.sql.DataSource getDataSource() {
+        return dataSource;
+    }
+    
+    
+    /** 
+     * {@inheritDoc}
+     */
+    public void setDataSourceJNDI(String jndiDS) {
+        this.dataSourceJNDI = jndiDS;
+    }
+    
+    /** 
+     * {@inheritDoc}
+     */
+    public String getDataSourceJNDI() {
+        return this.dataSourceJNDI;
+    }
+    
         
     public static Properties getProperties(String propText, Properties props) {
         if (props==null) props = new Properties();
index eff788d..c548a48 100644 (file)
@@ -69,6 +69,11 @@ public class PooledConnection {
      * The underlying database connection
      */
     private volatile java.sql.Connection connection;
+    
+    /**
+     * If using a XAConnection underneath.
+     */
+    private volatile javax.sql.XAConnection xaConnection;
     /**
      * When we track abandon traces, this string holds the thread dump
      */
@@ -142,6 +147,47 @@ public class PooledConnection {
                 log.debug("Unable to disconnect previous connection.", x);
             } //catch
         } //end if
+        if (poolProperties.getDataSource()==null && poolProperties.getDataSourceJNDI()!=null) {
+            //TODO lookup JNDI name
+        }
+        
+        if (poolProperties.getDataSource()!=null) {
+            connectUsingDataSource();
+        } else {
+            connectUsingDriver();
+        }
+        
+        //set up the default state, unless we expect the interceptor to do it
+        if (poolProperties.getJdbcInterceptors()==null || poolProperties.getJdbcInterceptors().indexOf(ConnectionState.class.getName())<0) {
+            if (poolProperties.getDefaultReadOnly()!=null) connection.setReadOnly(poolProperties.getDefaultReadOnly().booleanValue());
+            if (poolProperties.getDefaultAutoCommit()!=null) connection.setAutoCommit(poolProperties.getDefaultAutoCommit().booleanValue());
+            if (poolProperties.getDefaultCatalog()!=null) connection.setCatalog(poolProperties.getDefaultCatalog());
+            if (poolProperties.getDefaultTransactionIsolation()!=DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION) connection.setTransactionIsolation(poolProperties.getDefaultTransactionIsolation());
+        }        
+        this.discarded = false;
+        this.lastConnected = System.currentTimeMillis();
+    }
+    
+    protected void connectUsingDataSource() throws SQLException {
+        if (poolProperties.getDataSource() instanceof javax.sql.XADataSource) {
+            javax.sql.XADataSource xds = (javax.sql.XADataSource)poolProperties.getDataSource();
+            if (poolProperties.getUsername()!=null && poolProperties.getPassword()!=null) {
+                xaConnection = xds.getXAConnection(poolProperties.getUsername(), poolProperties.getPassword());
+                connection = xaConnection.getConnection();
+            } else {
+                xaConnection = xds.getXAConnection();
+                connection = xaConnection.getConnection();
+            }
+        } else {
+            javax.sql.DataSource ds = poolProperties.getDataSource();
+            if (poolProperties.getUsername()!=null && poolProperties.getPassword()!=null) {
+                connection = ds.getConnection(poolProperties.getUsername(), poolProperties.getPassword());
+            } else {
+                connection = ds.getConnection();
+            }
+        }
+    }
+    protected void connectUsingDriver() throws SQLException {
         java.sql.Driver driver = null;
         try {
             driver = (java.sql.Driver) Class.forName(poolProperties.getDriverClassName(),
@@ -180,16 +226,6 @@ public class PooledConnection {
         if (connection==null) {
             throw new SQLException("Driver:"+driver+" returned null for URL:"+driverURL);
         }
-        
-        //set up the default state, unless we expect the interceptor to do it
-        if (poolProperties.getJdbcInterceptors()==null || poolProperties.getJdbcInterceptors().indexOf(ConnectionState.class.getName())<0) {
-            if (poolProperties.getDefaultReadOnly()!=null) connection.setReadOnly(poolProperties.getDefaultReadOnly().booleanValue());
-            if (poolProperties.getDefaultAutoCommit()!=null) connection.setAutoCommit(poolProperties.getDefaultAutoCommit().booleanValue());
-            if (poolProperties.getDefaultCatalog()!=null) connection.setCatalog(poolProperties.getDefaultCatalog());
-            if (poolProperties.getDefaultTransactionIsolation()!=DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION) connection.setTransactionIsolation(poolProperties.getDefaultTransactionIsolation());
-        }        
-        this.discarded = false;
-        this.lastConnected = System.currentTimeMillis();
     }
     
     /**
@@ -229,6 +265,7 @@ public class PooledConnection {
             }
         }
         connection = null;
+        xaConnection = null;
         lastConnected = -1;
         if (finalize) parent.finalize(this);
     }
@@ -502,6 +539,14 @@ public class PooledConnection {
         return this.connection;
     }
     
+    /**
+     * Returns the underlying XA connection
+     * @return the underlying XA connection as it was returned from the Datasource
+     */
+    public javax.sql.XAConnection getXAConnection() {
+        return this.xaConnection;
+    }
+    
     
     /**
      * Returns the timestamp of when the connection was last connected to the database.
index 6d8ef5b..503b7d9 100644 (file)
@@ -19,6 +19,8 @@ package org.apache.tomcat.jdbc.pool;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.sql.SQLException;
+
+import javax.sql.XAConnection;
 /**
  * A ProxyConnection object is the bottom most interceptor that wraps an object of type 
  * {@link PooledConnection}. The ProxyConnection intercepts three methods:
@@ -95,6 +97,17 @@ public class ProxyConnection extends JdbcInterceptor {
             return this.toString();
         } else if (compare(GETCONNECTION_VAL,method) && connection!=null) {
             return connection.getConnection();
+        } else if (method.getClass().equals(XAConnection.class)) {
+            try {
+                return method.invoke(connection.getXAConnection(),args);
+            }catch (Throwable t) {
+                if (t instanceof InvocationTargetException) {
+                    InvocationTargetException it = (InvocationTargetException)t;
+                    throw it.getCause()!=null?it.getCause():it;
+                } else {
+                    throw t;
+                }
+            }
         }
         if (isClosed()) throw new SQLException("Connection has already been closed.");
         if (compare(UNWRAP_VAL,method)) {
index d53e491..fcb4812 100644 (file)
@@ -570,5 +570,34 @@ public class ConnectionPool extends NotificationBroadcasterSupport implements Co
    public void setSuspectTimeout(int seconds) {
        //no op
    }
+   
+   /** 
+    * {@inheritDoc}
+    */
+   public void setDataSource(javax.sql.DataSource ds) {
+       getPoolProperties().setDataSource(ds);
+   }
+   
+   /** 
+    * {@inheritDoc}
+    */
+   public javax.sql.DataSource getDataSource() {
+       return getPoolProperties().getDataSource();
+   }
+   
+   
+   /** 
+    * {@inheritDoc}
+    */
+   public void setDataSourceJNDI(String jndiDS) {
+       //noop
+   }
+   
+   /** 
+    * {@inheritDoc}
+    */
+   public String getDataSourceJNDI() {
+       return getPoolProperties().getDataSourceJNDI();
+   }
 
 }