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 }