View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You 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 implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.dbcp;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.sql.CallableStatement;
23  import java.sql.Connection;
24  import java.sql.DatabaseMetaData;
25  import java.sql.Driver;
26  import java.sql.DriverManager;
27  import java.sql.DriverPropertyInfo;
28  import java.sql.PreparedStatement;
29  import java.sql.SQLException;
30  import java.sql.SQLWarning;
31  import java.sql.Statement;
32  import java.util.HashMap;
33  import java.util.Map;
34  import java.util.NoSuchElementException;
35  import java.util.Properties;
36  import java.util.Set;
37  
38  import org.apache.commons.jocl.JOCLContentHandler;
39  import org.apache.commons.pool.ObjectPool;
40  import org.xml.sax.SAXException;
41  
42  
43  /**
44   * A {@link Driver} implementation that obtains
45   * {@link Connection}s from a registered
46   * {@link ObjectPool}.
47   *
48   * @author Rodney Waldhoff
49   * @author Dirk Verbeeck
50   * @version $Revision: 1023401 $ $Date: 2010-10-16 21:54:24 -0400 (Sat, 16 Oct 2010) $
51   */
52  public class PoolingDriver implements Driver {
53      /** Register myself with the {@link DriverManager}. */
54      static {
55          try {
56              DriverManager.registerDriver(new PoolingDriver());
57          } catch(Exception e) {
58          }
59      }
60  
61      /** The map of registered pools. */
62      protected static final HashMap _pools = new HashMap();
63  
64      /** Controls access to the underlying connection */
65      private static boolean accessToUnderlyingConnectionAllowed = false; 
66  
67      public PoolingDriver() {
68      }
69  
70      /**
71       * Returns the value of the accessToUnderlyingConnectionAllowed property.
72       * 
73       * @return true if access to the underlying is allowed, false otherwise.
74       */
75      public static synchronized boolean isAccessToUnderlyingConnectionAllowed() {
76          return accessToUnderlyingConnectionAllowed;
77      }
78  
79      /**
80       * Sets the value of the accessToUnderlyingConnectionAllowed property.
81       * It controls if the PoolGuard allows access to the underlying connection.
82       * (Default: false)
83       * 
84       * @param allow Access to the underlying connection is granted when true.
85       */
86      public static synchronized void setAccessToUnderlyingConnectionAllowed(boolean allow) {
87          accessToUnderlyingConnectionAllowed = allow;
88      }
89  
90      /**
91       * WARNING: This method throws DbcpExceptions (RuntimeExceptions)
92       * and will be replaced by the protected getConnectionPool method.
93       * 
94       * @deprecated This will be removed in a future version of DBCP.
95       */
96      public synchronized ObjectPool getPool(String name) {
97          try {
98              return getConnectionPool(name);
99          }
100         catch (Exception e) {
101             throw new DbcpException(e);
102         }
103     }
104     
105     public synchronized ObjectPool getConnectionPool(String name) throws SQLException {
106         ObjectPool pool = (ObjectPool)(_pools.get(name));
107         if(null == pool) {
108             InputStream in = this.getClass().getResourceAsStream(String.valueOf(name) + ".jocl");
109             if (in == null) {
110                 in = Thread.currentThread().getContextClassLoader(
111                         ).getResourceAsStream(String.valueOf(name) + ".jocl");
112             }
113             if(null != in) {
114                 JOCLContentHandler jocl = null;
115                 try {
116                     jocl = JOCLContentHandler.parse(in);
117                 }
118                 catch (SAXException e) {
119                     throw (SQLException) new SQLException("Could not parse configuration file").initCause(e);
120                 }
121                 catch (IOException e) {
122                     throw (SQLException) new SQLException("Could not load configuration file").initCause(e);
123                 }
124                 if(jocl.getType(0).equals(String.class)) {
125                     pool = getPool((String)(jocl.getValue(0)));
126                     if(null != pool) {
127                         registerPool(name,pool);
128                     }
129                 } else {
130                     pool = ((PoolableConnectionFactory)(jocl.getValue(0))).getPool();
131                     if(null != pool) {
132                         registerPool(name,pool);
133                     }
134                 }
135             }
136             else {
137                 throw new SQLException("Configuration file not found");
138             }
139         }
140         return pool;
141     }
142 
143     public synchronized void registerPool(String name, ObjectPool pool) {
144         _pools.put(name,pool);
145     }
146 
147     public synchronized void closePool(String name) throws SQLException {
148         ObjectPool pool = (ObjectPool) _pools.get(name);
149         if (pool != null) {
150             _pools.remove(name);
151             try {
152                 pool.close();
153             }
154             catch (Exception e) {
155                 throw (SQLException) new SQLException("Error closing pool " + name).initCause(e);
156             }
157         }
158     }
159     
160     public synchronized String[] getPoolNames(){
161         Set names = _pools.keySet();
162         return (String[]) names.toArray(new String[names.size()]);
163     }
164 
165     public boolean acceptsURL(String url) throws SQLException {
166         try {
167             return url.startsWith(URL_PREFIX);
168         } catch(NullPointerException e) {
169             return false;
170         }
171     }
172 
173     public Connection connect(String url, Properties info) throws SQLException {
174         if(acceptsURL(url)) {
175             ObjectPool pool = getConnectionPool(url.substring(URL_PREFIX_LEN));
176             if(null == pool) {
177                 throw new SQLException("No pool found for " + url + ".");
178             } else {
179                 try {
180                     Connection conn = (Connection)(pool.borrowObject());
181                     if (conn != null) {
182                         conn = new PoolGuardConnectionWrapper(pool, conn);
183                     } 
184                     return conn;
185                 } catch(SQLException e) {
186                     throw e;
187                 } catch(NoSuchElementException e) {
188                     throw (SQLException) new SQLException("Cannot get a connection, pool error: " + e.getMessage()).initCause(e);
189                 } catch(RuntimeException e) {
190                     throw e;
191                 } catch(Exception e) {
192                     throw (SQLException) new SQLException("Cannot get a connection, general error: " + e.getMessage()).initCause(e);
193                 }
194             }
195         } else {
196             return null;
197         }
198     }
199 
200     /**
201      * Invalidates the given connection.
202      * 
203      * @param conn connection to invalidate
204      * @throws SQLException if the connection is not a 
205      * <code>PoolGuardConnectionWrapper</code> or an error occurs invalidating
206      * the connection
207      * @since 1.2.2
208      */
209     public void invalidateConnection(Connection conn) throws SQLException {
210         if (conn instanceof PoolGuardConnectionWrapper) { // normal case
211             PoolGuardConnectionWrapper pgconn = (PoolGuardConnectionWrapper) conn;
212             ObjectPool pool = pgconn.pool;
213             Connection delegate = pgconn.delegate;
214             try {
215                 pool.invalidateObject(delegate);
216             } 
217             catch (Exception e) { 
218             }
219             pgconn.delegate = null;
220         }
221         else {
222             throw new SQLException("Invalid connection class");
223         }
224     }
225 
226     public int getMajorVersion() {
227         return MAJOR_VERSION;
228     }
229 
230     public int getMinorVersion() {
231         return MINOR_VERSION;
232     }
233 
234     public boolean jdbcCompliant() {
235         return true;
236     }
237 
238     public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) {
239         return new DriverPropertyInfo[0];
240     }
241 
242     /** My URL prefix */
243     protected static final String URL_PREFIX = "jdbc:apache:commons:dbcp:";
244     protected static final int URL_PREFIX_LEN = URL_PREFIX.length();
245 
246     // version numbers
247     protected static final int MAJOR_VERSION = 1;
248     protected static final int MINOR_VERSION = 0;
249 
250     /**
251      * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a 
252      * closed connection cannot be used anymore.
253      */
254     static private class PoolGuardConnectionWrapper extends DelegatingConnection {
255 
256         private final ObjectPool pool;
257         private Connection delegate;
258     
259         PoolGuardConnectionWrapper(ObjectPool pool, Connection delegate) {
260             super(delegate);
261             this.pool = pool;
262             this.delegate = delegate;
263         }
264 
265         protected void checkOpen() throws SQLException {
266             if(delegate == null) {
267                 throw new SQLException("Connection is closed.");
268             }
269         }
270 
271         public void close() throws SQLException {
272             if (delegate != null) {
273                 this.delegate.close();
274                 this.delegate = null;
275                 super.setDelegate(null);
276             }
277         }
278 
279         public boolean isClosed() throws SQLException {
280             if (delegate == null) {
281                 return true;
282             }
283             return delegate.isClosed();
284         }
285 
286         public void clearWarnings() throws SQLException {
287             checkOpen();
288             delegate.clearWarnings();
289         }
290 
291         public void commit() throws SQLException {
292             checkOpen();
293             delegate.commit();
294         }
295 
296         public Statement createStatement() throws SQLException {
297             checkOpen();
298             return new DelegatingStatement(this, delegate.createStatement());
299         }
300 
301         public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
302             checkOpen();
303             return new DelegatingStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency));
304         }
305 
306         public boolean equals(Object obj) {
307             if (delegate == null){
308                 return false;
309             }
310             return delegate.equals(obj);
311         }
312 
313         public boolean getAutoCommit() throws SQLException {
314             checkOpen();
315             return delegate.getAutoCommit();
316         }
317 
318         public String getCatalog() throws SQLException {
319             checkOpen();
320             return delegate.getCatalog();
321         }
322 
323         public DatabaseMetaData getMetaData() throws SQLException {
324             checkOpen();
325             return delegate.getMetaData();
326         }
327 
328         public int getTransactionIsolation() throws SQLException {
329             checkOpen();
330             return delegate.getTransactionIsolation();
331         }
332 
333         public Map getTypeMap() throws SQLException {
334             checkOpen();
335             return delegate.getTypeMap();
336         }
337 
338         public SQLWarning getWarnings() throws SQLException {
339             checkOpen();
340             return delegate.getWarnings();
341         }
342 
343         public int hashCode() {
344             if (delegate == null){
345                 return 0;
346             }
347             return delegate.hashCode();
348         }
349 
350         public boolean isReadOnly() throws SQLException {
351             checkOpen();
352             return delegate.isReadOnly();
353         }
354 
355         public String nativeSQL(String sql) throws SQLException {
356             checkOpen();
357             return delegate.nativeSQL(sql);
358         }
359 
360         public CallableStatement prepareCall(String sql) throws SQLException {
361             checkOpen();
362             return new DelegatingCallableStatement(this, delegate.prepareCall(sql));
363         }
364 
365         public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
366             checkOpen();
367             return new DelegatingCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency));
368         }
369 
370         public PreparedStatement prepareStatement(String sql) throws SQLException {
371             checkOpen();
372             return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql));
373         }
374 
375         public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
376             checkOpen();
377             return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency));
378         }
379 
380         public void rollback() throws SQLException {
381             checkOpen();
382             delegate.rollback();
383         }
384 
385         public void setAutoCommit(boolean autoCommit) throws SQLException {
386             checkOpen();
387             delegate.setAutoCommit(autoCommit);
388         }
389 
390         public void setCatalog(String catalog) throws SQLException {
391             checkOpen();
392             delegate.setCatalog(catalog);
393         }
394 
395         public void setReadOnly(boolean readOnly) throws SQLException {
396             checkOpen();
397             delegate.setReadOnly(readOnly);
398         }
399 
400         public void setTransactionIsolation(int level) throws SQLException {
401             checkOpen();
402             delegate.setTransactionIsolation(level);
403         }
404 
405         public void setTypeMap(Map map) throws SQLException {
406             checkOpen();
407             delegate.setTypeMap(map);
408         }
409 
410         public String toString() {
411             if (delegate == null){
412                 return "NULL";
413             }
414             return delegate.toString();
415         }
416 
417         public int getHoldability() throws SQLException {
418             checkOpen();
419             return delegate.getHoldability();
420         }
421     
422         public void setHoldability(int holdability) throws SQLException {
423             checkOpen();
424             delegate.setHoldability(holdability);
425         }
426 
427         public java.sql.Savepoint setSavepoint() throws SQLException {
428             checkOpen();
429             return delegate.setSavepoint();
430         }
431 
432         public java.sql.Savepoint setSavepoint(String name) throws SQLException {
433             checkOpen();
434             return delegate.setSavepoint(name);
435         }
436 
437         public void releaseSavepoint(java.sql.Savepoint savepoint) throws SQLException {
438             checkOpen();
439             delegate.releaseSavepoint(savepoint);
440         }
441 
442         public void rollback(java.sql.Savepoint savepoint) throws SQLException {
443             checkOpen();
444             delegate.rollback(savepoint);
445         }
446 
447         public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
448             checkOpen();
449             return new DelegatingStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability));
450         }
451 
452         public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
453             checkOpen();
454             return new DelegatingCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
455         }
456 
457         public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
458             checkOpen();
459             return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, autoGeneratedKeys));
460         }
461 
462         public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
463             checkOpen();
464             return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
465         }
466 
467         public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
468             checkOpen();
469             return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, columnIndexes));
470         }
471 
472         public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
473             checkOpen();
474             return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
475         }
476 
477         /**
478          * @see org.apache.commons.dbcp.DelegatingConnection#getDelegate()
479          */
480         public Connection getDelegate() {
481             if (isAccessToUnderlyingConnectionAllowed()) {
482                 return super.getDelegate();
483             } else {
484                 return null;
485             }
486         }
487 
488         /**
489          * @see org.apache.commons.dbcp.DelegatingConnection#getInnermostDelegate()
490          */
491         public Connection getInnermostDelegate() {
492             if (isAccessToUnderlyingConnectionAllowed()) {
493                 return super.getInnermostDelegate();
494             } else {
495                 return null;
496             }
497         }
498     }
499 }