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.datasources;
019:
020: import java.sql.Connection;
021: import java.sql.ResultSet;
022: import java.sql.SQLException;
023: import java.sql.Statement;
024: import java.util.HashMap;
025: import java.util.Map;
026: import java.util.WeakHashMap;
027:
028: import javax.sql.ConnectionEvent;
029: import javax.sql.ConnectionEventListener;
030: import javax.sql.ConnectionPoolDataSource;
031: import javax.sql.PooledConnection;
032:
033: import org.apache.commons.dbcp.SQLNestedException;
034: import org.apache.commons.pool.ObjectPool;
035: import org.apache.commons.pool.PoolableObjectFactory;
036:
037: /**
038: * A {@link PoolableObjectFactory} that creates
039: * {@link PoolableConnection}s.
040: *
041: * @author John D. McNally
042: * @version $Revision: 479137 $ $Date: 2006-11-25 08:51:48 -0700 (Sat, 25 Nov 2006) $
043: */
044: class CPDSConnectionFactory implements PoolableObjectFactory,
045: ConnectionEventListener {
046:
047: private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but "
048: + "I have no record of the underlying PooledConnection.";
049:
050: protected ConnectionPoolDataSource _cpds = null;
051: protected String _validationQuery = null;
052: protected boolean _rollbackAfterValidation = false;
053: protected ObjectPool _pool = null;
054: protected String _username = null;
055: protected String _password = null;
056: private Map validatingMap = new HashMap();
057: private WeakHashMap pcMap = new WeakHashMap();
058:
059: /**
060: * Create a new <tt>PoolableConnectionFactory</tt>.
061: *
062: * @param cpds the ConnectionPoolDataSource from which to obtain
063: * PooledConnection's
064: * @param pool the {@link ObjectPool} in which to pool those
065: * {@link Connection}s
066: * @param validationQuery a query to use to {@link #validateObject validate}
067: * {@link Connection}s. Should return at least one row. May be
068: * <tt>null</tt>
069: * @param username
070: * @param password
071: */
072: public CPDSConnectionFactory(ConnectionPoolDataSource cpds,
073: ObjectPool pool, String validationQuery, String username,
074: String password) {
075: _cpds = cpds;
076: _pool = pool;
077: _pool.setFactory(this );
078: _validationQuery = validationQuery;
079: _username = username;
080: _password = password;
081: }
082:
083: /**
084: * Create a new <tt>PoolableConnectionFactory</tt>.
085: *
086: * @param cpds the ConnectionPoolDataSource from which to obtain
087: * PooledConnection's
088: * @param pool the {@link ObjectPool} in which to pool those {@link
089: * Connection}s
090: * @param validationQuery a query to use to {@link #validateObject
091: * validate} {@link Connection}s. Should return at least one row.
092: * May be <tt>null</tt>
093: * @param rollbackAfterValidation whether a rollback should be issued
094: * after {@link #validateObject validating} {@link Connection}s.
095: * @param username
096: * @param password
097: */
098: public CPDSConnectionFactory(ConnectionPoolDataSource cpds,
099: ObjectPool pool, String validationQuery,
100: boolean rollbackAfterValidation, String username,
101: String password) {
102: this (cpds, pool, validationQuery, username, password);
103: _rollbackAfterValidation = rollbackAfterValidation;
104: }
105:
106: /**
107: * Sets the {*link ConnectionFactory} from which to obtain base
108: * {*link Connection}s.
109: * @param connFactory the {*link ConnectionFactory} from which to obtain
110: * base {*link Connection}s
111: */
112: public synchronized void setCPDS(ConnectionPoolDataSource cpds) {
113: _cpds = cpds;
114: }
115:
116: /**
117: * Sets the query I use to {*link #validateObject validate}
118: * {*link Connection}s.
119: * Should return at least one row.
120: * May be <tt>null</tt>
121: * @param validationQuery a query to use to {*link #validateObject validate}
122: * {*link Connection}s.
123: */
124: public synchronized void setValidationQuery(String validationQuery) {
125: _validationQuery = validationQuery;
126: }
127:
128: /**
129: * Sets whether a rollback should be issued after
130: * {*link #validateObject validating}
131: * {*link Connection}s.
132: * @param rollbackAfterValidation whether a rollback should be issued after
133: * {*link #validateObject validating}
134: * {*link Connection}s.
135: */
136: public synchronized void setRollbackAfterValidation(
137: boolean rollbackAfterValidation) {
138: _rollbackAfterValidation = rollbackAfterValidation;
139: }
140:
141: /**
142: * Sets the {*link ObjectPool} in which to pool {*link Connection}s.
143: * @param pool the {*link ObjectPool} in which to pool those
144: * {*link Connection}s
145: */
146: public synchronized void setPool(ObjectPool pool)
147: throws SQLException {
148: if (null != _pool && pool != _pool) {
149: try {
150: _pool.close();
151: } catch (RuntimeException e) {
152: throw e;
153: } catch (Exception e) {
154: throw new SQLNestedException(
155: "Cannot set the pool on this factory", e);
156: }
157: }
158: _pool = pool;
159: }
160:
161: public ObjectPool getPool() {
162: return _pool;
163: }
164:
165: public synchronized Object makeObject() {
166: Object obj;
167: try {
168: PooledConnection pc = null;
169: if (_username == null) {
170: pc = _cpds.getPooledConnection();
171: } else {
172: pc = _cpds.getPooledConnection(_username, _password);
173: }
174: // should we add this object as a listener or the pool.
175: // consider the validateObject method in decision
176: pc.addConnectionEventListener(this );
177: obj = new PooledConnectionAndInfo(pc, _username, _password);
178: pcMap.put(pc, obj);
179: } catch (SQLException e) {
180: throw new RuntimeException(e.getMessage());
181: }
182: return obj;
183: }
184:
185: public void destroyObject(Object obj) throws Exception {
186: if (obj instanceof PooledConnectionAndInfo) {
187: ((PooledConnectionAndInfo) obj).getPooledConnection()
188: .close();
189: }
190: }
191:
192: public boolean validateObject(Object obj) {
193: boolean valid = false;
194: if (obj instanceof PooledConnectionAndInfo) {
195: PooledConnection pconn = ((PooledConnectionAndInfo) obj)
196: .getPooledConnection();
197: String query = _validationQuery;
198: if (null != query) {
199: Connection conn = null;
200: Statement stmt = null;
201: ResultSet rset = null;
202: // logical Connection from the PooledConnection must be closed
203: // before another one can be requested and closing it will
204: // generate an event. Keep track so we know not to return
205: // the PooledConnection
206: validatingMap.put(pconn, null);
207: try {
208: conn = pconn.getConnection();
209: stmt = conn.createStatement();
210: rset = stmt.executeQuery(query);
211: if (rset.next()) {
212: valid = true;
213: } else {
214: valid = false;
215: }
216: if (_rollbackAfterValidation) {
217: conn.rollback();
218: }
219: } catch (Exception e) {
220: valid = false;
221: } finally {
222: if (rset != null) {
223: try {
224: rset.close();
225: } catch (Throwable t) {
226: // ignore
227: }
228: }
229: if (stmt != null) {
230: try {
231: stmt.close();
232: } catch (Throwable t) {
233: // ignore
234: }
235: }
236: if (conn != null) {
237: try {
238: conn.close();
239: } catch (Throwable t) {
240: // ignore
241: }
242: }
243: validatingMap.remove(pconn);
244: }
245: } else {
246: valid = true;
247: }
248: } else {
249: valid = false;
250: }
251: return valid;
252: }
253:
254: public void passivateObject(Object obj) {
255: }
256:
257: public void activateObject(Object obj) {
258: }
259:
260: // ***********************************************************************
261: // java.sql.ConnectionEventListener implementation
262: // ***********************************************************************
263:
264: /**
265: * This will be called if the Connection returned by the getConnection
266: * method came from a PooledConnection, and the user calls the close()
267: * method of this connection object. What we need to do here is to
268: * release this PooledConnection from our pool...
269: */
270: public void connectionClosed(ConnectionEvent event) {
271: PooledConnection pc = (PooledConnection) event.getSource();
272: // if this event occured becase we were validating, ignore it
273: // otherwise return the connection to the pool.
274: if (!validatingMap.containsKey(pc)) {
275: Object info = pcMap.get(pc);
276: if (info == null) {
277: throw new IllegalStateException(NO_KEY_MESSAGE);
278: }
279:
280: try {
281: _pool.returnObject(info);
282: } catch (Exception e) {
283: System.err
284: .println("CLOSING DOWN CONNECTION AS IT COULD "
285: + "NOT BE RETURNED TO THE POOL");
286: try {
287: destroyObject(info);
288: } catch (Exception e2) {
289: System.err
290: .println("EXCEPTION WHILE DESTROYING OBJECT "
291: + info);
292: e2.printStackTrace();
293: }
294: }
295: }
296: }
297:
298: /**
299: * If a fatal error occurs, close the underlying physical connection so as
300: * not to be returned in the future
301: */
302: public void connectionErrorOccurred(ConnectionEvent event) {
303: PooledConnection pc = (PooledConnection) event.getSource();
304: try {
305: if (null != event.getSQLException()) {
306: System.err
307: .println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR ("
308: + event.getSQLException() + ")");
309: }
310: //remove this from the listener list because we are no more
311: //interested in errors since we are about to close this connection
312: pc.removeConnectionEventListener(this );
313: } catch (Exception ignore) {
314: // ignore
315: }
316:
317: Object info = pcMap.get(pc);
318: if (info == null) {
319: throw new IllegalStateException(NO_KEY_MESSAGE);
320: }
321: try {
322: destroyObject(info);
323: } catch (Exception e) {
324: System.err.println("EXCEPTION WHILE DESTROYING OBJECT "
325: + info);
326: e.printStackTrace();
327: }
328: }
329:
330: }
|