001: package org.apache.ojb.broker.accesslayer;
002:
003: /* Copyright 2002-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * 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: import org.apache.commons.pool.BasePoolableObjectFactory;
019: import org.apache.commons.pool.ObjectPool;
020: import org.apache.commons.pool.PoolableObjectFactory;
021: import org.apache.commons.pool.impl.GenericObjectPool;
022: import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
023: import org.apache.ojb.broker.util.logging.Logger;
024: import org.apache.ojb.broker.util.logging.LoggerFactory;
025: import org.apache.ojb.broker.OJBRuntimeException;
026:
027: import java.sql.Connection;
028: import java.sql.ResultSet;
029: import java.sql.SQLException;
030: import java.sql.PreparedStatement;
031: import java.util.Collection;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.Map;
035: import java.util.NoSuchElementException;
036:
037: /**
038: * Connection factory which pools the requested
039: * connections for different JdbcConnectionDescriptors
040: * using Commons Pool API.
041: *
042: * @version $Id: ConnectionFactoryPooledImpl.java,v 1.15.2.9 2005/10/27 14:54:50 arminw Exp $
043: * @see <a href="http://jakarta.apache.org/commons/pool/">Commons Pool Website</a>
044: */
045: public class ConnectionFactoryPooledImpl extends
046: ConnectionFactoryAbstractImpl {
047:
048: private Logger log = LoggerFactory
049: .getLogger(ConnectionFactoryPooledImpl.class);
050: /** Key=PBKey, value=ObjectPool. */
051: private Map poolMap = new HashMap();
052: /** Synchronize object for operations not synchronized on Map only. */
053: private final Object poolSynch = new Object();
054:
055: public void releaseJdbcConnection(JdbcConnectionDescriptor jcd,
056: Connection con) throws LookupException {
057: final ObjectPool op = (ObjectPool) poolMap.get(jcd.getPBKey());
058: try {
059: /* mkalen: NB - according to the Commons Pool API we should _not_ perform
060: * any additional checks here since we will then break testOnX semantics
061: *
062: * To enable Connection validation on releaseJdbcConnection,
063: * set a validation query and specify testOnRelease=true
064: *
065: * Destruction of pooled objects is performed by the actual Commons Pool
066: * ObjectPool implementation when the object factory's validateObject method
067: * returns false. See ConPoolFactory#validateObject.
068: */
069: op.returnObject(con);
070: } catch (Exception e) {
071: throw new LookupException(e);
072: }
073: }
074:
075: public Connection checkOutJdbcConnection(
076: JdbcConnectionDescriptor jcd) throws LookupException {
077: ObjectPool op = (ObjectPool) poolMap.get(jcd.getPBKey());
078: if (op == null) {
079: synchronized (poolSynch) {
080: log.info("Create new connection pool:" + jcd);
081: op = createConnectionPool(jcd);
082: poolMap.put(jcd.getPBKey(), op);
083: }
084: }
085: final Connection conn;
086: try {
087: conn = (Connection) op.borrowObject();
088: } catch (NoSuchElementException e) {
089: int active = 0;
090: int idle = 0;
091: try {
092: active = op.getNumActive();
093: idle = op.getNumIdle();
094: } catch (Exception ignore) {
095: }
096: throw new LookupException(
097: "Could not borrow connection from pool, seems ObjectPool is exhausted."
098: + " Active/Idle instances in pool="
099: + active + "/" + idle + ". "
100: + JdbcConnectionDescriptor.class.getName()
101: + ": " + jcd, e);
102: } catch (Exception e) {
103: int active = 0;
104: int idle = 0;
105: try {
106: active = op.getNumActive();
107: idle = op.getNumIdle();
108: } catch (Exception ignore) {
109: }
110: throw new LookupException(
111: "Could not borrow connection from pool."
112: + " Active/Idle instances in pool="
113: + active + "/" + idle + ". "
114: + JdbcConnectionDescriptor.class.getName()
115: + ": " + jcd, e);
116: }
117: return conn;
118: }
119:
120: /**
121: * Create the pool for pooling the connections of the given connection descriptor.
122: * Override this method to implement your on {@link org.apache.commons.pool.ObjectPool}.
123: */
124: public ObjectPool createConnectionPool(JdbcConnectionDescriptor jcd) {
125: if (log.isDebugEnabled())
126: log.debug("createPool was called");
127: PoolableObjectFactory pof = new ConPoolFactory(this , jcd);
128: GenericObjectPool.Config conf = jcd
129: .getConnectionPoolDescriptor().getObjectPoolConfig();
130: return (ObjectPool) new GenericObjectPool(pof, conf);
131: }
132:
133: /**
134: * Closes all managed pools.
135: */
136: public void releaseAllResources() {
137: synchronized (poolSynch) {
138: Collection pools = poolMap.values();
139: poolMap = new HashMap(poolMap.size());
140: ObjectPool op = null;
141: for (Iterator iterator = pools.iterator(); iterator
142: .hasNext();) {
143: try {
144: op = ((ObjectPool) iterator.next());
145: op.close();
146: } catch (Exception e) {
147: log.error("Exception occured while closing pool "
148: + op, e);
149: }
150: }
151: }
152: super .releaseAllResources();
153: }
154:
155: //**************************************************************************************
156: // Inner classes
157: //************************************************************************************
158:
159: /**
160: * Inner class - {@link org.apache.commons.pool.PoolableObjectFactory}
161: * used as factory for connection pooling.
162: */
163: class ConPoolFactory extends BasePoolableObjectFactory {
164: final private JdbcConnectionDescriptor jcd;
165: final private ConnectionFactoryPooledImpl cf;
166: private int failedValidationQuery;
167:
168: public ConPoolFactory(ConnectionFactoryPooledImpl cf,
169: JdbcConnectionDescriptor jcd) {
170: this .cf = cf;
171: this .jcd = jcd;
172: }
173:
174: public boolean validateObject(Object obj) {
175: boolean isValid = false;
176: if (obj != null) {
177: final Connection con = (Connection) obj;
178: try {
179: isValid = !con.isClosed();
180: } catch (SQLException e) {
181: log.warn("Connection validation failed: "
182: + e.getMessage());
183: if (log.isDebugEnabled())
184: log.debug(e);
185: isValid = false;
186: }
187: if (isValid) {
188: final String validationQuery;
189: validationQuery = jcd.getConnectionPoolDescriptor()
190: .getValidationQuery();
191: if (validationQuery != null) {
192: isValid = validateConnection(con,
193: validationQuery);
194: }
195: }
196: }
197: return isValid;
198: }
199:
200: private boolean validateConnection(Connection conn, String query) {
201: PreparedStatement stmt = null;
202: ResultSet rset = null;
203: boolean isValid = false;
204: if (failedValidationQuery > 100) {
205: --failedValidationQuery;
206: throw new OJBRuntimeException(
207: "Validation of connection " + conn
208: + " using validation query '" + query
209: + "' failed more than 100 times.");
210: }
211: try {
212: stmt = conn.prepareStatement(query);
213: stmt.setMaxRows(1);
214: stmt.setFetchSize(1);
215: rset = stmt.executeQuery();
216: if (rset.next()) {
217: failedValidationQuery = 0;
218: isValid = true;
219: } else {
220: ++failedValidationQuery;
221: log
222: .warn("Validation query '"
223: + query
224: + "' result set does not match, discard connection");
225: isValid = false;
226: }
227: } catch (SQLException e) {
228: ++failedValidationQuery;
229: log
230: .warn("Validation query for connection failed, discard connection. Query was '"
231: + query
232: + "', Message was "
233: + e.getMessage());
234: if (log.isDebugEnabled())
235: log.debug(e);
236: } finally {
237: try {
238: if (rset != null)
239: rset.close();
240: } catch (SQLException t) {
241: if (log.isDebugEnabled())
242: log.debug("ResultSet already closed.", t);
243: }
244: try {
245: if (stmt != null)
246: stmt.close();
247: } catch (SQLException t) {
248: if (log.isDebugEnabled())
249: log.debug("Statement already closed.", t);
250: }
251: }
252: return isValid;
253: }
254:
255: public Object makeObject() throws Exception {
256: if (log.isDebugEnabled())
257: log.debug("makeObject called");
258: return cf.newConnectionFromDriverManager(jcd);
259: }
260:
261: public void destroyObject(Object obj) throws Exception {
262: log
263: .info("Destroy object was called, try to close connection: "
264: + obj);
265: try {
266: ((Connection) obj).close();
267: } catch (SQLException ignore) {
268: //ignore it
269: }
270: }
271: }
272:
273: }
|