Attached you will find a org.apache.james.util.dbcp.JdbcDataSource with more logging.

Don't forget about the thread dumps when you get the failures.

-- Dirk


Noel J. Bergman wrote:
Dirk verbeeck wrote:

Chris Means wrote:

The update for DBCP didn't fix the problem.


Bother ... worked all that time, and now failing again?


I have restarted it, and turned on DEBUG for: spoolmanager,
pop3server, smtpserver, and mailstore.


That might not help, but we'll see.  We'd want some detailed accounting from
the connection pool when it is exhausted, so that we could see how is
holding the connections.


are the blocking threads (threaddumps?)
If we suspect a leak then ds.getNumActive() & ds.getNumIdle() should
be printed (exception logging in JdbcDataSource.getConnection).
If the numbers are correct (active=max and idle=0) then the DBCP
abandoned connection tracing should be enabled to trace where the
connection are allocated.


FWIW, the James code that uses this is:

http://cvs.apache.org/viewcvs.cgi/james-server/src/java/org/apache/james/uti
l/dbcp/JdbcDataSource.java?annotate=1.1.2.3&only_with_tag=branch_2_1_fcs

Any suggestions on instrumentation?  A patch would be welcomed.  At the
moment, I'm rather rushed for time, and don't have time to examine the issue
in more detail immediately.  We have our own adapter for the
BasicDataSource, so adding the instrumentation should be fairly straight
foward.

        --- Noel



/***********************************************************************
 * Copyright (c) 2000-2004 The Apache Software Foundation.             *
 * All rights reserved.                                                *
 * ------------------------------------------------------------------- *
 * Licensed under the Apache License, Version 2.0 (the "License"); you *
 * may not use this file except in compliance with the License. You    *
 * may obtain a copy of the License at:                                *
 *                                                                     *
 *     http://www.apache.org/licenses/LICENSE-2.0                      *
 *                                                                     *
 * Unless required by applicable law or agreed to in writing, software *
 * distributed under the License is distributed on an "AS IS" BASIS,   *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
 * implied.  See the License for the specific language governing       *
 * permissions and limitations under the License.                      *
 ***********************************************************************/

package org.apache.james.util.dbcp;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;

import org.apache.avalon.excalibur.datasource.DataSourceComponent;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.commons.dbcp.BasicDataSource;

/**
 * <p>
 * This is a reliable DataSource implementation, based on the pooling logic provided 
by <a
 * href="http://jakarta.apache.org/commons/dbcp/";>DBCP</a> and the configuration found 
in
 * Avalon's excalibur code.
 * </p>
 *
 * <p>
 * This uses the normal <code>java.sql.Connection</code> object and
 * <code>java.sql.DriverManager</code>.  The Configuration is like this:
 * <pre>
 *   &lt;jdbc&gt;
 *     &lt;pool-controller min="<i>5</i>" max="<i>10</i>" 
connection-class="<i>my.overrided.ConnectionClass</i>"&gt;
 *       &lt;keep-alive&gt;select 1&lt;/keep-alive&gt;
 *     &lt;/pool-controller&gt;
 *     &lt;driver&gt;<i>com.database.jdbc.JdbcDriver</i>&lt;/driver&gt;
 *     &lt;dburl&gt;<i>jdbc:driver://host/mydb</i>&lt;/dburl&gt;
 *     &lt;user&gt;<i>username</i>&lt;/user&gt;
 *     &lt;password&gt;<i>password</i>&lt;/password&gt;
 *   &lt;/jdbc&gt;
 * </pre>
 * </p>
 * <p>
 * These configuration settings are available:
 * <ul>
 * <li><b>driver</b> - The class name of the JDBC driver</li>
 * <li><b>dburl</b> - The JDBC URL for this connection</li>
 * <li><b>user</b> - The username to use for this connection</li>
 * <li><b>password</b> - The password to use for this connection</li>
 * <li><b>keep-alive</b> - The SQL query that will be used to validate connections 
from this pool before returning them to the caller.  If specified, this query 
<strong>MUST</strong> be an SQL SELECT statement that returns at least one row.</li>
 * <li><b>max</b> - The maximum number of active connections allowed in the pool. 0 
means no limit. (default 2)</li>
 * <li><b>max_idle</b> - The maximum number of idle connections.  0 means no limit.  
(default 0)</li>
 * </ul>
 *
 * @version CVS $Revision: 1.1.2.3 $
 */
public class JdbcDataSource extends AbstractLogEnabled
    implements Configurable,
               Disposable,
               DataSourceComponent {

    BasicDataSource source = null;

    /**
     * @see 
org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
     */
    public void configure(final Configuration configuration)
                   throws ConfigurationException {
        //Configure the DBCP
        try {
            
            String driver = configuration.getChild("driver").getValue(null);
            Class.forName(driver);

            String dburl = configuration.getChild("dburl").getValue(null);
            String user = configuration.getChild("user").getValue(null);
            String password = configuration.getChild("password").getValue(null);

            source = new BasicDataSource();

            // Turn on validation, so
            // that the pool can recover from a server outage.
            source.setTestOnBorrow(true);
            source.setTestOnReturn(true);

            source.setDriverClassName(driver);
            source.setUrl(dburl);
            source.setUsername(user);
            source.setPassword(password);
            source.setMaxActive(configuration.getChild("max").getValueAsInteger(2));
            source.setMaxIdle(configuration.getChild("max_idle").getValueAsInteger(0));
            
source.setValidationQuery(configuration.getChild("keep-alive").getValue(null));
            //Unsupported
            
//source.setLoginTimeout(configuration.getChild("login_timeout").getValueAsInteger(0));

            //This is necessary, otherwise a connection could hang forever
            
source.setMaxWait(configuration.getChild("max_wait").getValueAsInteger(5000));
            
            
source.setRemoveAbandoned(configuration.getChild("removeAbandoned").getValueAsBoolean(source.getRemoveAbandoned()));
            
source.setRemoveAbandonedTimeout(configuration.getChild("removeAbandonedTimeout").getValueAsInteger(source.getRemoveAbandonedTimeout()));
            
source.setLogAbandoned(configuration.getChild("logAbandoned").getValueAsBoolean(source.getLogAbandoned()));

            // DBCP uses a PrintWriter approach to logging.  This
            // Writer class will bridge between DBCP and Avalon
            // Logging. Unfortunately, DBCP 1.0 is clueless about the
            // concept of a log level.
            final java.io.Writer writer = new java.io.CharArrayWriter() {
                public void flush() {
                    // flush the stream to the log
                    if (JdbcDataSource.this.getLogger().isErrorEnabled()) {
                        JdbcDataSource.this.getLogger().error(toString());
                    }
                    reset();    // reset the contents for the next message
                }
            };

            source.setLogWriter(new PrintWriter(writer, true));

            // Extra debug
            if (getLogger().isDebugEnabled()) {
                getLogger().debug("MaxWait: " + source.getMaxWait());
                getLogger().debug("MaxIdle: " + source.getMaxIdle());
                getLogger().debug("MaxActive: " + source.getMaxActive());
                getLogger().debug("RemoveAbandoned: " + source.getRemoveAbandoned());
                getLogger().debug("RemoveAbandonedTimeout: " + 
source.getRemoveAbandonedTimeout());
                getLogger().debug("LogAbandoned: " + source.getLogAbandoned());
                getLogger().debug("TestOnBorrow:" + source.getTestOnBorrow());
                getLogger().debug("TestOnReturn:" + source.getTestOnReturn());
                getLogger().debug("TestWhileIdle:" + source.getTestWhileIdle());
                getLogger().debug("NumTestsPerEvictionRun: " + 
source.getNumTestsPerEvictionRun());
                getLogger().debug("MinEvictableIdleTimeMillis: " + 
source.getMinEvictableIdleTimeMillis());
                getLogger().debug("TimeBetweenEvictionRunsMillis: " + 
source.getTimeBetweenEvictionRunsMillis());
            }
            
            //Get a connection and close it, just to test that we connected.
            source.getConnection().close();
        } catch (Exception e) {
            getLogger().error("Error configurable datasource", e);
            throw new ConfigurationException("Error configurable datasource", e);
        }
    }

    /**
     * @see org.apache.avalon.framework.configuration.Configurable#dispose()
     */
    public void dispose() {
        //Close all database connections
        try {
            source.close();
        } catch (SQLException sqle) {
            getLogger().error("Error disposing datasource", sqle);
        }
    }

    public Connection getConnection() throws SQLException {
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("getConnection()" 
                + "   NumActive: " + source.getNumActive()
                + "   NumIdle: " + source.getNumIdle());
        }
        try {
            return source.getConnection();
        }
        catch (SQLException e) {
            getLogger().error("Error during getConnection()", e);
            getLogger().error("Pool stats" 
                + "   NumActive: " + source.getNumActive()
                + "   MaxActive: " + source.getMaxActive()
                + "   NumIdle: " + source.getNumIdle()
                + "   MaxIdle: " + source.getMaxIdle());
            throw e;
        }
    }
}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to