Author: kfujino
Date: Mon Sep  9 10:11:07 2013
New Revision: 1521024

URL: http://svn.apache.org/r1521024
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=54693.
- Add a validationQueryTimeout property
- Avoid NPE caused by createConnection(..) returns null. (see Comment 6)

Added:
    
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestValidationQueryTimeout.java
   (with props)
Modified:
    tomcat/trunk/modules/jdbc-pool/doc/jdbc-pool.xml
    
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
    
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
    
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
    
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java
    
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
    
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
    
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
    
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml

Modified: tomcat/trunk/modules/jdbc-pool/doc/jdbc-pool.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/doc/jdbc-pool.xml?rev=1521024&r1=1521023&r2=1521024&view=diff
==============================================================================
--- tomcat/trunk/modules/jdbc-pool/doc/jdbc-pool.xml (original)
+++ tomcat/trunk/modules/jdbc-pool/doc/jdbc-pool.xml Mon Sep  9 10:11:07 2013
@@ -274,6 +274,15 @@
          Example values are <code>SELECT 1</code>(mysql), <code>select 1 from 
dual</code>(oracle), <code>SELECT 1</code>(MS Sql Server)
       </p>
     </attribute>
+    
+    <attribute name="validationQueryTimeout" required="false">
+      <p>(int) The timeout in seconds before a connection validation queries 
fail.  This works by calling 
+         <code>java.sql.Statement.setQueryTimeout(seconds)</code> on the 
statement that executes the <code>validationQuery</code>.
+         The pool itself doesn't timeout the query, it is still up to the JDBC 
driver to enforce query timeouts. 
+         A value less than or equal to zero will disable this feature. 
+         The default value is <code>-1</code>.
+      </p>
+    </attribute>
 
     <attribute name="validatorClassName" required="false">
       <p>(String) The name of a class which implements the

Modified: 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java?rev=1521024&r1=1521023&r2=1521024&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
 Mon Sep  9 10:11:07 2013
@@ -712,7 +712,7 @@ public class ConnectionPool {
             } else {
                 //validation failed, make sure we disconnect
                 //and clean up
-                error =true;
+                throw new SQLException("Validation Query Failed, enable 
logValidationErrors for more details.");
             } //end if
         } catch (Exception e) {
             error = true;
@@ -732,7 +732,6 @@ public class ConnectionPool {
             }
             con.unlock();
         }//catch
-        return null;
     }
 
     /**

Modified: 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java?rev=1521024&r1=1521023&r2=1521024&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
 Mon Sep  9 10:11:07 2013
@@ -80,6 +80,7 @@ public class DataSourceFactory implement
     protected static final String PROP_TESTWHILEIDLE = "testWhileIdle";
     protected static final String PROP_TESTONCONNECT = "testOnConnect";
     protected static final String PROP_VALIDATIONQUERY = "validationQuery";
+    protected static final String PROP_VALIDATIONQUERY_TIMEOUT = 
"validationQueryTimeout";
     protected static final String PROP_VALIDATOR_CLASS_NAME = 
"validatorClassName";
 
     protected static final String PROP_NUMTESTSPEREVICTIONRUN = 
"numTestsPerEvictionRun";
@@ -151,6 +152,7 @@ public class DataSourceFactory implement
         PROP_URL,
         PROP_USERNAME,
         PROP_VALIDATIONQUERY,
+        PROP_VALIDATIONQUERY_TIMEOUT,
         PROP_VALIDATOR_CLASS_NAME,
         PROP_VALIDATIONINTERVAL,
         PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED,
@@ -369,6 +371,11 @@ public class DataSourceFactory implement
         if (value != null) {
             poolProperties.setValidationQuery(value);
         }
+        
+        value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT);
+        if (value != null) {
+            poolProperties.setValidationQueryTimeout(Integer.parseInt(value));
+        }
 
         value = properties.getProperty(PROP_VALIDATOR_CLASS_NAME);
         if (value != null) {

Modified: 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java?rev=1521024&r1=1521023&r2=1521024&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
 Mon Sep  9 10:11:07 2013
@@ -452,6 +452,15 @@ public class DataSourceProxy implements 
     public void setValidatorClassName(String className) {
         this.poolProperties.setValidatorClassName(className);
     }
+    
+    /**
+     * {@inheritDoc}
+     */
+    
+    @Override
+    public void setValidationQueryTimeout(int validationQueryTimeout) {
+        this.poolProperties.setValidationQueryTimeout(validationQueryTimeout);
+    }
 
     /**
      * {@inheritDoc}
@@ -928,6 +937,15 @@ public class DataSourceProxy implements 
     /**
      * {@inheritDoc}
      */
+    
+    @Override
+    public int getValidationQueryTimeout() {
+        return getPoolProperties().getValidationQueryTimeout();
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
 
     @Override
     public String getValidatorClassName() {

Modified: 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java?rev=1521024&r1=1521023&r2=1521024&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java
 Mon Sep  9 10:11:07 2013
@@ -524,6 +524,19 @@ public interface PoolConfiguration {
     public void setValidationQuery(String validationQuery);
 
     /**
+     * The timeout in seconds before a connection validation queries fail.
+     * A value less than or equal to zero will disable this feature.  Defaults 
to -1. 
+     * @return the timeout value in seconds 
+     */
+    public int getValidationQueryTimeout();
+    
+    /**
+     * The timeout in seconds before a connection validation queries fail.
+     * A value less than or equal to zero will disable this feature.  Defaults 
to -1. 
+     */
+    public void setValidationQueryTimeout(int validationQueryTimeout);
+    
+    /**
      * Return the name of the optional validator class - may be null.
      *
      * @return the name of the optional validator class - may be null

Modified: 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java?rev=1521024&r1=1521023&r2=1521024&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
 Mon Sep  9 10:11:07 2013
@@ -57,6 +57,7 @@ public class PoolProperties implements P
     private volatile int minIdle = initialSize;
     private volatile int maxWait = 30000;
     private volatile String validationQuery;
+    private volatile int validationQueryTimeout = -1;
     private volatile String validatorClassName;
     private volatile Validator validator;
     private volatile boolean testOnBorrow = false;
@@ -382,6 +383,22 @@ public class PoolProperties implements P
     /**
      * {@inheritDoc}
      */
+    @Override
+    public int getValidationQueryTimeout() {
+        return validationQueryTimeout;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setValidationQueryTimeout(int validationQueryTimeout) {
+        this.validationQueryTimeout = validationQueryTimeout;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
 
     @Override
     public String getValidatorClassName() {

Modified: 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java?rev=1521024&r1=1521023&r2=1521024&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
 Mon Sep  9 10:11:07 2013
@@ -451,6 +451,12 @@ public class PooledConnection {
         Statement stmt = null;
         try {
             stmt = connection.createStatement();
+            
+            int validationQueryTimeout = 
poolProperties.getValidationQueryTimeout();
+            if (validationQueryTimeout > 0) {
+                stmt.setQueryTimeout(validationQueryTimeout);
+            }
+            
             stmt.execute(query);
             stmt.close();
             this.lastValidated = now;

Modified: 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java?rev=1521024&r1=1521023&r2=1521024&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
 Mon Sep  9 10:11:07 2013
@@ -310,6 +310,11 @@ public class ConnectionPool extends Noti
     public String getValidationQuery() {
         return getPoolProperties().getValidationQuery();
     }
+    
+    @Override
+    public int getValidationQueryTimeout() {
+        return getPoolProperties().getValidationQueryTimeout();
+    }
 
     /**
      * {@inheritDoc}
@@ -664,6 +669,11 @@ public class ConnectionPool extends Noti
         getPoolProperties().setValidationQuery(validationQuery);
     }
 
+    @Override
+    public void setValidationQueryTimeout(int validationQueryTimeout) {
+        getPoolProperties().setValidationQueryTimeout(validationQueryTimeout);
+    }
+    
     /**
      * {@inheritDoc}
      */

Modified: 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml?rev=1521024&r1=1521023&r2=1521024&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml
 Mon Sep  9 10:11:07 2013
@@ -125,6 +125,11 @@
            description="The query to run during validation"
                   type="java.lang.String"
              writeable="false"/>
+             
+    <attribute    name="validationQueryTimeout"
+           description="The timeout in seconds before a connection validation 
queries fail"
+                  type="java.lang.Integer"
+             writeable="false" />
 
     <attribute    name="testOnBorrow"
            description="True if validation happens when a connection is 
requested"

Added: 
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestValidationQueryTimeout.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestValidationQueryTimeout.java?rev=1521024&view=auto
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestValidationQueryTimeout.java
 (added)
+++ 
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestValidationQueryTimeout.java
 Mon Sep  9 10:11:07 2013
@@ -0,0 +1,249 @@
+package org.apache.tomcat.jdbc.test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.DriverPropertyInfo;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Statement;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import junit.framework.Assert;
+
+import org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestValidationQueryTimeout extends DefaultTestCase {
+    
+    private static int TIMEOUT = 10;
+    private static boolean isTimeoutSet;
+    private static final String longQuery = "select * from test as A, test as 
B, test as C, test as D, test as E";
+    
+    @Before
+    public void setUp() throws SQLException {
+        DriverManager.registerDriver(new MockDriver());
+        
+        // use our mock driver
+        this.datasource.setDriverClassName(MockDriver.class.getName());
+        this.datasource.setUrl(MockDriver.url);
+        
+        // Required to trigger validation query's execution 
+        this.datasource.setInitialSize(1);
+        this.datasource.setTestOnBorrow(true);
+        this.datasource.setValidationInterval(-1);
+        this.datasource.setValidationQuery("SELECT 1");
+        this.datasource.setValidationQueryTimeout(TIMEOUT);
+        
+        TIMEOUT = 10;
+        isTimeoutSet = false;
+    }
+    
+    @After
+    public void tearDown() throws SQLException {
+        DriverManager.deregisterDriver(new MockDriver());
+    }
+    
+    @Test
+    public void testValidationQueryTimeoutEnabled() throws Exception {
+        // because testOnBorrow is true, this triggers the validation query
+        this.datasource.getConnection();
+        Assert.assertTrue(isTimeoutSet);
+    }
+    
+    @Test
+    public void testValidationQueryTimeoutDisabled() throws Exception {
+        this.datasource.setValidationQueryTimeout(-1);
+        
+        // because testOnBorrow is true, this triggers the validation query
+        this.datasource.getConnection();
+        Assert.assertFalse(isTimeoutSet);
+    }
+    
+    @Test
+    public void testValidationQueryTimeoutWithQueryTimeoutInterceptor() throws 
Exception {
+        int interceptorTimeout = 30;
+        this.datasource.setJdbcInterceptors(
+                            QueryTimeoutInterceptor.class.getName()+
+                            "(queryTimeout="+ interceptorTimeout +")");
+        
+        // because testOnBorrow is true, this triggers the validation query
+        Connection con = this.datasource.getConnection();
+        Assert.assertTrue(isTimeoutSet);
+        
+        // increase the expected timeout to 30, which is what we set for the 
interceptor
+        TIMEOUT = 30;
+        
+        // now create a statement, make sure the query timeout is set by the 
interceptor
+        Statement st = con.createStatement();
+        Assert.assertEquals(interceptorTimeout, st.getQueryTimeout());
+        st.close();
+        st = con.prepareStatement("");
+        Assert.assertEquals(interceptorTimeout, st.getQueryTimeout());
+        st.close();
+        st = con.prepareCall("");
+        Assert.assertEquals(interceptorTimeout, st.getQueryTimeout());
+        st.close();
+        con.close();
+        
+        // pull another connection and check it
+        TIMEOUT = 10;
+        isTimeoutSet = false;
+        this.datasource.getConnection();
+        Assert.assertTrue(isTimeoutSet);
+    }
+    
+    // this test depends on the execution time of the validation query
+    //   specifically, it needs to run for longer than 1 second to pass
+    //   if this fails
+    @Test(expected=SQLException.class) 
+    public void testValidationQueryTimeoutOnConnection() throws Exception {
+        // use our mock driver
+        this.datasource.setDriverClassName("org.h2.Driver");
+        
this.datasource.setUrl("jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=FALSE");
+        
+        // Required to trigger validation query's execution 
+        this.datasource.setTestOnConnect(true);
+        this.datasource.setValidationInterval(-1);
+        this.datasource.setValidationQuery(longQuery);
+        this.datasource.setValidationQueryTimeout(1);
+        
+        this.datasource.getConnection();
+    }
+    
+    @Test(expected=SQLException.class)
+    public void testValidationInvalidOnConnection() throws Exception {
+        // use our mock driver
+        this.datasource.setDriverClassName("org.h2.Driver");
+        
this.datasource.setUrl("jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=FALSE");
+        
+        // Required to trigger validation query's execution 
+        this.datasource.setTestOnBorrow(true);
+        this.datasource.setInitialSize(1);
+        this.datasource.setTestOnConnect(true);
+        this.datasource.setValidationInterval(-1);
+        this.datasource.setValidationQuery("SELECT");
+        this.datasource.setValidationQueryTimeout(1);
+        
+        this.datasource.getConnection();
+    }
+    
+    @Test
+    public void testLongValidationQueryTime() throws Exception {
+        // use our mock driver
+        this.datasource.setDriverClassName("org.h2.Driver");
+        
this.datasource.setUrl("jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=FALSE");
+        Connection con = this.datasource.getConnection();
+        Statement stmt = null;
+        long start = 0, end = 0;
+        try {
+            stmt = con.createStatement();
+            // set the query timeout to 2 sec
+            //  this keeps this test from slowing things down too much
+            stmt.setQueryTimeout(2);
+            // assert that our long query takes longer than one second to run
+            //  this is a requirement for other tests to run properly
+            start = System.currentTimeMillis();
+            stmt.execute(longQuery);
+        } catch (SQLException ex) {}
+        finally {
+            end = System.currentTimeMillis();
+        
+            if (stmt != null) { stmt.close(); }
+            if (con != null) { con.close(); }
+            
+            Assert.assertTrue(start != 0 && end != 0);
+            Assert.assertTrue((end - start) > 1000);
+        }
+    }
+    
+    @Test
+    public void testValidationQueryTimeoutOnBorrow() throws Exception {
+        // use our mock driver
+        this.datasource.setDriverClassName("org.h2.Driver");
+        
this.datasource.setUrl("jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=FALSE");
+        
+        // Required to trigger validation query's execution 
+        this.datasource.setTestOnBorrow(true);
+        this.datasource.setValidationInterval(-1);
+        this.datasource.setValidationQuery(longQuery);
+        this.datasource.setValidationQueryTimeout(1);
+        
+        // assert that even though the validation query times out, we still 
get a connection
+        Connection con = this.datasource.getConnection();
+        Assert.assertNotNull(con);
+        Statement st = con.createStatement();
+        ResultSet rs = st.executeQuery("SELECT 1");
+        rs.close();
+        st.close();
+        con.close();
+    }
+    
+    /**
+     * Mock Driver, Connection and Statement implementations use to verify 
setQueryTimeout was called.
+     */
+    public static class MockDriver implements java.sql.Driver {
+        public static final String url = "jdbc:tomcat:mock";
+
+        public MockDriver() {
+        }
+
+        @Override
+        public boolean acceptsURL(String url) throws SQLException {
+            return url!=null && url.equals(MockDriver.url);
+        }
+
+        @Override
+        public Connection connect(String url, Properties info) throws 
SQLException {
+            return new MockConnection(info);
+        }
+
+        @Override
+        public int getMajorVersion() {
+            return 0;
+        }
+
+        @Override
+        public int getMinorVersion() {
+            return 0;
+        }
+
+        @Override
+        public DriverPropertyInfo[] getPropertyInfo(String url, Properties 
info) throws SQLException {
+            return null;
+        }
+
+        @Override
+        public boolean jdbcCompliant() {
+            return false;
+        }
+
+        @Override
+        public Logger getParentLogger() throws SQLFeatureNotSupportedException 
{
+            return null;
+        }
+    }
+    
+    public static class MockConnection extends 
org.apache.tomcat.jdbc.test.driver.Connection {
+        public MockConnection(Properties info) {
+            super(info);
+        }
+
+        @Override
+        public Statement createStatement() throws SQLException {
+            return new MockStatement();
+        }
+    }
+    
+    public static class MockStatement extends 
org.apache.tomcat.jdbc.test.driver.Statement {
+        @Override
+        public void setQueryTimeout(int seconds) throws SQLException {
+            super.setQueryTimeout(seconds);
+            Assert.assertEquals(TIMEOUT, seconds);
+            isTimeoutSet = true;
+        }
+    }
+}

Propchange: 
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestValidationQueryTimeout.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to