You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by jm...@apache.org on 2003/10/13 07:06:00 UTC

cvs commit: jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources PerUserPoolDataSourceFactory.java SharedPoolDataSourceFactory.java InstanceKeyDataSource.java InstanceKeyObjectFactory.java PerUserPoolDataSource.java SharedPoolDataSource.java

jmcnally    2003/10/12 22:06:00

  Modified:    dbcp/src/java/org/apache/commons/dbcp/datasources
                        InstanceKeyDataSource.java
                        InstanceKeyObjectFactory.java
                        PerUserPoolDataSource.java
                        SharedPoolDataSource.java
  Added:       dbcp/src/java/org/apache/commons/dbcp/datasources
                        PerUserPoolDataSourceFactory.java
                        SharedPoolDataSourceFactory.java
  Log:
  allow datasources to be created by tomcat which creates a Reference out of
  the parameters in server.xml
  
  Revision  Changes    Path
  1.7       +5 -5      jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/InstanceKeyDataSource.java
  
  Index: InstanceKeyDataSource.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/InstanceKeyDataSource.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- InstanceKeyDataSource.java	9 Oct 2003 21:03:57 -0000	1.6
  +++ InstanceKeyDataSource.java	13 Oct 2003 05:06:00 -0000	1.7
  @@ -158,7 +158,7 @@
       /** Description */
       private String description = null;
       /** Environment that may be used to set up a jndi initial context. */
  -    private Properties jndiEnvironment = null;
  +    Properties jndiEnvironment = null;
       /** Login TimeOut in seconds */
       private int loginTimeout = 0;
       /** Log stream */
  @@ -669,7 +669,7 @@
        * Attempt to establish a database connection.
        */
       public Connection getConnection(String username, String password)
  -            throws SQLException {
  +            throws SQLException {        
           if (instanceKey == null) {
               throw new SQLException("Must set the ConnectionPoolDataSource " 
                       + "through setDataSourceName or setConnectionPoolDataSource"
  
  
  
  1.6       +155 -17   jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/InstanceKeyObjectFactory.java
  
  Index: InstanceKeyObjectFactory.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/InstanceKeyObjectFactory.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- InstanceKeyObjectFactory.java	9 Oct 2003 21:03:57 -0000	1.5
  +++ InstanceKeyObjectFactory.java	13 Oct 2003 05:06:00 -0000	1.6
  @@ -61,10 +61,15 @@
   
   package org.apache.commons.dbcp.datasources;
   
  +import java.io.ByteArrayInputStream;
  +import java.io.IOException;
  +import java.io.ObjectInputStream;
  +
   import java.util.Hashtable;
   import java.util.Map;
   import java.util.HashMap;
   import java.util.Iterator;
  +import java.util.Properties;
   
   import javax.naming.Context;
   import javax.naming.Name;
  @@ -74,24 +79,22 @@
   
   /**
    * A JNDI ObjectFactory which creates <code>SharedPoolDataSource</code>s
  + * or <code>PerUserPoolDataSource</code>s
    */
  -public class InstanceKeyObjectFactory
  +abstract class InstanceKeyObjectFactory
       implements ObjectFactory
   {
  -    private static final String SHARED_POOL_CLASSNAME =
  -        SharedPoolDataSource.class.getName();
  -
  -    private static final String PER_USER_POOL_CLASSNAME =
  -        PerUserPoolDataSource.class.getName();
  -
       private static Map instanceMap = new HashMap();
   
       synchronized static String registerNewInstance(InstanceKeyDataSource ds) {
           int max = 0;
           Iterator i = instanceMap.keySet().iterator();
           while (i.hasNext()) {
  -            int key = Integer.parseInt((String)i.next());
  -            max = Math.max(max, key);
  +            Object obj = i.next();
  +            if (obj instanceof String) 
  +            {
  +                max = Math.max(max, Integer.parseInt((String)obj));
  +            }
           }
           String instanceKey = String.valueOf(max + 1);
           // put a placeholder here for now, so other instances will not
  @@ -118,27 +121,162 @@
           instanceMap.clear();
       }
   
  +
       /**
        * implements ObjectFactory to create an instance of SharedPoolDataSource
        * or PerUserPoolDataSource
        */ 
       public Object getObjectInstance(Object refObj, Name name, 
  -                                    Context context, Hashtable env) 
  -        {
  +                                    Context context, Hashtable env)
  +        throws IOException, ClassNotFoundException {
           // The spec says to return null if we can't create an instance 
           // of the reference
           Object obj = null;
           if (refObj instanceof Reference) {
               Reference ref = (Reference) refObj;
  -            String classname = ref.getClassName();
  -            if (SHARED_POOL_CLASSNAME.equals(classname)
  -                || PER_USER_POOL_CLASSNAME.equals(classname)) {
  +            if (isCorrectClass(ref.getClassName())) { 
                   RefAddr ra = ref.get("instanceKey");
                   if (ra != null && ra.getContent() != null) {
  +                    // object was bound to jndi via Referenceable api.
                       obj = instanceMap.get(ra.getContent());
                   }
  -            }            
  +                else 
  +                {
  +                    // tomcat jndi creates a Reference out of server.xml 
  +                    // <ResourceParam> configuration and passes it to an
  +                    // instance of the factory given in server.xml.
  +                    String key = null;
  +                    if (name != null) 
  +                    {
  +                        key = name.toString();
  +                        obj = instanceMap.get(key); 
  +                    }                    
  +                    if (obj == null)
  +                    {
  +                        InstanceKeyDataSource ds = getNewInstance(ref);
  +                        setCommonProperties(ref, ds);
  +                        obj = ds;
  +                        if (key != null) 
  +                        {
  +                            instanceMap.put(key, ds);
  +                        }                        
  +                    }
  +                }
  +            }
           }
           return obj;
       }
  +
  +    private void setCommonProperties(Reference ref, 
  +                                     InstanceKeyDataSource ikds) 
  +        throws IOException, ClassNotFoundException {
  +                    
  +        RefAddr ra = ref.get("dataSourceName");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setDataSourceName(ra.getContent().toString());
  +        }
  +                    
  +        ra = ref.get("defaultAutoCommit");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setDefaultAutoCommit(Boolean.valueOf(
  +                ra.getContent().toString()).booleanValue());
  +        }
  +        
  +        ra = ref.get("defaultReadOnly");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setDefaultReadOnly(Boolean.valueOf(
  +                ra.getContent().toString()).booleanValue());
  +        }
  +        
  +        ra = ref.get("description");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setDescription(ra.getContent().toString());
  +        }
  +        
  +        ra = ref.get("jndiEnvironment");
  +        if (ra != null  && ra.getContent() != null) {
  +            byte[] serialized = (byte[]) ra.getContent();
  +            ikds.jndiEnvironment = 
  +                (Properties) deserialize(serialized);
  +        }
  +        
  +        ra = ref.get("loginTimeout");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setLoginTimeout(
  +                Integer.parseInt(ra.getContent().toString()));
  +        }
  +        
  +        ra = ref.get("testOnBorrow");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setTestOnBorrow(
  +                Boolean.getBoolean(ra.getContent().toString()));
  +        }
  +        
  +        ra = ref.get("testOnReturn");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setTestOnReturn(Boolean.valueOf(
  +                ra.getContent().toString()).booleanValue());
  +        }
  +        
  +        ra = ref.get("timeBetweenEvictionRunsMillis");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setTimeBetweenEvictionRunsMillis(
  +                Integer.parseInt(ra.getContent().toString()));
  +        }
  +        
  +        ra = ref.get("numTestsPerEvictionRun");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setNumTestsPerEvictionRun(
  +                Integer.parseInt(ra.getContent().toString()));
  +        }
  +        
  +        ra = ref.get("minEvictableIdleTimeMillis");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setMinEvictableIdleTimeMillis(
  +                Integer.parseInt(ra.getContent().toString()));
  +        }
  +        
  +        ra = ref.get("testWhileIdle");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setTestWhileIdle(Boolean.valueOf(
  +                ra.getContent().toString()).booleanValue());
  +        }
  +                
  +        ra = ref.get("validationQuery");
  +        if (ra != null && ra.getContent() != null) {
  +            ikds.setValidationQuery(ra.getContent().toString());
  +        }
  +    }
  +
  +
  +    /**
  +     * @return true if and only if className is the value returned
  +     * from getClass().getName().toString()
  +     */
  +    protected abstract boolean isCorrectClass(String className);
  +
  +    /**
  +     * Creates an instance of the subclass and sets any properties
  +     * contained in the Reference.
  +     */
  +    protected abstract InstanceKeyDataSource getNewInstance(Reference ref)
  +        throws IOException, ClassNotFoundException;
  +
  +    /**
  +     * used to set some properties saved within a Reference
  +     */
  +    protected static final Object deserialize(byte[] data) 
  +        throws IOException, ClassNotFoundException {
  +        ObjectInputStream in = null;
  +        try {
  +            in = new ObjectInputStream(new ByteArrayInputStream(data));
  +            return in.readObject();
  +        } finally {
  +            try { 
  +                in.close(); 
  +            } catch (IOException ex) {
  +            }
  +        }
  +    }
   }
  +
  
  
  
  1.7       +10 -10    jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/PerUserPoolDataSource.java
  
  Index: PerUserPoolDataSource.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/PerUserPoolDataSource.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- PerUserPoolDataSource.java	9 Oct 2003 21:03:57 -0000	1.6
  +++ PerUserPoolDataSource.java	13 Oct 2003 05:06:00 -0000	1.7
  @@ -99,12 +99,12 @@
       private int defaultMaxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
       private int defaultMaxWait = (int)Math.min((long)Integer.MAX_VALUE,
           GenericObjectPool.DEFAULT_MAX_WAIT);
  -    private Map perUserDefaultAutoCommit = null;    
  -    private Map perUserDefaultTransactionIsolation = null;
  -    private Map perUserMaxActive = null;    
  -    private Map perUserMaxIdle = null;    
  -    private Map perUserMaxWait = null;
  -    private Map perUserDefaultReadOnly = null;    
  +    Map perUserDefaultAutoCommit = null;    
  +    Map perUserDefaultTransactionIsolation = null;
  +    Map perUserMaxActive = null;    
  +    Map perUserMaxIdle = null;    
  +    Map perUserMaxWait = null;
  +    Map perUserDefaultReadOnly = null;    
   
       private transient Map pools = new HashMap();
   
  @@ -541,7 +541,7 @@
           {
               in.defaultReadObject();
               PerUserPoolDataSource oldDS = (PerUserPoolDataSource)
  -                new InstanceKeyObjectFactory()
  +                new PerUserPoolDataSourceFactory()
                       .getObjectInstance(getReference(), null, null, null);
               this.pools = oldDS.pools;
           }
  
  
  
  1.7       +4 -4      jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/SharedPoolDataSource.java
  
  Index: SharedPoolDataSource.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/SharedPoolDataSource.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- SharedPoolDataSource.java	9 Oct 2003 21:03:57 -0000	1.6
  +++ SharedPoolDataSource.java	13 Oct 2003 05:06:00 -0000	1.7
  @@ -278,7 +278,7 @@
           {
               in.defaultReadObject();
               SharedPoolDataSource oldDS = (SharedPoolDataSource)
  -                new InstanceKeyObjectFactory()
  +                new SharedPoolDataSourceFactory()
                       .getObjectInstance(getReference(), null, null, null);
               this.pool = oldDS.pool;
           }
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/PerUserPoolDataSourceFactory.java
  
  Index: PerUserPoolDataSourceFactory.java
  ===================================================================
  /*
   * $Source: /home/cvs/jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/PerUserPoolDataSourceFactory.java,v $
   * $Revision: 1.1 $
   * $Date: 2003/10/13 05:06:00 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowledgement:
   *       "This product includes software developed by the
   *        Apache Software Foundation - http://www.apache.org/"
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * http://www.apache.org/
   *
   */
  
  package org.apache.commons.dbcp.datasources;
  
  import java.io.IOException;
  import java.util.Hashtable;
  import java.util.Map;
  import java.util.HashMap;
  import java.util.Iterator;
  
  import javax.naming.Context;
  import javax.naming.Name;
  import javax.naming.RefAddr;
  import javax.naming.Reference;
  import javax.naming.spi.ObjectFactory;
  
  /**
   * A JNDI ObjectFactory which creates <code>SharedPoolDataSource</code>s
   */
  public class PerUserPoolDataSourceFactory
      extends InstanceKeyObjectFactory
  {
      private static final String PER_USER_POOL_CLASSNAME =
          PerUserPoolDataSource.class.getName();
  
      protected boolean isCorrectClass(String className) {
          return PER_USER_POOL_CLASSNAME.equals(className);
      }
  
      protected InstanceKeyDataSource getNewInstance(Reference ref) 
          throws IOException, ClassNotFoundException {
          PerUserPoolDataSource pupds =  new PerUserPoolDataSource();
          RefAddr ra = ref.get("defaultMaxActive");
          if (ra != null && ra.getContent() != null) {
              pupds.setDefaultMaxActive(
                  Integer.parseInt(ra.getContent().toString()));
          }
  
          ra = ref.get("defaultMaxIdle");
          if (ra != null && ra.getContent() != null) {
              pupds.setDefaultMaxIdle(
                  Integer.parseInt(ra.getContent().toString()));
          }
  
          ra = ref.get("defaultMaxWait");
          if (ra != null && ra.getContent() != null) {
              pupds.setDefaultMaxWait(
                  Integer.parseInt(ra.getContent().toString()));
          }
  
          ra = ref.get("perUserDefaultAutoCommit");
          if (ra != null  && ra.getContent() != null) {
              byte[] serialized = (byte[]) ra.getContent();
              pupds.perUserDefaultAutoCommit = (Map) deserialize(serialized);
          }
  
          ra = ref.get("perUserDefaultTransactionIsolation");
          if (ra != null  && ra.getContent() != null) {
              byte[] serialized = (byte[]) ra.getContent();
              pupds.perUserDefaultTransactionIsolation = 
                  (Map) deserialize(serialized);
          }
  
          ra = ref.get("perUserMaxActive");
          if (ra != null  && ra.getContent() != null) {
              byte[] serialized = (byte[]) ra.getContent();
              pupds.perUserMaxActive = (Map) deserialize(serialized);
          }
          
          ra = ref.get("perUserMaxIdle");
          if (ra != null  && ra.getContent() != null) {
              byte[] serialized = (byte[]) ra.getContent();
              pupds.perUserMaxIdle = (Map) deserialize(serialized);
          }
          
          ra = ref.get("perUserMaxWait");
          if (ra != null  && ra.getContent() != null) {
              byte[] serialized = (byte[]) ra.getContent();
              pupds.perUserMaxWait = (Map) deserialize(serialized);
          }
                  
          ra = ref.get("perUserDefaultReadOnly");
          if (ra != null  && ra.getContent() != null) {
              byte[] serialized = (byte[]) ra.getContent();
              pupds.perUserDefaultReadOnly = (Map) deserialize(serialized);
          }
          return pupds;
      }            
  }
  
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/SharedPoolDataSourceFactory.java
  
  Index: SharedPoolDataSourceFactory.java
  ===================================================================
  /*
   * $Source: /home/cvs/jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/datasources/SharedPoolDataSourceFactory.java,v $
   * $Revision: 1.1 $
   * $Date: 2003/10/13 05:06:00 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowledgement:
   *       "This product includes software developed by the
   *        Apache Software Foundation - http://www.apache.org/"
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * http://www.apache.org/
   *
   */
  
  package org.apache.commons.dbcp.datasources;
  
  import java.util.Hashtable;
  import java.util.Map;
  import java.util.HashMap;
  import java.util.Iterator;
  
  import javax.naming.Context;
  import javax.naming.Name;
  import javax.naming.RefAddr;
  import javax.naming.Reference;
  import javax.naming.spi.ObjectFactory;
  
  /**
   * A JNDI ObjectFactory which creates <code>SharedPoolDataSource</code>s
   */
  public class SharedPoolDataSourceFactory
      extends InstanceKeyObjectFactory
  {
      private static final String SHARED_POOL_CLASSNAME =
          SharedPoolDataSource.class.getName();
  
      protected boolean isCorrectClass(String className) {
          return SHARED_POOL_CLASSNAME.equals(className);
      }
  
      protected InstanceKeyDataSource getNewInstance(Reference ref) {
          SharedPoolDataSource spds = new SharedPoolDataSource();
          RefAddr ra = ref.get("maxActive");
          if (ra != null && ra.getContent() != null) {
              spds.setMaxActive(
                  Integer.parseInt(ra.getContent().toString()));
          }
  
          ra = ref.get("maxIdle");
          if (ra != null && ra.getContent() != null) {
              spds.setMaxIdle(
                  Integer.parseInt(ra.getContent().toString()));
          }
  
          ra = ref.get("maxWait");
          if (ra != null && ra.getContent() != null) {
              spds.setMaxWait(
                  Integer.parseInt(ra.getContent().toString()));
          }
          
          return spds;
      }            
  }
  
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org