View Javadoc

1   package org.apache.ojb.broker.accesslayer;
2   
3   /* Copyright 2002-2005 The Apache Software Foundation
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * 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  import org.apache.commons.pool.BasePoolableObjectFactory;
19  import org.apache.commons.pool.ObjectPool;
20  import org.apache.commons.pool.PoolableObjectFactory;
21  import org.apache.commons.pool.impl.GenericObjectPool;
22  import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
23  import org.apache.ojb.broker.util.logging.Logger;
24  import org.apache.ojb.broker.util.logging.LoggerFactory;
25  import org.apache.ojb.broker.OJBRuntimeException;
26  
27  import java.sql.Connection;
28  import java.sql.ResultSet;
29  import java.sql.SQLException;
30  import java.sql.PreparedStatement;
31  import java.util.Collection;
32  import java.util.HashMap;
33  import java.util.Iterator;
34  import java.util.Map;
35  import java.util.NoSuchElementException;
36  
37  /**
38   * Connection factory which pools the requested
39   * connections for different JdbcConnectionDescriptors
40   * using Commons Pool API.
41   *
42   * @version $Id: ConnectionFactoryPooledImpl.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
43   * @see <a href="http://jakarta.apache.org/commons/pool/">Commons Pool Website</a>
44   */
45  public class ConnectionFactoryPooledImpl extends ConnectionFactoryAbstractImpl
46  {
47  
48      private Logger log = LoggerFactory.getLogger(ConnectionFactoryPooledImpl.class);
49      /** Key=PBKey, value=ObjectPool. */
50      private Map poolMap = new HashMap();
51      /** Synchronize object for operations not synchronized on Map only. */
52      private final Object poolSynch = new Object();
53  
54      public void releaseJdbcConnection(JdbcConnectionDescriptor jcd, Connection con)
55              throws LookupException
56      {
57          final ObjectPool op = (ObjectPool) poolMap.get(jcd.getPBKey());
58          try
59          {
60              /* mkalen: NB - according to the Commons Pool API we should _not_ perform
61               * any additional checks here since we will then break testOnX semantics
62               *
63               * To enable Connection validation on releaseJdbcConnection,
64               * set a validation query and specify testOnRelease=true
65               *
66               * Destruction of pooled objects is performed by the actual Commons Pool
67               * ObjectPool implementation when the object factory's validateObject method
68               * returns false. See ConPoolFactory#validateObject.
69               */
70              op.returnObject(con);
71          }
72          catch (Exception e)
73          {
74              throw new LookupException(e);
75          }
76      }
77  
78      public Connection checkOutJdbcConnection(JdbcConnectionDescriptor jcd) throws LookupException
79      {
80          ObjectPool op = (ObjectPool) poolMap.get(jcd.getPBKey());
81          if (op == null)
82          {
83              synchronized (poolSynch)
84              {
85                  log.info("Create new connection pool:" + jcd);
86                  op = createConnectionPool(jcd);
87                  poolMap.put(jcd.getPBKey(), op);
88              }
89          }
90          final Connection conn;
91          try
92          {
93              conn = (Connection) op.borrowObject();
94          }
95          catch (NoSuchElementException e)
96          {
97              int active = 0;
98              int idle = 0;
99              try
100             {
101                 active = op.getNumActive();
102                 idle = op.getNumIdle();
103             }
104             catch(Exception ignore){}
105             throw new LookupException("Could not borrow connection from pool, seems ObjectPool is exhausted." +
106                     " Active/Idle instances in pool=" + active + "/" +  idle
107                     + ". "+ JdbcConnectionDescriptor.class.getName() + ":  " + jcd, e);
108         }
109         catch (Exception e)
110         {
111             int active = 0;
112             int idle = 0;
113             try
114             {
115                 active = op.getNumActive();
116                 idle = op.getNumIdle();
117             }
118             catch(Exception ignore){}
119             throw new LookupException("Could not borrow connection from pool." +
120                     " Active/Idle instances in pool=" + active + "/" +  idle
121                     + ". "+ JdbcConnectionDescriptor.class.getName() + ":  " + jcd, e);
122         }
123         return conn;
124     }
125 
126     /**
127      * Create the pool for pooling the connections of the given connection descriptor.
128      * Override this method to implement your on {@link org.apache.commons.pool.ObjectPool}.
129      */
130     public ObjectPool createConnectionPool(JdbcConnectionDescriptor jcd)
131     {
132         if (log.isDebugEnabled()) log.debug("createPool was called");
133         PoolableObjectFactory pof = new ConPoolFactory(this, jcd);
134         GenericObjectPool.Config conf = jcd.getConnectionPoolDescriptor().getObjectPoolConfig();
135         return (ObjectPool)new GenericObjectPool(pof, conf);
136     }
137 
138     /**
139      * Closes all managed pools.
140      */
141     public void releaseAllResources()
142     {
143         synchronized (poolSynch)
144         {
145             Collection pools = poolMap.values();
146             poolMap = new HashMap(poolMap.size());
147             ObjectPool op = null;
148             for (Iterator iterator = pools.iterator(); iterator.hasNext();)
149             {
150                 try
151                 {
152                     op = ((ObjectPool) iterator.next());
153                     op.close();
154                 }
155                 catch (Exception e)
156                 {
157                     log.error("Exception occured while closing pool " + op, e);
158                 }
159             }
160         }
161         super.releaseAllResources();
162     }
163 
164     //**************************************************************************************
165     // Inner classes
166     //************************************************************************************
167 
168     /**
169      * Inner class - {@link org.apache.commons.pool.PoolableObjectFactory}
170      * used as factory for connection pooling.
171      */
172     class ConPoolFactory extends BasePoolableObjectFactory
173     {
174         final private JdbcConnectionDescriptor jcd;
175         final private ConnectionFactoryPooledImpl cf;
176         private int failedValidationQuery;
177 
178         public ConPoolFactory(ConnectionFactoryPooledImpl cf, JdbcConnectionDescriptor jcd)
179         {
180             this.cf = cf;
181             this.jcd = jcd;
182         }
183 
184         public boolean validateObject(Object obj)
185         {
186             boolean isValid = false;
187             if (obj != null)
188             {
189                 final Connection con = (Connection) obj;
190                 try
191                 {
192                     isValid = !con.isClosed();
193                 }
194                 catch (SQLException e)
195                 {
196                     log.warn("Connection validation failed: " + e.getMessage());
197                     if (log.isDebugEnabled()) log.debug(e);
198                     isValid = false;
199                 }
200                 if (isValid)
201                 {
202                     final String validationQuery;
203                     validationQuery = jcd.getConnectionPoolDescriptor().getValidationQuery();
204                     if (validationQuery != null)
205                     {
206                         isValid = validateConnection(con, validationQuery);
207                     }
208                 }
209             }
210             return isValid;
211         }
212 
213         private boolean validateConnection(Connection conn, String query)
214         {
215             PreparedStatement stmt = null;
216             ResultSet rset = null;
217             boolean isValid = false;
218             if (failedValidationQuery > 100)
219             {
220                 --failedValidationQuery;
221                 throw new OJBRuntimeException("Validation of connection "+conn+" using validation query '"+
222                         query + "' failed more than 100 times.");
223             }
224             try
225             {
226                 stmt = conn.prepareStatement(query);
227                 stmt.setMaxRows(1);
228                 stmt.setFetchSize(1);
229                 rset = stmt.executeQuery();
230                 if (rset.next())
231                 {
232                     failedValidationQuery = 0;
233                     isValid = true;
234                 }
235                 else
236                 {
237                     ++failedValidationQuery;
238                     log.warn("Validation query '" + query +
239                             "' result set does not match, discard connection");
240                     isValid = false;
241                 }
242             }
243             catch (SQLException e)
244             {
245                 ++failedValidationQuery;
246                 log.warn("Validation query for connection failed, discard connection. Query was '" +
247                         query + "', Message was " + e.getMessage());
248                 if (log.isDebugEnabled()) log.debug(e);
249             }
250             finally
251             {
252                 try
253                 {
254                     if(rset != null) rset.close();
255                 }
256                 catch (SQLException t)
257                 {
258                     if (log.isDebugEnabled()) log.debug("ResultSet already closed.", t);
259                 }
260                 try
261                 {
262                     if(stmt != null) stmt.close();
263                 }
264                 catch (SQLException t)
265                 {
266                     if (log.isDebugEnabled()) log.debug("Statement already closed.", t);
267                 }
268             }
269             return isValid;
270         }
271 
272         public Object makeObject() throws Exception
273         {
274             if (log.isDebugEnabled()) log.debug("makeObject called");
275             return cf.newConnectionFromDriverManager(jcd);
276         }
277 
278         public void destroyObject(Object obj)
279                 throws Exception
280         {
281             log.info("Destroy object was called, try to close connection: " + obj);
282             try
283             {
284                 ((Connection) obj).close();
285             }
286             catch (SQLException ignore)
287             {
288                 //ignore it
289             }
290         }
291     }
292 
293 }