You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by cr...@apache.org on 2001/10/30 04:26:51 UTC

cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/factory DbcpDataSourceFactory.java

craigmcc    01/10/29 19:26:51

  Modified:    catalina build.xml
  Added:       catalina/src/share/org/apache/naming/factory
                        DbcpDataSourceFactory.java
  Log:
  Add an experimental JDBC data source factory based on the commons-dbcp and
  commons-pool pacakges.
  
  FIXME - Like the Tyrex factory, doesn't save the previous pool.  Needs to
  be fixed!
  
  FIXME - Add configuration parameters for other characteristics of the
  underlying pool.
  
  Revision  Changes    Path
  1.84      +47 -1     jakarta-tomcat-4.0/catalina/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/build.xml,v
  retrieving revision 1.83
  retrieving revision 1.84
  diff -u -r1.83 -r1.84
  --- build.xml	2001/10/27 21:20:03	1.83
  +++ build.xml	2001/10/30 03:26:51	1.84
  @@ -24,8 +24,10 @@
       <pathelement location="${activation.jar}"/>
       <pathelement location="${commons-beanutils.jar}"/>
       <pathelement location="${commons-collections.jar}"/>
  +    <pathelement location="${commons-dbcp.jar}"/>
       <pathelement location="${commons-digester.jar}"/>
       <pathelement location="${commons-modeler.jar}"/>
  +    <pathelement location="${commons-pool.jar}"/>
       <pathelement location="${jcert.jar}"/>
       <pathelement location="${jdbc20ext.jar}"/>
       <pathelement location="${jmxri.jar}"/>
  @@ -49,8 +51,10 @@
       <pathelement location="${activation.jar}"/>
       <pathelement location="${commons-beanutils.jar}"/>
       <pathelement location="${commons-collections.jar}"/>
  +    <pathelement location="${commons-dbcp.jar}"/>
       <pathelement location="${commons-digester.jar}"/>
       <pathelement location="${commons-modeler.jar}"/>
  +    <pathelement location="${commons-pool.jar}"/>
       <pathelement location="${jcert.jar}"/>
       <pathelement location="${jdbc20ext.jar}"/>
       <pathelement location="${jmxri.jar}"/>
  @@ -90,6 +94,9 @@
       <available property="collections.present"
        classname="org.apache.commons.collections.FastHashMap"
        classpath="${commons-collections.jar}"/>
  +    <available property="dbcp.present"
  +     classname="org.apache.commons.dbcp.ConnectionFactory"
  +     classpath="${commons-dbcp.jar}"/>
       <available property="digester.present"
        classname="org.apache.commons.digester.Digester"
        classpath="${commons-digester.jar}"/>
  @@ -145,6 +152,9 @@
       <available property="ldap.present"
        classname="com.sun.jndi.ldap.LdapClient"
        classpath="${ldap.jar}" />
  +    <available property="pool.present"
  +     classname="org.apache.commons.pool.ObjectPool"
  +     classpath="${commons-pool.jar}"/>
       <available property="regexp.present"
        classname="org.apache.regexp.RE"
        classpath="${regexp.jar}" />
  @@ -160,6 +170,7 @@
   
       <!-- JAR files availability flags -->
       <available property="tomcat-ajp.jar.present" file="${tomcat-ajp.jar}" />
  +    <available property="dbcp.jar.present" file="${commons-dbcp.jar}" />
       <condition property="javamail.jar.present">
         <and>
           <available file="${activation.jar}" />
  @@ -177,6 +188,7 @@
       <available property="junit.jar.present" file="${junit.jar}" />
       <available property="ldap.jar.present" file="${ldap.jar}" />
       <available property="modeler.jar.present" file="${commons-modeler.jar}" />
  +    <available property="pool.jar.present" file="${commons-pool.jar}" />
       <available property="regexp.jar.present" file="${regexp.jar}" />
       <available property="servlet.jar.present" file="${servlet.jar}" />
       <available property="tomcat-util.jar.present" file="${tomcat-util.jar}" />
  @@ -184,6 +196,15 @@
       <available property="xerces.jar.present" file="${xerces.jar}" />
   
       <!-- Conditional compilation flags (determined from the flags above) -->
  +    <condition property="compile.dbcp">
  +      <or>
  +        <equals arg1="${full.dist}" arg2="on" />
  +        <and>
  +          <equals arg1="${dbcp.present}" arg2="true" />
  +          <equals arg1="${pool.present}" arg2="true" />
  +        </and>
  +      </or>
  +    </condition>
       <condition property="compile.javamail">
         <or>
           <equals arg1="${full.dist}" arg2="on" />
  @@ -249,6 +270,12 @@
           <equals arg1="${tomcat-ajp.jar.present}" arg2="true" />
         </or>
       </condition>
  +    <condition property="copy.dbcp.jar">
  +      <or>
  +        <equals arg1="${full.dist}" arg2="on" />
  +        <equals arg1="${dbcp.present}" arg2="true" />
  +      </or>
  +    </condition>
       <condition property="copy.jdbc20ext.jar">
         <or>
           <equals arg1="${full.dist}" arg2="on" />
  @@ -331,6 +358,12 @@
           <equals arg1="${modeler.present}" arg2="true" />
         </or>
       </condition>
  +    <condition property="copy.pool.jar">
  +      <or>
  +        <equals arg1="${full.dist}" arg2="on" />
  +        <equals arg1="${pool.present}" arg2="true" />
  +      </or>
  +    </condition>
       <condition property="copy.tyrex.jar">
         <or>
           <equals arg1="${full.dist}" arg2="on" />
  @@ -381,6 +414,7 @@
       <echo message="servlet.present=${servlet.present}" />
   
       <echo message="--- Optional Libraries ---" />
  +    <echo message="dbcp.present=${dbcp.present}" />
       <echo message="javamail.present=${javamail.present}" />
       <echo message="jmx.present=${jmx.present}" />
       <echo message="jmxtools.present=${jmxtools.present}" />
  @@ -389,6 +423,7 @@
       <echo message="junit.present=${junit.present}" />
       <echo message="ldap.present=${ldap.present}" />
       <echo message="modeler.present=${modeler.present}" />
  +    <echo message="pool.present=${pool.present}" />
       <echo message="tyrex.present=${tyrex.present}" />
   
       <echo message="--- Required JARs ---" />
  @@ -399,6 +434,7 @@
   
       <echo message="--- Optional JARs ---" />
       <echo message="tomcat-ajp.jar.present=${tomcat-ajp.jar.present}" />
  +    <echo message="dbcp.jar.present=${dbcp.jar.present}" />
       <echo message="javamail.jar.present=${javamail.jar.present}" />
       <echo message="jdbc20ext.jar.present=${jdbc20ext.jar.present}" />
       <echo message="jmxri.jar.present=${jmxri.jar.present}" />
  @@ -407,10 +443,12 @@
       <echo message="junit.jar.present=${junit.jar.present}" />
       <echo message="ldap.jar.present=${ldap.jar.present}" />
       <echo message="modeler.jar.present=${modeler.jar.present}" />
  +    <echo message="pool.jar.present=${pool.jar.present}" />
       <echo message="tomcat-util.jar.present=${tomcat-util.jar.present}" />
       <echo message="tyrex.jar.present=${tyrex.jar.present}" />
   
       <echo message="--- Conditional compilation flags ---" />
  +    <echo message="compile.dbcp=${compile.dbcp}" />
       <echo message="compile.javamail=${compile.javamail}" />
       <echo message="compile.jmx=${compile.jmx}" />
       <echo message="compile.jndi=${compile.jndi}" />
  @@ -423,6 +461,7 @@
   
       <echo message="--- Distribution flags ---" />
       <echo message="copy.tomcat-ajp.jar=${copy.tomcat-ajp.jar}" />
  +    <echo message="copy.dbcp.jar=${copy.dbcp.jar}" />
       <echo message="copy.jdbc20ext.jar=${copy.jdbc20ext.jar}" />
       <echo message="copy.javamail.jar=${copy.javamail.jar}" />
       <echo message="copy.jmxri.jar=${copy.jmxri.jar}" />
  @@ -431,6 +470,7 @@
       <echo message="copy.jta.jar=${copy.jta.jar}" />
       <echo message="copy.ldap.jar=${copy.ldap.jar}" />
       <echo message="copy.modeler.jar=${copy.modeler.jar}" />
  +    <echo message="copy.pool.jar=${copy.pool.jar}" />
       <echo message="copy.tyrex.jar=${copy.tyrex.jar}" />
       <echo message="copy.xerces.jar=${copy.xerces.jar}" />
   
  @@ -462,6 +502,9 @@
       <copy todir="${catalina.build}/common/lib" file="${activation.jar}"/>
       <copy todir="${catalina.build}/common/lib" file="${mail.jar}"/>
     </target>
  +  <target name="copy-dbcp.jar" if="copy.dbcp.jar">
  +    <copy todir="${catalina.build}/server/lib" file="${commons-dbcp.jar}"/>
  +  </target>
     <target name="copy-jdbc20ext.jar" if="copy.jdbc20ext.jar">
       <copy todir="${catalina.build}/common/lib" file="${jdbc20ext.jar}"/>
     </target>
  @@ -492,6 +535,9 @@
     <target name="copy-modeler.jar" if="copy.modeler.jar">
       <copy todir="${catalina.build}/server/lib" file="${commons-modeler.jar}"/>
     </target>
  +  <target name="copy-pool.jar" if="copy.pool.jar">
  +    <copy todir="${catalina.build}/server/lib" file="${commons-pool.jar}"/>
  +  </target>
     <target name="copy-tomcat-util.jar">
       <copy todir="${catalina.build}/server/lib" file="${tomcat-util.jar}"/>
     </target>
  @@ -505,7 +551,7 @@
   
   
     <!-- =================== BUILD: Copy Static Files ======================= -->
  -  <target name="build-static" depends="flags,flags.display,build-prepare,copy-activation.jar,copy-tomcat-ajp.jar,copy-jdbc20ext.jar,copy-jmxri.jar,copy-jmxtools.jar,copy-jndi.jar,copy-jsse.jar,copy-jta.jar,copy-ldap.jar,copy-modeler.jar,copy-tomcat-util.jar,copy-tyrex.jar,copy-xerces.jar">
  +  <target name="build-static" depends="flags,flags.display,build-prepare,copy-activation.jar,copy-tomcat-ajp.jar,copy-dbcp.jar,copy-jdbc20ext.jar,copy-jmxri.jar,copy-jmxtools.jar,copy-jndi.jar,copy-jsse.jar,copy-jta.jar,copy-ldap.jar,copy-modeler.jar,copy-pool.jar,copy-tomcat-util.jar,copy-tyrex.jar,copy-xerces.jar">
   
       <!-- Executable Commands -->
       <copy todir="${catalina.build}/bin">
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/factory/DbcpDataSourceFactory.java
  
  Index: DbcpDataSourceFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/factory/DbcpDataSourceFactory.java,v 1.1 2001/10/30 03:26:51 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2001/10/30 03:26:51 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 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 acknowlegement:  
   *       "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", "Tomcat", 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/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  
  package org.apache.naming.factory;
  
  import java.util.Hashtable;
  import java.sql.Driver;
  import java.sql.DriverManager;
  import javax.naming.Name;
  import javax.naming.Context;
  import javax.naming.NamingException;
  import javax.naming.Reference;
  import javax.naming.RefAddr;
  import javax.naming.spi.ObjectFactory;
  import org.apache.commons.dbcp.DriverManagerConnectionFactory;
  import org.apache.commons.dbcp.PoolableConnectionFactory;
  import org.apache.commons.dbcp.PoolingDataSource;
  import org.apache.commons.pool.impl.GenericObjectPool;
  import org.apache.naming.ResourceRef;
  
  
  /**
   * <p>Object factory for DataSources based on the <code>commons-dbcp</code>
   * connection pool. See
   * <a href="http://jakarta.apache.org/commons/">http://jakarta.apache.org/commons</a>
   * for more information.</p>
   *
   * <p>This factory is configured based on the following properties:</p>
   * <ul>
   * <li><strong>driverClassName</strong> - Fully qualified Java class name of
   *     the JDBC driver to be used.</li>
   * <li><strong>driverName</strong> - Connection URL to be passed to our JDBC
   *     driver.  <em>DEPRECATED - use the <strong>url</strong> property to
   *     initialize the connection URL.</li>
   * <li><strong>password</strong> - Database password to be passed to our
   *     JDBC driver.</li>
   * <li><strong>url</strong> - Connection URL to be passed to our
   *     JDBC driver.</li>
   * <li><strong>user</strong> - Database username to be passed to our
   *     JDBC driver.</li>
   * </ul>
   * 
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2001/10/30 03:26:51 $
   */
  
  public class DbcpDataSourceFactory
      implements ObjectFactory {
  
  
      // ----------------------------------------------------------- Constructors
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      // --------------------------------------------------------- Public Methods
  
  
      // -------------------------------------------------- ObjectFactory Methods
  
  
      /**
       * Create a new DataSource instance.
       * 
       * @param obj The reference object describing the DataSource
       */
      public Object getObjectInstance(Object obj, Name name, Context nameCtx,
                                      Hashtable environment)
          throws NamingException {
          
          // Can we process this request?
          if (!(obj instanceof ResourceRef))
              return (null);
          Reference ref = (Reference) obj;
          if (!"javax.sql.DataSource".equals(ref.getClassName()))
              return (null);
  
          // Extract configuration parameters for our new data source
          RefAddr currentRefAddr = null;
  
          String driverClassName = null;
          currentRefAddr = ref.get("driverClassName");
          if (currentRefAddr != null)
              driverClassName = currentRefAddr.getContent().toString();
  
          String password = null;
          currentRefAddr = ref.get("password");
          if (currentRefAddr != null)
              password = currentRefAddr.getContent().toString();
  
          String url = null;
          currentRefAddr = ref.get("url");
          if (currentRefAddr == null)
              currentRefAddr = ref.get("driverName");
          if (currentRefAddr != null)
              url = currentRefAddr.getContent().toString();
  
          String user = null;
          currentRefAddr = ref.get("user");
          if (currentRefAddr != null)
              user = currentRefAddr.getContent().toString();
  
          // Validate our configuration parameters
          if (driverClassName == null)
              throw new NamingException
                  ("DbcpDataSourceFactory:  driverClassName is required");
          if (password == null)
              throw new NamingException
                  ("DbcpDataSourceFactory:  password is required");
          if (url == null)
              throw new NamingException
                  ("DbcpDataSourceFactory:  url is required");
          if (user == null)
              throw new NamingException
                  ("DbcpDataSourceFactory:  username is required");
          log("driverClassName=" + driverClassName + ", password=" + password +
              ", url=" + url + ", user=" + user);
  
          // Load and register the JDBC driver
          Class driverClass = null;
          try {
              driverClass = Class.forName(driverClassName);
          } catch (Throwable t) {
              log("Cannot load JDBC driver " + driverClassName, t);
              throw new NamingException("Cannot load JDBC driver " +
                                        driverClassName);
          }
          Driver driver = null;
          try {
              driver = (Driver) driverClass.newInstance();
          } catch (Throwable t) {
              log("Cannot create JDBC driver " + driverClassName +
                  " instance", t);
              throw new NamingException("Cannot create JDBC driver " +
                                        driverClassName + " instance");
          }
          try {
              DriverManager.registerDriver(driver);
          } catch (Throwable t) {
              log("Cannot register JDBC driver " + driverClassName +
                  " instance", t);
              throw new NamingException("Cannot register JDBC driver " +
                                        driverClassName + " instance");
          }
  
          // Create a new data source instance
          // FIXME - Cache this for later reuse???
          GenericObjectPool connectionPool = new GenericObjectPool(null);
          DriverManagerConnectionFactory connectionFactory =
              new DriverManagerConnectionFactory(url, user, password);
          PoolableConnectionFactory poolableConnectionFactory =
              new PoolableConnectionFactory(connectionFactory, connectionPool,
                                            null, null,
                                            false, true);
          PoolingDataSource dataSource =
              new PoolingDataSource(connectionPool);
          return (dataSource);
  
          
      }
  
  
      // -------------------------------------------------------- Private Methods
  
  
      private void log(String message) {
          System.out.print("DbcpDataSourceFactory:  ");
          System.out.println(message);
      }
  
  
      private void log(String message, Throwable exception) {
          log(message);
          exception.printStackTrace(System.out);
      }
  
  }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/factory DbcpDataSourceFactory.java

Posted by Remy Maucherat <rm...@home.com>.
> > On Mon, 29 Oct 2001, Remy Maucherat wrote:
> >
> > > In the HEAD branch, we're supposed to be rebinding the object returned
> by
> > > the factory. I didn't try if it was actually doing it, though.
> > >
> >
> > Where is the code that is supposed to do the rebind?
>
> Since it's a memory context, it's very easy to do the rebind, so it's easy
> to miss in the code.
>
> In MemoryContext :
>
> from line 832:
> ...
>             } else if (entry.type == NamingEntry.REFERENCE) {
>                 try {
>                     Object obj = NamingManager.getObjectInstance
>                         (entry.value, name, this, env);
>                     if (obj != null) {
>                         entry.value = obj;
>                     }
>                     return obj;
>                 } catch (Exception e) {
>                     throw new NamingException(e.getMessage());
>                 }
>             } else {
> ...
>
> The entry.value = obj is supposed to do the trick, since the factory won't
> return another reference. Next time, the same instance will be returned.

Now that I think about it, the entry type also needs to be changed, and it
seems I forgot to do it.
Ooooops ...

Remy


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/factory DbcpDataSourceFactory.java

Posted by Remy Maucherat <rm...@home.com>.
> On Mon, 29 Oct 2001, Remy Maucherat wrote:
>
> > In the HEAD branch, we're supposed to be rebinding the object returned
by
> > the factory. I didn't try if it was actually doing it, though.
> >
>
> Where is the code that is supposed to do the rebind?

Since it's a memory context, it's very easy to do the rebind, so it's easy
to miss in the code.

In MemoryContext :

from line 832:
...
            } else if (entry.type == NamingEntry.REFERENCE) {
                try {
                    Object obj = NamingManager.getObjectInstance
                        (entry.value, name, this, env);
                    if (obj != null) {
                        entry.value = obj;
                    }
                    return obj;
                } catch (Exception e) {
                    throw new NamingException(e.getMessage());
                }
            } else {
...

The entry.value = obj is supposed to do the trick, since the factory won't
return another reference. Next time, the same instance will be returned.

Remy


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/factory DbcpDataSourceFactory.java

Posted by "Craig R. McClanahan" <cr...@apache.org>.

On Mon, 29 Oct 2001, Remy Maucherat wrote:

> Date: Mon, 29 Oct 2001 19:46:15 -0800
> From: Remy Maucherat <rm...@home.com>
> Reply-To: Tomcat Developers List <to...@jakarta.apache.org>
> To: Tomcat Developers List <to...@jakarta.apache.org>
> Subject: Re: cvs commit:
>     jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/factory
>     DbcpDataSourceFactory.java
>
> > craigmcc    01/10/29 19:26:51
> >
> >   Modified:    catalina build.xml
> >   Added:       catalina/src/share/org/apache/naming/factory
> >                         DbcpDataSourceFactory.java
> >   Log:
> >   Add an experimental JDBC data source factory based on the commons-dbcp
> and
> >   commons-pool pacakges.
> >
> >   FIXME - Like the Tyrex factory, doesn't save the previous pool.  Needs
> to
> >   be fixed!
>
> In the HEAD branch, we're supposed to be rebinding the object returned by
> the factory. I didn't try if it was actually doing it, though.
>

Where is the code that is supposed to do the rebind?

> Remy
>

Craig


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/naming/factory DbcpDataSourceFactory.java

Posted by Remy Maucherat <rm...@home.com>.
> craigmcc    01/10/29 19:26:51
>
>   Modified:    catalina build.xml
>   Added:       catalina/src/share/org/apache/naming/factory
>                         DbcpDataSourceFactory.java
>   Log:
>   Add an experimental JDBC data source factory based on the commons-dbcp
and
>   commons-pool pacakges.
>
>   FIXME - Like the Tyrex factory, doesn't save the previous pool.  Needs
to
>   be fixed!

In the HEAD branch, we're supposed to be rebinding the object returned by
the factory. I didn't try if it was actually doing it, though.

Remy


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>