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.PrintWriter;
21 import java.sql.CallableStatement;
22 import java.sql.Connection;
23 import java.sql.DatabaseMetaData;
24 import java.sql.PreparedStatement;
25 import java.sql.SQLException;
26 import java.sql.SQLWarning;
27 import java.sql.Statement;
28 import java.util.Map;
29 import java.util.NoSuchElementException;
30
31 import javax.sql.DataSource;
32
33 import org.apache.commons.pool.ObjectPool;
34
35 /**
36 * A simple {@link DataSource} implementation that obtains
37 * {@link Connection}s from the specified {@link ObjectPool}.
38 *
39 * @author Rodney Waldhoff
40 * @author Glenn L. Nielsen
41 * @author James House
42 * @author Dirk Verbeeck
43 * @version $Revision: 479137 $ $Date: 2006-11-25 08:51:48 -0700 (Sat, 25 Nov 2006) $
44 */
45 public class PoolingDataSource implements DataSource {
46
47 /** Controls access to the underlying connection */
48 private boolean accessToUnderlyingConnectionAllowed = false;
49
50 public PoolingDataSource() {
51 this(null);
52 }
53
54 public PoolingDataSource(ObjectPool pool) {
55 _pool = pool;
56 }
57
58 public void setPool(ObjectPool pool) throws IllegalStateException, NullPointerException {
59 if(null != _pool) {
60 throw new IllegalStateException("Pool already set");
61 } else if(null == pool) {
62 throw new NullPointerException("Pool must not be null.");
63 } else {
64 _pool = pool;
65 }
66 }
67
68 /**
69 * Returns the value of the accessToUnderlyingConnectionAllowed property.
70 *
71 * @return true if access to the underlying is allowed, false otherwise.
72 */
73 public boolean isAccessToUnderlyingConnectionAllowed() {
74 return this.accessToUnderlyingConnectionAllowed;
75 }
76
77 /**
78 * Sets the value of the accessToUnderlyingConnectionAllowed property.
79 * It controls if the PoolGuard allows access to the underlying connection.
80 * (Default: false)
81 *
82 * @param allow Access to the underlying connection is granted when true.
83 */
84 public void setAccessToUnderlyingConnectionAllowed(boolean allow) {
85 this.accessToUnderlyingConnectionAllowed = allow;
86 }
87
88 //--- DataSource methods -----------------------------------------
89
90 /**
91 * Return a {@link java.sql.Connection} from my pool,
92 * according to the contract specified by {@link ObjectPool#borrowObject}.
93 */
94 public Connection getConnection() throws SQLException {
95 try {
96 Connection conn = (Connection)(_pool.borrowObject());
97 if (conn != null) {
98 conn = new PoolGuardConnectionWrapper(conn);
99 }
100 return conn;
101 } catch(SQLException e) {
102 throw e;
103 } catch(NoSuchElementException e) {
104 throw new SQLNestedException("Cannot get a connection, pool error " + e.getMessage(), e);
105 } catch(RuntimeException e) {
106 throw e;
107 } catch(Exception e) {
108 throw new SQLNestedException("Cannot get a connection, general error", e);
109 }
110 }
111
112 /**
113 * Throws {@link UnsupportedOperationException}
114 * @throws UnsupportedOperationException
115 */
116 public Connection getConnection(String uname, String passwd) throws SQLException {
117 throw new UnsupportedOperationException();
118 }
119
120 /**
121 * Returns my log writer.
122 * @return my log writer
123 * @see DataSource#getLogWriter
124 */
125 public PrintWriter getLogWriter() {
126 return _logWriter;
127 }
128
129 /**
130 * Throws {@link UnsupportedOperationException}.
131 * @throws UnsupportedOperationException As this
132 * implementation does not support this feature.
133 */
134 public int getLoginTimeout() {
135 throw new UnsupportedOperationException("Login timeout is not supported.");
136 }
137
138 /**
139 * Throws {@link UnsupportedOperationException}.
140 * @throws UnsupportedOperationException As this
141 * implementation does not support this feature.
142 */
143 public void setLoginTimeout(int seconds) {
144 throw new UnsupportedOperationException("Login timeout is not supported.");
145 }
146
147 /**
148 * Sets my log writer.
149 * @see DataSource#setLogWriter
150 */
151 public void setLogWriter(PrintWriter out) {
152 _logWriter = out;
153 }
154
155 /** My log writer. */
156 protected PrintWriter _logWriter = null;
157
158 protected ObjectPool _pool = null;
159
160 /**
161 * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a
162 * closed connection cannot be used anymore.
163 */
164 private class PoolGuardConnectionWrapper extends DelegatingConnection {
165
166 private Connection delegate;
167
168 PoolGuardConnectionWrapper(Connection delegate) {
169 super(delegate);
170 this.delegate = delegate;
171 }
172
173 protected void checkOpen() throws SQLException {
174 if(delegate == null) {
175 throw new SQLException("Connection is closed.");
176 }
177 }
178
179 public void close() throws SQLException {
180 checkOpen();
181 this.delegate.close();
182 this.delegate = null;
183 super.setDelegate(null);
184 }
185
186 public boolean isClosed() throws SQLException {
187 if (delegate == null) {
188 return true;
189 }
190 return delegate.isClosed();
191 }
192
193 public void clearWarnings() throws SQLException {
194 checkOpen();
195 delegate.clearWarnings();
196 }
197
198 public void commit() throws SQLException {
199 checkOpen();
200 delegate.commit();
201 }
202
203 public Statement createStatement() throws SQLException {
204 checkOpen();
205 return delegate.createStatement();
206 }
207
208 public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
209 checkOpen();
210 return delegate.createStatement(resultSetType, resultSetConcurrency);
211 }
212
213 public boolean innermostDelegateEquals(Connection c) {
214 Connection innerCon = super.getInnermostDelegate();
215 if (innerCon == null) {
216 return c == null;
217 } else {
218 return innerCon.equals(c);
219 }
220 }
221
222 public boolean getAutoCommit() throws SQLException {
223 checkOpen();
224 return delegate.getAutoCommit();
225 }
226
227 public String getCatalog() throws SQLException {
228 checkOpen();
229 return delegate.getCatalog();
230 }
231
232 public DatabaseMetaData getMetaData() throws SQLException {
233 checkOpen();
234 return delegate.getMetaData();
235 }
236
237 public int getTransactionIsolation() throws SQLException {
238 checkOpen();
239 return delegate.getTransactionIsolation();
240 }
241
242 public Map getTypeMap() throws SQLException {
243 checkOpen();
244 return delegate.getTypeMap();
245 }
246
247 public SQLWarning getWarnings() throws SQLException {
248 checkOpen();
249 return delegate.getWarnings();
250 }
251
252 public int hashCode() {
253 if (delegate == null){
254 return 0;
255 }
256 return delegate.hashCode();
257 }
258
259 public boolean equals(Object obj) {
260 if (obj == null) {
261 return false;
262 }
263 if (obj == this) {
264 return true;
265 }
266 // Use superclass accessor to skip access test
267 Connection delegate = super.getInnermostDelegate();
268 if (delegate == null) {
269 return false;
270 }
271 if (obj instanceof DelegatingConnection) {
272 DelegatingConnection c = (DelegatingConnection) obj;
273 return c.innermostDelegateEquals(delegate);
274 }
275 else {
276 return delegate.equals(obj);
277 }
278 }
279
280 public boolean isReadOnly() throws SQLException {
281 checkOpen();
282 return delegate.isReadOnly();
283 }
284
285 public String nativeSQL(String sql) throws SQLException {
286 checkOpen();
287 return delegate.nativeSQL(sql);
288 }
289
290 public CallableStatement prepareCall(String sql) throws SQLException {
291 checkOpen();
292 return delegate.prepareCall(sql);
293 }
294
295 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
296 checkOpen();
297 return delegate.prepareCall(sql, resultSetType, resultSetConcurrency);
298 }
299
300 public PreparedStatement prepareStatement(String sql) throws SQLException {
301 checkOpen();
302 return delegate.prepareStatement(sql);
303 }
304
305 public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
306 checkOpen();
307 return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency);
308 }
309
310 public void rollback() throws SQLException {
311 checkOpen();
312 delegate.rollback();
313 }
314
315 public void setAutoCommit(boolean autoCommit) throws SQLException {
316 checkOpen();
317 delegate.setAutoCommit(autoCommit);
318 }
319
320 public void setCatalog(String catalog) throws SQLException {
321 checkOpen();
322 delegate.setCatalog(catalog);
323 }
324
325 public void setReadOnly(boolean readOnly) throws SQLException {
326 checkOpen();
327 delegate.setReadOnly(readOnly);
328 }
329
330 public void setTransactionIsolation(int level) throws SQLException {
331 checkOpen();
332 delegate.setTransactionIsolation(level);
333 }
334
335 public void setTypeMap(Map map) throws SQLException {
336 checkOpen();
337 delegate.setTypeMap(map);
338 }
339
340 public String toString() {
341 if (delegate == null){
342 return null;
343 }
344 return delegate.toString();
345 }
346
347 // ------------------- JDBC 3.0 -----------------------------------------
348 // Will be commented by the build process on a JDBC 2.0 system
349
350 /* JDBC_3_ANT_KEY_BEGIN */
351
352 public int getHoldability() throws SQLException {
353 checkOpen();
354 return delegate.getHoldability();
355 }
356
357 public void setHoldability(int holdability) throws SQLException {
358 checkOpen();
359 delegate.setHoldability(holdability);
360 }
361
362 public java.sql.Savepoint setSavepoint() throws SQLException {
363 checkOpen();
364 return delegate.setSavepoint();
365 }
366
367 public java.sql.Savepoint setSavepoint(String name) throws SQLException {
368 checkOpen();
369 return delegate.setSavepoint(name);
370 }
371
372 public void releaseSavepoint(java.sql.Savepoint savepoint) throws SQLException {
373 checkOpen();
374 delegate.releaseSavepoint(savepoint);
375 }
376
377 public void rollback(java.sql.Savepoint savepoint) throws SQLException {
378 checkOpen();
379 delegate.rollback(savepoint);
380 }
381
382 public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
383 checkOpen();
384 return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
385 }
386
387 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
388 checkOpen();
389 return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
390 }
391
392 public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
393 checkOpen();
394 return delegate.prepareStatement(sql, autoGeneratedKeys);
395 }
396
397 public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
398 checkOpen();
399 return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
400 }
401
402 public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
403 checkOpen();
404 return delegate.prepareStatement(sql, columnIndexes);
405 }
406
407 public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
408 checkOpen();
409 return delegate.prepareStatement(sql, columnNames);
410 }
411
412 /* JDBC_3_ANT_KEY_END */
413
414 /**
415 * @see org.apache.commons.dbcp.DelegatingConnection#getDelegate()
416 */
417 public Connection getDelegate() {
418 if (isAccessToUnderlyingConnectionAllowed()) {
419 return super.getDelegate();
420 } else {
421 return null;
422 }
423 }
424
425 /**
426 * @see org.apache.commons.dbcp.DelegatingConnection#getInnermostDelegate()
427 */
428 public Connection getInnermostDelegate() {
429 if (isAccessToUnderlyingConnectionAllowed()) {
430 return super.getInnermostDelegate();
431 } else {
432 return null;
433 }
434 }
435 }
436 }