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.PrintWriter;
021    import java.sql.CallableStatement;
022    import java.sql.Connection;
023    import java.sql.DatabaseMetaData;
024    import java.sql.PreparedStatement;
025    import java.sql.SQLException;
026    import java.sql.SQLWarning;
027    import java.sql.Statement;
028    import java.util.Map;
029    import java.util.NoSuchElementException;
030    
031    import javax.sql.DataSource;
032    
033    import org.apache.commons.pool.ObjectPool;
034    
035    /**
036     * A simple {@link DataSource} implementation that obtains
037     * {@link Connection}s from the specified {@link ObjectPool}.
038     *
039     * @author Rodney Waldhoff
040     * @author Glenn L. Nielsen
041     * @author James House
042     * @author Dirk Verbeeck
043     * @version $Revision: 892307 $ $Date: 2013-12-31 23:27:28 +0000 (Tue, 31 Dec 2013) $
044     */
045    public class PoolingDataSource implements DataSource {
046    
047        /** Controls access to the underlying connection */
048        private boolean accessToUnderlyingConnectionAllowed = false; 
049    
050        public PoolingDataSource() {
051            this(null);
052        }
053    
054        public PoolingDataSource(ObjectPool pool) {
055            _pool = pool;
056        }
057    
058        public void setPool(ObjectPool pool) throws IllegalStateException, NullPointerException {
059            if(null != _pool) {
060                throw new IllegalStateException("Pool already set");
061            } else if(null == pool) {
062                throw new NullPointerException("Pool must not be null.");
063            } else {
064                _pool = pool;
065            }
066        }
067    
068        /**
069         * Returns the value of the accessToUnderlyingConnectionAllowed property.
070         * 
071         * @return true if access to the underlying is allowed, false otherwise.
072         */
073        public boolean isAccessToUnderlyingConnectionAllowed() {
074            return this.accessToUnderlyingConnectionAllowed;
075        }
076    
077        /**
078         * Sets the value of the accessToUnderlyingConnectionAllowed property.
079         * It controls if the PoolGuard allows access to the underlying connection.
080         * (Default: false)
081         * 
082         * @param allow Access to the underlying connection is granted when true.
083         */
084        public void setAccessToUnderlyingConnectionAllowed(boolean allow) {
085            this.accessToUnderlyingConnectionAllowed = allow;
086        }
087    
088        /*
089        public boolean isWrapperFor(Class<?> iface) throws SQLException {
090            return false;
091        }
092    
093        public <T> T unwrap(Class<T> iface) throws SQLException {
094            throw new SQLException("PoolingDataSource is not a wrapper.");
095        }
096        */
097        
098        //--- DataSource methods -----------------------------------------
099    
100        /**
101         * Return a {@link java.sql.Connection} from my pool,
102         * according to the contract specified by {@link ObjectPool#borrowObject}.
103         */
104        public Connection getConnection() throws SQLException {
105            try {
106                Connection conn = (Connection)(_pool.borrowObject());
107                if (conn != null) {
108                    conn = new PoolGuardConnectionWrapper(conn);
109                } 
110                return conn;
111            } catch(SQLException e) {
112                throw e;
113            } catch(NoSuchElementException e) {
114                throw new SQLNestedException("Cannot get a connection, pool error " + e.getMessage(), e);
115            } catch(RuntimeException e) {
116                throw e;
117            } catch(Exception e) {
118                throw new SQLNestedException("Cannot get a connection, general error", e);
119            }
120        }
121    
122        /**
123         * Throws {@link UnsupportedOperationException}
124         * @throws UnsupportedOperationException
125         */
126        public Connection getConnection(String uname, String passwd) throws SQLException {
127            throw new UnsupportedOperationException();
128        }
129    
130        /**
131         * Returns my log writer.
132         * @return my log writer
133         * @see DataSource#getLogWriter
134         */
135        public PrintWriter getLogWriter() {
136            return _logWriter;
137        }
138    
139        /**
140         * Throws {@link UnsupportedOperationException}.
141         * @throws UnsupportedOperationException As this
142         *   implementation does not support this feature.
143         */
144        public int getLoginTimeout() {
145            throw new UnsupportedOperationException("Login timeout is not supported.");
146        }
147    
148        /**
149         * Throws {@link UnsupportedOperationException}.
150         * @throws UnsupportedOperationException As this
151         *   implementation does not support this feature.
152         */
153        public void setLoginTimeout(int seconds) {
154            throw new UnsupportedOperationException("Login timeout is not supported.");
155        }
156    
157        /**
158         * Sets my log writer.
159         * @see DataSource#setLogWriter
160         */
161        public void setLogWriter(PrintWriter out) {
162            _logWriter = out;
163        }
164    
165        /** My log writer. */
166        protected PrintWriter _logWriter = null;
167    
168        protected ObjectPool _pool = null;
169    
170        /**
171         * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a 
172         * closed connection cannot be used anymore.
173         */
174        private class PoolGuardConnectionWrapper extends DelegatingConnection {
175    
176            private Connection delegate;
177        
178            PoolGuardConnectionWrapper(Connection delegate) {
179                super(delegate);
180                this.delegate = delegate;
181            }
182    
183            protected void checkOpen() throws SQLException {
184                if(delegate == null) {
185                    throw new SQLException("Connection is closed.");
186                }
187            }
188        
189            public void close() throws SQLException {
190                if (delegate != null) {
191                    this.delegate.close();
192                    this.delegate = null;
193                    super.setDelegate(null);
194                }
195            }
196    
197            public boolean isClosed() throws SQLException {
198                if (delegate == null) {
199                    return true;
200                }
201                return delegate.isClosed();
202            }
203    
204            public void clearWarnings() throws SQLException {
205                checkOpen();
206                delegate.clearWarnings();
207            }
208    
209            public void commit() throws SQLException {
210                checkOpen();
211                delegate.commit();
212            }
213    
214            public Statement createStatement() throws SQLException {
215                checkOpen();
216                return new DelegatingStatement(this, delegate.createStatement());
217            }
218    
219            public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
220                checkOpen();
221                return new DelegatingStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency));
222            }
223    
224            public boolean innermostDelegateEquals(Connection c) {
225                Connection innerCon = super.getInnermostDelegate();
226                if (innerCon == null) {
227                    return c == null;
228                } else {
229                    return innerCon.equals(c);
230                }
231            }
232            
233            public boolean getAutoCommit() throws SQLException {
234                checkOpen();
235                return delegate.getAutoCommit();
236            }
237    
238            public String getCatalog() throws SQLException {
239                checkOpen();
240                return delegate.getCatalog();
241            }
242    
243            public DatabaseMetaData getMetaData() throws SQLException {
244                checkOpen();
245                return delegate.getMetaData();
246            }
247    
248            public int getTransactionIsolation() throws SQLException {
249                checkOpen();
250                return delegate.getTransactionIsolation();
251            }
252    
253            public Map getTypeMap() throws SQLException {
254                checkOpen();
255                return delegate.getTypeMap();
256            }
257    
258            public SQLWarning getWarnings() throws SQLException {
259                checkOpen();
260                return delegate.getWarnings();
261            }
262    
263            public int hashCode() {
264                if (delegate == null){
265                    return 0;
266                }
267                return delegate.hashCode();
268            }
269            
270            public boolean equals(Object obj) {
271                if (obj == null) {
272                    return false;
273                }
274                if (obj == this) {
275                    return true;
276                }
277                // Use superclass accessor to skip access test
278                Connection conn = super.getInnermostDelegate();
279                if (conn == null) {
280                    return false;
281                }
282                if (obj instanceof DelegatingConnection) {    
283                    DelegatingConnection c = (DelegatingConnection) obj;
284                    return c.innermostDelegateEquals(conn);
285                }
286                else {
287                    return conn.equals(obj);
288                }
289            }
290    
291            public boolean isReadOnly() throws SQLException {
292                checkOpen();
293                return delegate.isReadOnly();
294            }
295    
296            public String nativeSQL(String sql) throws SQLException {
297                checkOpen();
298                return delegate.nativeSQL(sql);
299            }
300    
301            public CallableStatement prepareCall(String sql) throws SQLException {
302                checkOpen();
303                return new DelegatingCallableStatement(this, delegate.prepareCall(sql));
304            }
305    
306            public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
307                checkOpen();
308                return new DelegatingCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency));
309            }
310    
311            public PreparedStatement prepareStatement(String sql) throws SQLException {
312                checkOpen();
313                return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql));
314            }
315    
316            public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
317                checkOpen();
318                return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency));
319            }
320    
321            public void rollback() throws SQLException {
322                checkOpen();
323                delegate.rollback();
324            }
325    
326            public void setAutoCommit(boolean autoCommit) throws SQLException {
327                checkOpen();
328                delegate.setAutoCommit(autoCommit);
329            }
330    
331            public void setCatalog(String catalog) throws SQLException {
332                checkOpen();
333                delegate.setCatalog(catalog);
334            }
335    
336            public void setReadOnly(boolean readOnly) throws SQLException {
337                checkOpen();
338                delegate.setReadOnly(readOnly);
339            }
340    
341            public void setTransactionIsolation(int level) throws SQLException {
342                checkOpen();
343                delegate.setTransactionIsolation(level);
344            }
345    
346            public void setTypeMap(Map map) throws SQLException {
347                checkOpen();
348                delegate.setTypeMap(map);
349            }
350    
351            public String toString() {
352                if (delegate == null){
353                    return "NULL";
354                }
355                return delegate.toString();
356            }
357    
358            public int getHoldability() throws SQLException {
359                checkOpen();
360                return delegate.getHoldability();
361            }
362        
363            public void setHoldability(int holdability) throws SQLException {
364                checkOpen();
365                delegate.setHoldability(holdability);
366            }
367    
368            public java.sql.Savepoint setSavepoint() throws SQLException {
369                checkOpen();
370                return delegate.setSavepoint();
371            }
372    
373            public java.sql.Savepoint setSavepoint(String name) throws SQLException {
374                checkOpen();
375                return delegate.setSavepoint(name);
376            }
377    
378            public void releaseSavepoint(java.sql.Savepoint savepoint) throws SQLException {
379                checkOpen();
380                delegate.releaseSavepoint(savepoint);
381            }
382    
383            public void rollback(java.sql.Savepoint savepoint) throws SQLException {
384                checkOpen();
385                delegate.rollback(savepoint);
386            }
387    
388            public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
389                checkOpen();
390                return new DelegatingStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability));
391            }
392    
393            public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
394                checkOpen();
395                return new DelegatingCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
396            }
397    
398            public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
399                checkOpen();
400                return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, autoGeneratedKeys));
401            }
402    
403            public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
404                checkOpen();
405                return new DelegatingPreparedStatement(this,delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
406            }
407    
408            public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
409                checkOpen();
410                return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, columnIndexes));
411            }
412    
413            public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
414                checkOpen();
415                return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
416            }
417    
418            /**
419             * @see org.apache.commons.dbcp.DelegatingConnection#getDelegate()
420             */
421            public Connection getDelegate() {
422                if (isAccessToUnderlyingConnectionAllowed()) {
423                    return super.getDelegate();
424                } else {
425                    return null;
426                }
427            }
428    
429            /**
430             * @see org.apache.commons.dbcp.DelegatingConnection#getInnermostDelegate()
431             */
432            public Connection getInnermostDelegate() {
433                if (isAccessToUnderlyingConnectionAllowed()) {
434                    return super.getInnermostDelegate();
435                } else {
436                    return null;
437                }
438            }
439        }
440    }