EMMA Coverage Report (generated Sun May 02 20:42:29 CEST 2010)
[all classes][hu.netmind.beankeeper.db.impl]

COVERAGE SUMMARY FOR SOURCE FILE [ConnectionSourceImpl.java]

nameclass, %method, %block, %line, %
ConnectionSourceImpl.java75%  (3/4)86%  (12/14)80%  (315/394)74%  (71.6/97)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ConnectionSourceImpl100% (1/1)80%  (8/10)77%  (271/350)71%  (61.6/87)
closeWrapper (ConnectionSourceImpl$ConnectionWrapper): void 0%   (0/1)0%   (0/21)0%   (0/7)
getDataSource (): DataSource 0%   (0/1)0%   (0/3)0%   (0/1)
configurationChanged (ConfigurationEvent): void 100% (1/1)75%  (6/8)67%  (2/3)
isDataSourcePooled (): boolean 100% (1/1)75%  (6/8)75%  (0.8/1)
getConnection (): Connection 100% (1/1)79%  (160/203)69%  (29.9/43)
releaseConnection (Connection): void 100% (1/1)83%  (15/18)83%  (5/6)
release (): void 100% (1/1)87%  (33/38)80%  (8/10)
<static initializer> 100% (1/1)100% (6/6)100% (2/2)
ConnectionSourceImpl (ConfigurationTracker, SnapshotLogger, DataSource): void 100% (1/1)100% (36/36)100% (12/12)
configurationReload (): void 100% (1/1)100% (9/9)100% (2/2)
     
class ConnectionSourceImpl$10%   (0/1)100% (0/0)100% (0/0)100% (0/0)
     
class ConnectionSourceImpl$ConnectionWrapper100% (1/1)100% (2/2)100% (19/19)100% (4/4)
ConnectionSourceImpl$ConnectionWrapper (ConnectionSourceImpl): void 100% (1/1)100% (15/15)100% (4/4)
ConnectionSourceImpl$ConnectionWrapper (ConnectionSourceImpl, ConnectionSourc... 100% (1/1)100% (4/4)100% (1/1)
     
class ConnectionSourceImpl$WrapperHandler100% (1/1)100% (2/2)100% (25/25)100% (6/6)
ConnectionSourceImpl$WrapperHandler (ConnectionSourceImpl, ConnectionSourceIm... 100% (1/1)100% (9/9)100% (3/3)
invoke (Object, Method, Object []): Object 100% (1/1)100% (16/16)100% (3/3)

1/**
2 * Copyright (C) 2006 NetMind Consulting Bt.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19package hu.netmind.beankeeper.db.impl;
20 
21import java.util.LinkedList;
22import java.util.HashMap;
23import java.sql.Connection;
24import javax.sql.DataSource;
25import java.sql.DatabaseMetaData;
26import java.util.Iterator;
27import java.lang.reflect.Proxy;
28import java.lang.reflect.InvocationHandler;
29import java.lang.reflect.Method;
30import org.apache.log4j.Logger;
31import org.apache.commons.configuration.event.ConfigurationEvent;
32import hu.netmind.beankeeper.common.StoreException;
33import hu.netmind.beankeeper.db.ConnectionSource;
34import hu.netmind.beankeeper.config.ExtendedConfigurationListener;
35import hu.netmind.beankeeper.config.ConfigurationTracker;
36import hu.netmind.beankeeper.logging.SnapshotLogger;
37 
38/**
39 * This is the connection source for database implementations. It pools
40 * the connections so the database implementation does not need to
41 * bother with connection allocation details. Be sure to return the
42 * connection to this pool when ready.
43 * @author Brautigam Robert
44 * @version Revision: $Revision$
45 */
46public class ConnectionSourceImpl implements ConnectionSource,ExtendedConfigurationListener
47{
48   private static Logger logger = Logger.getLogger(ConnectionSource.class);
49   
50   // Timeout for a single connection before it gets dumped.
51   // Note: This could be exchanged for validating a connection, but
52   // there is no certain way to do it, except running an sql statement.
53   private static long TIMEOUT = 10*60*1000; // 10 mins
54      
55   private DataSource dataSource;
56 
57   private int maxConnections;
58   private LinkedList pool;
59   private HashMap wrappers;
60 
61   private ConfigurationTracker configurationTracker = null;
62   private SnapshotLogger snapshotLogger = null;
63 
64   public ConnectionSourceImpl(ConfigurationTracker configurationTracker, SnapshotLogger snapshotLogger, DataSource dataSource)
65   {
66      // Initialize variables
67      this.dataSource=dataSource;
68      this.configurationTracker=configurationTracker;
69      this.snapshotLogger=snapshotLogger;
70      pool = new LinkedList();
71      wrappers = new HashMap();
72      // Set max connection count to infinite for start
73      maxConnections = 0;
74      // Load configuration
75      configurationReload();
76      configurationTracker.addListener(this);
77   }
78 
79   DataSource getDataSource()
80   {
81      return dataSource;
82   }
83 
84   /**
85    * Returns whether the datasource is pooled. Currently, all
86    * datasources allocated through JNDI are considered pooled.
87    */
88   private boolean isDataSourcePooled()
89   {
90      return ! (dataSource instanceof DriverDataSource);
91   }
92 
93   /**
94    * Release all database connections. After calling this method, the
95    * connection source is considered unusable.
96    */
97   public synchronized void release()
98   {
99      // Close all connections
100      for ( int i=0; i<pool.size(); i++)
101      {
102         ConnectionWrapper wrapper = (ConnectionWrapper) pool.get(i);
103         try
104         {
105            if ( wrapper.connection != null )
106               wrapper.connection.close();
107         } catch ( Exception e ) {
108            logger.error("could not close connection on release.",e);
109         }
110      }
111      // Empty pool
112      pool = new LinkedList();
113      wrappers = new HashMap();
114   }
115 
116   /**
117    * Get a new pooled connection. This method get a connection from the
118    * pool, or allocates a new connection is no pooled ones are available.
119    * @return A new connection object.
120    */
121   public synchronized Connection getConnection()
122   {
123      // Find an available connection, if a connection becomes unusable, it
124      // is removed
125      long currentTime = System.currentTimeMillis();
126      ConnectionWrapper wrapper = null;
127      Iterator iterator = new LinkedList(pool).iterator();
128      int usedCount = 0;
129      while ( iterator.hasNext() )
130      {
131         ConnectionWrapper current = (ConnectionWrapper) iterator.next();
132         boolean usable = true;
133         // If the connection is pooled elsewhere, and it is used,
134         // then we have no business with it
135         if ( (current.used) && (isDataSourcePooled()) )
136            continue;
137         // Check whether connection became closed somehow
138         try
139         {
140            usable = ! current.connection.isClosed();
141         } catch ( Exception e ) {
142            // Ok, probably closed
143            logger.debug("while checking closed status, connection threw",e);
144            usable = false;            
145         }
146         // Check for timeout
147         if ( current.lastUsed+TIMEOUT < currentTime )
148         {
149            usable = false;
150            if ( current.used )
151               logger.warn("a connection is used, but reached timeout and will be reclaimed, possible unbalanced transaction handling!");
152         }
153         // If usable, then return this wrapper, else close it
154         if ( usable )
155         {
156            if ( ! current.used )
157            {
158               // This is usable but not used, so return it. Next iterations 
159               // can override this still, so always the last usable 
160               // connection will be used.
161               wrapper = current; 
162            } else {
163               // Connection is used
164               usedCount++;
165            }
166         }
167      }
168      // Allocate connection if not yet found
169      if ( wrapper == null )
170      {
171         // Allocate connection
172         wrapper = new ConnectionWrapper();
173         try
174         {
175            logger.debug("connection pool has: "+pool.size()+
176                  " connections, maximum connections allocated at any given time: "+maxConnections+", need new connection.");
177            wrapper.connection=dataSource.getConnection();
178            wrapper.connection.setAutoCommit(false);
179         } catch ( StoreException e ) {
180            throw e;
181         } catch ( Exception e ) {
182            throw new StoreException("could not get a new connection from datasouce, current pool size: "+pool.size(),e);
183         }
184         // Add to pool
185         pool.add(wrapper);
186         wrappers.put(wrapper.connection,wrapper);
187         // Successful connection
188         if ( pool.size() > maxConnections )
189            maxConnections = pool.size();
190      }
191      // Profile
192      snapshotLogger.log("connectionpool","Connection used/pool/wrappers: "+usedCount+"/"+pool.size()+"/"+wrappers.size());
193      // Return connection if one has been found
194      wrapper.used=true;
195      wrapper.lastUsed=currentTime;
196      // Create a wrapper on the connection object, so each use
197      // of it's prepareStatement() can update it's wrapper's last used time.
198      try
199      {
200         return (Connection) Proxy.newProxyInstance(getClass().getClassLoader(),
201               new Class[] { Connection.class },
202               new WrapperHandler(wrapper));
203      } catch ( Exception e ) {
204         throw new StoreException("exception while creating connection proxy",e);
205      }
206   }
207 
208   /**
209    * Close a wrapper, and drop from pool.
210    */
211   private void closeWrapper(ConnectionWrapper wrapper)
212   {
213      pool.remove(wrapper);
214      wrappers.remove(wrapper.connection);
215      // Close it too
216      try
217      {
218         wrapper.connection.close();
219      } catch ( Exception e ) {
220         logger.debug("could not close wrapper, this is no problem, it may already been closed",e);
221      }
222   }
223 
224   /**
225    * Release a connection back to the pool.
226    * @param connection The connection to release.
227    */
228   public synchronized void releaseConnection(Connection connection)
229   {
230      ConnectionWrapper wrapper = (ConnectionWrapper) wrappers.get(connection);
231      if ( wrapper != null )
232         wrapper.used=false;
233      // If the data source is pool, then drop this wrapper and
234      // do not return it to the pool
235      if ( isDataSourcePooled() )
236         closeWrapper(wrapper);
237   }
238   
239   private class WrapperHandler implements InvocationHandler
240   {
241      private ConnectionWrapper wrapper;
242 
243      public WrapperHandler(ConnectionWrapper wrapper)
244      {
245         this.wrapper=wrapper;
246      }
247      
248      public Object invoke(Object proxy, Method method, Object[] args)
249         throws Throwable
250      {
251         // If method is prepareStatement(), then update wrapper's
252         // last used indicator.
253         if ( method.getName().equals("prepareStatement") )
254            wrapper.lastUsed = System.currentTimeMillis();
255         // Call wrapped connection
256         return method.invoke(wrapper.connection,args);
257      }
258   }
259   
260   private class ConnectionWrapper
261   {
262      public volatile boolean used = false;
263      public volatile long lastUsed = 0;
264      public Connection connection = null;
265   }
266 
267   public void configurationChanged(ConfigurationEvent event)
268   {
269      if ( "beankeeper.pool.connection_timeout".equals(event.getPropertyName()) )
270         configurationReload();
271   }
272 
273   public void configurationReload()
274   {
275      TIMEOUT = configurationTracker.getConfiguration().
276         getInt("beankeeper.pool.connection_timeout",10*60*1000);
277   }
278}
279 
280 
281 

[all classes][hu.netmind.beankeeper.db.impl]
EMMA 2.0.5312debian (C) Vladimir Roubtsov