001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     * 
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.dbcp;
019    
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.sql.CallableStatement;
023    import java.sql.Connection;
024    import java.sql.DatabaseMetaData;
025    import java.sql.Driver;
026    import java.sql.DriverManager;
027    import java.sql.DriverPropertyInfo;
028    import java.sql.PreparedStatement;
029    import java.sql.SQLException;
030    import java.sql.SQLWarning;
031    import java.sql.Statement;
032    import java.util.HashMap;
033    import java.util.Map;
034    import java.util.NoSuchElementException;
035    import java.util.Properties;
036    import java.util.Set;
037    
038    import org.apache.commons.jocl.JOCLContentHandler;
039    import org.apache.commons.pool.ObjectPool;
040    import org.xml.sax.SAXException;
041    
042    
043    /**
044     * A {@link Driver} implementation that obtains
045     * {@link Connection}s from a registered
046     * {@link ObjectPool}.
047     *
048     * @author Rodney Waldhoff
049     * @author Dirk Verbeeck
050     * @version $Revision: 892307 $ $Date: 2014-01-01 00:27:28 +0100 (Wed, 01 Jan 2014) $
051     */
052    public class PoolingDriver implements Driver {
053        /** Register myself with the {@link DriverManager}. */
054        static {
055            try {
056                DriverManager.registerDriver(new PoolingDriver());
057            } catch(Exception e) {
058            }
059        }
060    
061        /** The map of registered pools. */
062        protected static final HashMap _pools = new HashMap();
063    
064        /** Controls access to the underlying connection */
065        private static boolean accessToUnderlyingConnectionAllowed = false; 
066    
067        public PoolingDriver() {
068        }
069    
070        /**
071         * Returns the value of the accessToUnderlyingConnectionAllowed property.
072         * 
073         * @return true if access to the underlying is allowed, false otherwise.
074         */
075        public static synchronized boolean isAccessToUnderlyingConnectionAllowed() {
076            return accessToUnderlyingConnectionAllowed;
077        }
078    
079        /**
080         * Sets the value of the accessToUnderlyingConnectionAllowed property.
081         * It controls if the PoolGuard allows access to the underlying connection.
082         * (Default: false)
083         * 
084         * @param allow Access to the underlying connection is granted when true.
085         */
086        public static synchronized void setAccessToUnderlyingConnectionAllowed(boolean allow) {
087            accessToUnderlyingConnectionAllowed = allow;
088        }
089    
090        /**
091         * WARNING: This method throws DbcpExceptions (RuntimeExceptions)
092         * and will be replaced by the protected getConnectionPool method.
093         * 
094         * @deprecated This will be removed in a future version of DBCP.
095         */
096        public synchronized ObjectPool getPool(String name) {
097            try {
098                return getConnectionPool(name);
099            }
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    }