View Javadoc

1   /************************************************************************
2    * Copyright (c) 2000-2006 The Apache Software Foundation.             *
3    * All rights reserved.                                                *
4    * ------------------------------------------------------------------- *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you *
6    * may not use this file except in compliance with the License. You    *
7    * may obtain a copy of the License at:                                *
8    *                                                                     *
9    *     http://www.apache.org/licenses/LICENSE-2.0                      *
10   *                                                                     *
11   * Unless required by applicable law or agreed to in writing, software *
12   * distributed under the License is distributed on an "AS IS" BASIS,   *
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
14   * implied.  See the License for the specific language governing       *
15   * permissions and limitations under the License.                      *
16   ***********************************************************************/
17  
18  package org.apache.james.util.dbcp;
19  
20  import org.apache.avalon.excalibur.datasource.DataSourceComponent;
21  import org.apache.avalon.framework.activity.Disposable;
22  import org.apache.avalon.framework.configuration.Configurable;
23  import org.apache.avalon.framework.configuration.Configuration;
24  import org.apache.avalon.framework.configuration.ConfigurationException;
25  import org.apache.avalon.framework.logger.AbstractLogEnabled;
26  import org.apache.commons.dbcp.BasicDataSource;
27  
28  import java.io.PrintWriter;
29  import java.sql.Connection;
30  import java.sql.SQLException;
31  
32  /***
33   * <p>
34   * This is a reliable DataSource implementation, based on the pooling logic provided by <a
35   * href="http://jakarta.apache.org/commons/dbcp.html">DBCP</a> and the configuration found in
36   * Avalon's excalibur code.
37   * </p>
38   *
39   * <p>
40   * This uses the normal <code>java.sql.Connection</code> object and
41   * <code>java.sql.DriverManager</code>.  The Configuration is like this:
42   * <pre>
43   *   &lt;jdbc&gt;
44  
45   *     &lt;pool-controller min="<i>5</i>" max="<i>10</i>" connection-class="<i>my.overrided.ConnectionClass</i>"&gt;
46   *       &lt;keep-alive&gt;select 1&lt;/keep-alive&gt;
47   *     &lt;/pool-controller&gt;
48  
49   *     &lt;driver&gt;<i>com.database.jdbc.JdbcDriver</i>&lt;/driver&gt;
50   *     &lt;dburl&gt;<i>jdbc:driver://host/mydb</i>&lt;/dburl&gt;
51   *     &lt;user&gt;<i>username</i>&lt;/user&gt;
52   *     &lt;password&gt;<i>password</i>&lt;/password&gt;
53   *   &lt;/jdbc&gt;
54   * </pre>
55   * </p>
56   * <p>
57   * These configuration settings are available:
58   * <ul>
59   * <li><b>driver</b> - The class name of the JDBC driver</li>
60   * <li><b>dburl</b> - The JDBC URL for this connection</li>
61   * <li><b>user</b> - The username to use for this connection</li>
62   * <li><b>password</b> - The password to use for this connection</li>
63   * <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>
64   * <li><b>max</b> - The maximum number of active connections allowed in the pool. 0 means no limit. (default 2)</li>
65   * <li><b>max_idle</b> - The maximum number of idle connections.  0 means no limit.  (default 0)</li>
66   * <li><b>initial_size</b> -  The initial number of connections that are created when the pool is started. (default 0)</li>
67   * <li><b>min_idle</b> -  The minimum number of active connections that can remain idle in the pool, without extra ones being created, or zero to create none. (default 0)</li>
68   * <li><b>max_wait</b> -  The maximum number of milliseconds that the pool will wait (when there are no available connections) for a connection to be returned before throwing an exception, or -1 to wait indefinitely. (default -1)</li>
69   * <li><b>testOnBorrow</b> -  The indication of whether objects will be validated before being borrowed from the pool. If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another.  (default true)</li>
70   * <li><b>testOnReturn</b> -  The indication of whether objects will be validated before being returned to the pool. (default false)</li>
71   * <li><b>testWhileIdle</b> -  The indication of whether objects will be validated by the idle object evictor (if any). If an object fails to validate, it will be dropped from the pool. (default false)</li>
72   * <li><b>timeBetweenEvictionRunsMillis</b> -  The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive, no idle object evictor thread will be run. (default -1)</li>
73   * <li><b>numTestsPerEvictionRun</b> -  The number of objects to examine during each run of the idle object evictor thread (if any). (default 3)</li>
74   * <li><b>minEvictableIdleTimeMillis</b> -  The minimum amount of time an object may sit idle in the pool before it is eligable for eviction by the idle object evictor (if any). (default 1000 * 60 * 30)</li>
75   * </ul>
76   *
77   * @version CVS $Revision: 381990 $
78   */
79  public class JdbcDataSource extends AbstractLogEnabled
80      implements Configurable,
81                 Disposable,
82                 DataSourceComponent {
83  
84      BasicDataSource source = null;
85      //Jdbc2PoolDataSource source = null;
86      //PoolingDataSource source = null;
87  
88      /***
89       * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
90       */
91      public void configure(final Configuration configuration)
92                     throws ConfigurationException {
93          //Configure the DBCP
94          try {
95              String driver = configuration.getChild("driver").getValue(null);
96              Class.forName(driver);
97  
98              String dburl = configuration.getChild("dburl").getValue(null);
99              String user = configuration.getChild("user").getValue(null);
100             String password = configuration.getChild("password").getValue(null);
101 
102             // This inner class extends DBCP's BasicDataSource, and
103             // turns on validation (using Connection.isClosed()), so
104             // that the pool can recover from a server outage.
105             source = new BasicDataSource() {
106                 protected synchronized javax.sql.DataSource createDataSource()
107                         throws SQLException {
108                     if (dataSource != null) {
109                         return (dataSource);
110                     } else {
111                         javax.sql.DataSource ds = super.createDataSource();
112                         connectionPool.setTestOnBorrow(true);
113                         connectionPool.setTestOnReturn(true);
114                         return ds;
115                     }
116                 }
117             };
118 
119             source.setDriverClassName(driver);
120             source.setUrl(dburl);
121             source.setUsername(user);
122             source.setPassword(password);
123             source.setMaxActive(configuration.getChild("max").getValueAsInteger(2));
124             source.setMaxIdle(configuration.getChild("max_idle").getValueAsInteger(0));
125             source.setInitialSize(configuration.getChild("initial_size").getValueAsInteger(0));
126             source.setMinIdle(configuration.getChild("min_idle").getValueAsInteger(0));
127             //This is necessary, otherwise a connection could hang forever
128             source.setMaxWait(configuration.getChild("max_wait").getValueAsInteger(5000));
129             source.setValidationQuery(configuration.getChild("keep-alive").getValue(null));
130             source.setTestOnBorrow(configuration.getChild("testOnBorrow").getValueAsBoolean(true));
131             source.setTestOnReturn(configuration.getChild("testOnReturn").getValueAsBoolean(false));
132             source.setTestWhileIdle(configuration.getChild("testWhileIdle").getValueAsBoolean(false));
133             source.setTimeBetweenEvictionRunsMillis(configuration.getChild("timeBetweenEvictionRunsMillis").getValueAsInteger(-1));
134             source.setNumTestsPerEvictionRun(configuration.getChild("numTestsPerEvictionRun").getValueAsInteger(3));
135             source.setMinEvictableIdleTimeMillis(configuration.getChild("minEvictableIdleTimeMillis").getValueAsInteger(1000 * 30 * 60));
136 
137             //Unsupported
138             //source.setLoginTimeout(configuration.getChild("login_timeout").getValueAsInteger(0));
139 
140 
141             // DBCP uses a PrintWriter approach to logging.  This
142             // Writer class will bridge between DBCP and Avalon
143             // Logging. Unfortunately, DBCP 1.0 is clueless about the
144             // concept of a log level.
145             final java.io.Writer writer = new java.io.CharArrayWriter() {
146                 public void flush() {
147                     // flush the stream to the log
148                     if (JdbcDataSource.this.getLogger().isErrorEnabled()) {
149                         JdbcDataSource.this.getLogger().error(toString());
150                     }
151                     reset();    // reset the contents for the next message
152                 }
153             };
154 
155             source.setLogWriter(new PrintWriter(writer, true));
156 
157             // Extra debug for first cut
158             getLogger().debug("max wait: " + source.getMaxWait());
159             getLogger().debug("max idle: " + source.getMaxIdle());
160             getLogger().debug("max active: " + source.getMaxActive());
161             getLogger().debug("initial size: " + source.getInitialSize());
162             getLogger().debug("TestOnBorrow: " + source.getTestOnBorrow());
163             getLogger().debug("TestOnReturn: " + source.getTestOnReturn());
164             getLogger().debug("TestWhileIdle: " + source.getTestWhileIdle());
165             getLogger().debug("NumTestsPerEvictionRun(): " + source.getNumTestsPerEvictionRun());
166             getLogger().debug("MinEvictableIdleTimeMillis(): " + source.getMinEvictableIdleTimeMillis());
167             getLogger().debug("TimeBetweenEvictionRunsMillis(): " + source.getTimeBetweenEvictionRunsMillis());
168 
169             /*
170             //Another sample that doesn't work
171             GenericObjectPool connectionPool = new GenericObjectPool(null);
172             ConnectionFactory connectionFactory =
173                     new DriverManagerConnectionFactory(dburl, user, password);
174             PoolableConnectionFactory poolableConnectionFactory =
175                     new PoolableConnectionFactory(connectionFactory, connectionPool, null, null, false, true);
176             PoolingDataSource dataSource = new PoolingDataSource(connectionPool);
177             source = dataSource;
178             */
179 
180             /*
181              As documented on the DBCP website, which is wrong
182             DriverAdapterCPDS cpds = new DriverAdapterCPDS();
183             cpds.setDriver(configuration.getChild("driver").getValue(null));
184             cpds.setUrl(configuration.getChild("dburl").getValue(null));
185             cpds.setUsername(configuration.getChild("user").getValue(null));
186             cpds.setPassword(configuration.getChild("password").getValue(null));
187 
188             source = new Jdbc2PoolDataSource();
189             source.setConnectionPoolDataSource(cpds);
190             source.setDefaultMaxActive(10);
191             source.setDefaultMaxWait(50);
192             */
193 
194 
195             //Get a connection and close it, just to test that we connected.
196             source.getConnection().close();
197         } catch (Exception e) {
198             throw new ConfigurationException("Error configurable datasource", e);
199         }
200     }
201 
202     /***
203      * @see org.apache.avalon.framework.configuration.Configurable#dispose()
204      */
205     public void dispose() {
206         //Close all database connections
207         try {
208             source.close();
209         } catch (SQLException sqle) {
210             sqle.printStackTrace();
211         }
212     }
213 
214     /***
215      *
216      */
217     public Connection getConnection() throws SQLException {
218         return source.getConnection();
219     }
220 }