001: /*
002: * ====================================================================
003: * JAFFA - Java Application Framework For All
004: *
005: * Copyright (C) 2002 JAFFA Development Group
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Redistribution and use of this software and associated documentation ("Software"),
022: * with or without modification, are permitted provided that the following conditions are met:
023: * 1. Redistributions of source code must retain copyright statements and notices.
024: * Redistributions must also contain a copy of this document.
025: * 2. Redistributions in binary form must reproduce the above copyright notice,
026: * this list of conditions and the following disclaimer in the documentation
027: * and/or other materials provided with the distribution.
028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
029: * this Software without prior written permission. For written permission,
030: * please contact mail to: jaffagroup@yahoo.com.
031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
032: * appear in their names without prior written permission.
033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
034: *
035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: */
049:
050: package org.jaffa.persistence.engines.jdbcengine.datasource;
051:
052: import java.io.IOException;
053: import java.sql.Connection;
054: import java.sql.SQLException;
055: import javax.sql.DataSource;
056: import org.apache.log4j.Logger;
057: import org.apache.commons.pool.KeyedObjectPoolFactory;
058: import org.apache.commons.pool.ObjectPool;
059: import org.apache.commons.pool.impl.GenericObjectPool;
060: import org.apache.commons.pool.impl.StackKeyedObjectPoolFactory;
061: import org.apache.commons.dbcp.ConnectionFactory;
062: import org.apache.commons.dbcp.PoolingDataSource;
063: import org.apache.commons.dbcp.PoolableConnectionFactory;
064: import org.apache.commons.dbcp.DriverManagerConnectionFactory;
065:
066: /** This is a Dbcp DataSource based implementation of IConnectionFactory.
067: *
068: * It requires commons-collections.jar, commons-pool.jar, commons-dbcp.jar, j2ee.jar (for the javax.sql classes), the classes for your (underlying) JDBC driver in the classpath.
069: * @author GautamJ
070: */
071: public class DbcpDataSourceConnectionFactory implements
072: IConnectionFactory {
073:
074: private static final Logger log = Logger
075: .getLogger(DbcpDataSourceConnectionFactory.class);
076:
077: // This is the number of times the createConnection will try to acquire a connection from the pool
078: private static final int RETRY_LIMIT = 10;
079:
080: // The default wait time, between connection retries, in milliseconds
081: private static final long DEFAULT_WAIT_TIME = 2000;
082:
083: // the DataSource used for pooling connections
084: private static DataSource c_dataSource = null;
085:
086: // **************************************
087: // Properties for this Connection Factory
088: // **************************************
089: private String m_driverClass;
090: private String m_url;
091: private String m_user;
092: private String m_password;
093: private Integer m_minimumConnections;
094: private Integer m_maximumConnections;
095: private Long m_maxWait;
096: private Boolean m_testOnBorrow;
097: private Boolean m_testOnReturn;
098: private String m_validationQuery;
099:
100: /** Getter for property driverClass.
101: * @return Value of property driverClass.
102: */
103: public String getDriverClass() {
104: return m_driverClass;
105: }
106:
107: /** Setter for property driverClass.
108: * @param driverClass New value of property driverClass.
109: */
110: public void setDriverClass(String driverClass) {
111: if (driverClass == null || driverClass.length() == 0)
112: m_driverClass = null;
113: else
114: m_driverClass = driverClass;
115: }
116:
117: /** Getter for property url.
118: * @return Value of property url.
119: */
120: public String getUrl() {
121: return m_url;
122: }
123:
124: /** Setter for property url.
125: * @param url New value of property url.
126: */
127: public void setUrl(String url) {
128: if (url == null || url.length() == 0)
129: m_url = null;
130: else
131: m_url = url;
132: }
133:
134: /** Getter for property user.
135: * @return Value of property user.
136: */
137: public String getUser() {
138: return m_user;
139: }
140:
141: /** Setter for property user.
142: * @param user New value of property user.
143: */
144: public void setUser(String user) {
145: if (user == null || user.length() == 0)
146: m_user = null;
147: else
148: m_user = user;
149: }
150:
151: /** Getter for property password.
152: * @return Value of property password.
153: */
154: public String getPassword() {
155: return m_password;
156: }
157:
158: /** Setter for property password.
159: * @param password New value of property password.
160: */
161: public void setPassword(String password) {
162: if (password == null || password.length() == 0)
163: m_password = null;
164: else
165: m_password = password;
166: }
167:
168: /** Getter for property minimumConnections.
169: * @return Value of property minimumConnections.
170: */
171: public Integer getMinimumConnections() {
172: return m_minimumConnections;
173: }
174:
175: /** Setter for property minimumConnections.
176: * @param minimumConnections New value of property minimumConnections.
177: */
178: public void setMinimumConnections(Integer minimumConnections) {
179: m_minimumConnections = minimumConnections;
180: }
181:
182: /** Getter for property maximumConnections.
183: * @return Value of property maximumConnections.
184: */
185: public Integer getMaximumConnections() {
186: return m_maximumConnections;
187: }
188:
189: /** Setter for property maximumConnections.
190: * @param maximumConnections New value of property maximumConnections.
191: */
192: public void setMaximumConnections(Integer maximumConnections) {
193: m_maximumConnections = maximumConnections;
194: }
195:
196: /** Getter for property maxWait.
197: * @return Value of property maxWait.
198: *
199: */
200: public Long getMaxWait() {
201: return m_maxWait;
202: }
203:
204: /** Setter for property maxWait.
205: * @param maxWait New value of property maxWait.
206: *
207: */
208: public void setMaxWait(Long maxWait) {
209: m_maxWait = maxWait;
210: }
211:
212: /** Getter for property testOnBorrow.
213: * @return Value of property testOnBorrow.
214: *
215: */
216: public Boolean getTestOnBorrow() {
217: return m_testOnBorrow;
218: }
219:
220: /** Setter for property testOnBorrow.
221: * @param testOnBorrow New value of property testOnBorrow.
222: *
223: */
224: public void setTestOnBorrow(Boolean testOnBorrow) {
225: m_testOnBorrow = testOnBorrow;
226: }
227:
228: /** Getter for property testOnReturn.
229: * @return Value of property testOnReturn.
230: *
231: */
232: public Boolean getTestOnReturn() {
233: return m_testOnReturn;
234: }
235:
236: /** Setter for property testOnReturn.
237: * @param testOnReturn New value of property testOnReturn.
238: *
239: */
240: public void setTestOnReturn(Boolean testOnReturn) {
241: m_testOnReturn = testOnReturn;
242: }
243:
244: /** Getter for property validationQuery.
245: * @return Value of property validationQuery.
246: *
247: */
248: public String getValidationQuery() {
249: return m_validationQuery;
250: }
251:
252: /** Setter for property validationQuery.
253: * @param validationQuery New value of property validationQuery.
254: *
255: */
256: public void setValidationQuery(String validationQuery) {
257: if (validationQuery == null || validationQuery.length() == 0)
258: m_validationQuery = null;
259: else
260: m_validationQuery = validationQuery;
261: }
262:
263: // **************************************
264: // Implementation methods
265: // **************************************
266:
267: /** Creates a connection using Dbcp DataSource implementation.
268: * @throws SQLException if any SQL error occurs
269: * @throws IOException if any IO error occurs
270: * @return a Connection
271: */
272: public Connection createConnection() throws SQLException,
273: IOException {
274: if (c_dataSource == null)
275: createDbcpDataSource();
276:
277: Connection connection = null;
278: for (int i = 0; i < RETRY_LIMIT; i++) {
279: try {
280: connection = c_dataSource.getConnection();
281: } catch (Exception e) {
282: // do nothing
283: }
284: if (connection != null)
285: break;
286:
287: if (log.isDebugEnabled())
288: log
289: .debug((i + 1)
290: + " : Could not acquire Connection from Dbcp DataSource. Will try again.");
291:
292: try {
293: Thread.sleep(getMaxWait() != null ? getMaxWait()
294: .longValue() : DEFAULT_WAIT_TIME);
295: } catch (Exception e) {
296: // do nothing
297: }
298: }
299:
300: if (connection == null) {
301: String str = "Could not acquire a connection from Dbcp DataSource even after "
302: + RETRY_LIMIT + " tries";
303: log.error(str);
304: throw new SQLException(str);
305: }
306:
307: if (log.isDebugEnabled())
308: log
309: .debug("Acquired a pooled Connection from DbcpDataSource: "
310: + connection);
311:
312: return connection;
313: }
314:
315: /** Returns the connection back to the pool.
316: * @param connection The connection to return back to the pool.
317: * @throws SQLException if any SQL error occurs
318: * @throws IOException if any IO error occurs
319: */
320: public void freeConnection(Connection connection)
321: throws SQLException, IOException {
322: if (log.isDebugEnabled())
323: log
324: .debug("Freeing up the pooled Connection back to DbcpDataSource: "
325: + connection);
326: connection.close();
327: }
328:
329: /** Returns a true if the Prepared (and Call) Statements are to be cached for a connection.
330: * This will return a false. It uses the StackKeyedObjectPoolFactory for creating a pool for caching the PreparedStatements.
331: * @return a true if the Prepared (and Call) Statements are to be cached for a connection.
332: */
333: public boolean usePreparedStatementCaching() {
334: return false;
335: }
336:
337: private synchronized void createDbcpDataSource()
338: throws SQLException {
339: try {
340: if (c_dataSource == null) {
341: // First we load the underlying JDBC driver.
342: Class.forName(getDriverClass());
343:
344: // Next, we'll need a ObjectPool that serves as the actual pool of connections.
345: // We'll use a GenericObjectPool instance
346: GenericObjectPool connectionPool = new GenericObjectPool(
347: null);
348: if (getMaximumConnections() != null)
349: connectionPool.setMaxActive(getMaximumConnections()
350: .intValue());
351: if (getMinimumConnections() != null)
352: connectionPool.setMaxIdle(getMinimumConnections()
353: .intValue());
354: if (getMaxWait() != null)
355: connectionPool.setMaxWait(getMaxWait().longValue());
356: if (getTestOnBorrow() != null)
357: connectionPool.setTestOnBorrow(getTestOnBorrow()
358: .booleanValue());
359: if (getTestOnReturn() != null)
360: connectionPool.setTestOnReturn(getTestOnReturn()
361: .booleanValue());
362:
363: // Next, we'll create a ConnectionFactory that the pool will use to create Connections.
364: // We'll use the DriverManagerConnectionFactory
365: ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
366: getUrl(), getUser(), getPassword());
367:
368: // Now we'll create the PoolableConnectionFactory, which wraps the "real" Connections created by the ConnectionFactory with the classes that implement the pooling functionality.
369: KeyedObjectPoolFactory stmtPoolFactory = new StackKeyedObjectPoolFactory();
370: String validationQuery = getValidationQuery();
371: boolean defaultReadOnly = false;
372: boolean defaultAutoCommit = false;
373: PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(
374: connectionFactory, connectionPool,
375: stmtPoolFactory, validationQuery,
376: defaultReadOnly, defaultAutoCommit);
377:
378: // Finally, we create the PoolingDriver itself, passing in the object pool we created.
379: c_dataSource = new PoolingDataSource(connectionPool);
380:
381: // This will allow us to access the underlying Connection objects, required by the JdbcSecurityPlugin
382: ((PoolingDataSource) c_dataSource)
383: .setAccessToUnderlyingConnectionAllowed(true);
384:
385: if (log.isDebugEnabled())
386: log.debug("Created the Dbcp DataSource");
387: }
388: } catch (Exception e) {
389: String str = "Error in creating the Dbcp DataSource";
390: log.error(str, e);
391: throw new SQLException(str);
392: }
393: }
394:
395: }
|