KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > myvietnam > mvncore > db > DBConnectionManager


1 /*
2  * $Header: /cvsroot/mvnforum/myvietnam/src/net/myvietnam/mvncore/db/DBConnectionManager.java,v 1.18.2.1 2006/04/15 15:06:34 minhnn Exp $
3  * $Author: minhnn $
4  * $Revision: 1.18.2.1 $
5  * $Date: 2006/04/15 15:06:34 $
6  *
7  * ====================================================================
8  *
9  * Copyright (C) 2002-2006 by MyVietnam.net
10  *
11  * All copyright notices regarding MyVietnam and MyVietnam CoreLib
12  * MUST remain intact in the scripts and source code.
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27  *
28  * Correspondence and Marketing Questions can be sent to:
29  * info at MyVietnam net
30  *
31  * @author: Minh Nguyen
32  * @author: Mai Nguyen
33  */

34 package net.myvietnam.mvncore.db;
35
36 import java.sql.*;
37 import java.util.Enumeration JavaDoc;
38 import java.util.Vector JavaDoc;
39
40 import net.myvietnam.mvncore.MVNCoreConfig;
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43
44 /**
45  * This class is a Singleton that provides access to the
46  * connection pool. A client gets access to the single
47  * instance through the static getInstance() method
48  * and can then check-out and check-in connections from a pool.
49  * When the client shuts down it should call the release() method
50  * to close all opened connections and do other clean up.
51  */

52 class DBConnectionManager {
53
54     private static Log log = LogFactory.getLog(DBConnectionManager.class);
55
56     private static final int TIME_BETWEEN_RETRIES = 500; // O.5 second
57

58     // static variable
59
static private DBConnectionManager instance = null; // The single instance
60

61     // instance variable
62
private DBConnectionPool pool = null;// please be careful if u want to make this variable static
63

64     /**
65      * A private constructor since this is a Singleton
66      * Note: This constructor is lightweight since DBConnectionPool is lightweight,
67      * so no connection is created until the first time getConnection() is called
68      */

69     private DBConnectionManager() {
70         String JavaDoc driverClassName = MVNCoreConfig.getDriverClassName();
71         try {
72             Class.forName(driverClassName).newInstance();
73         } catch (Exception JavaDoc e) {
74             log.fatal("DBConnectionManager: Unable to load driver = " + driverClassName, e);
75         }
76
77         String JavaDoc url = MVNCoreConfig.getDatabaseURL();
78         String JavaDoc user = MVNCoreConfig.getDatabaseUser();
79         String JavaDoc password = MVNCoreConfig.getDatabasePassword();
80         int maxConnection = MVNCoreConfig.getMaxConnection();
81
82         //always new the pool because pool is an instance variable
83
pool = new DBConnectionPool(url, user, password, maxConnection);
84     }
85
86     /**
87      * Returns the single instance, creating one if it's the
88      * first time this method is called.
89      *
90      * @return DBConnectionManager The single instance.
91      */

92     /*
93     public static synchronized DBConnectionManager getInstance() {
94         if (instance == null) {
95             DBOptions option = new DBOptions();
96             instance = new DBConnectionManager(option);
97         }
98         return instance;
99     }*/

100
101     /**
102      * Returns the single instance, creating one if it's the
103      * first time this method is called.
104      *
105      * @return DBConnectionManager The single instance.
106      */

107     /*
108     private static synchronized DBConnectionManager getInstance(DBOptions option) {
109         if (instance == null) {
110             if (option == null) {
111                 option = new DBOptions();
112             }
113             instance = new DBConnectionManager(option);
114         }
115         return instance;
116     }*/

117
118     public static synchronized DBConnectionManager getInstance(boolean useConfig) {
119         if (instance == null) {
120             instance = new DBConnectionManager();
121         }
122         return instance;
123     }
124
125     /**
126      * Returns a connection to the pool.
127      *
128      * @param con The Connection
129      */

130     void freeConnection(Connection con) {
131         pool.freeConnection(con);
132     }
133
134     /**
135      * Returns an open connection. If no one is available, and the max
136      * number of connections has not been reached, a new connection is
137      * created.
138      *
139      * @return Connection The connection or null
140      */

141     Connection getConnection() {
142         return getConnection(0);
143     }
144
145     /**
146      * Returns an open connection. If no one is available, and the max
147      * number of connections has not been reached, a new connection is
148      * created. If the max number has been reached, waits until one
149      * is available or the specified time has elapsed.
150      *
151      * @param time The number of milliseconds to wait
152      * @return Connection The connection or null
153      */

154     Connection getConnection(long time) {
155         Connection connection = pool.getConnection(time);
156         if (connection == null) {
157             return null;
158         }
159
160         try {
161             // we always setAutoCommit(true) for backward compatible with mvnForum
162
connection.setAutoCommit(true);
163         } catch (SQLException e) {
164             log.error("Cannot setAutoCommit", e);
165         }
166         return connection;
167     }
168
169     /**
170      * Closes all open connections.
171      * @return true if the pool is empty and balance
172      * false if the pool has returned some connection to outside
173      */

174     boolean release() {
175         return pool.release();
176     }
177
178     /**
179      * This inner class represents a connection pool. It creates new
180      * connections on demand, up to a max number if specified.
181      * It also checks to make sure that the connection is still open
182      * before it is returned to a client.
183      */

184     class DBConnectionPool {
185         private int checkedOut = 0;//NOTE: this variable should be changed in synchronized method only
186
private Vector JavaDoc freeConnections = new Vector JavaDoc();
187
188         private int maxConn = 0;
189         private String JavaDoc password = null;
190         private String JavaDoc URL = null;
191         private String JavaDoc user = null;
192
193         /**
194          * Creates new connection pool.
195          * NOTE: new an instance of this class is lightweight since it does not create any connections
196          *
197          * @param URL The JDBC URL for the database
198          * @param user The database user, or null
199          * @param password The database user password, or null
200          * @param maxConn The maximal number of connections, or 0 for no limit
201          */

202         public DBConnectionPool(String JavaDoc URL, String JavaDoc user, String JavaDoc password, int maxConn) {
203             this.URL = URL;
204             this.user = user;
205             this.password = password;
206             this.maxConn = maxConn;
207         }
208
209         /**
210          * Checks in a connection to the pool. Notify other Threads that
211          * may be waiting for a connection.
212          *
213          * @todo: Maybe we dont need notifyAll(); ???
214          *
215          * @param con The connection to check in
216          */

217         synchronized void freeConnection(Connection con) {
218             // Put the connection at the end of the Vector
219
if (con != null) {//make sure that the connection is not null
220
if (checkedOut <= 0) {
221                     // this means that connection is open too much
222
// There are 2 cases:
223
// 1. Not get from this connection pool (maybe get directly)
224
// 2. this connection is gotten and then the whole pool is released
225
// In these case, just close the connection
226
try {
227                         log.debug("DBConnectionManager: about to close the orphan connection.");
228                         con.close();
229                     } catch (SQLException ex) { }
230                 } else {
231                     // Return this connection to the pool
232
// note that we dont have to check if the connection is not connected
233
// this will be check in the getConnection method
234
freeConnections.addElement(con);
235                     // FIXME: posible negative value
236
// NOTE: checkOut should never be negative here
237
checkedOut--; // NOTE: this number can be negative (in case connection does not come from the pool)
238
notifyAll(); // can I remove it ???
239
}
240             }
241         }
242
243         /**
244          * Checks out a connection from the pool. If no free connection
245          * is available, a new connection is created unless the max
246          * number of connections has been reached. If a free connection
247          * has been closed by the database, it's removed from the pool
248          * and this method is called again recursively.
249          */

250         synchronized Connection getConnection() {
251             Connection con = null;
252
253             while ( (freeConnections.size() > 0) && (con == null) ) {
254                 // Pick the first Connection in the Vector
255
// to get round-robin usage
256
con = (Connection) freeConnections.firstElement();
257                 freeConnections.removeElementAt(0);
258                 try {
259                     if (con.isClosed()) {
260                         log.info("Removed bad connection in DBConnectionPool.");
261                         con = null; // to make the while loop to continue
262
}
263                 } catch (SQLException e) {
264                     con = null; // to make the while loop to continue
265
}
266             } // while
267

268             if (con == null) {// cannot get any connection from the pool
269
if (maxConn == 0 || checkedOut < maxConn) {// maxConn = 0 means unlimited connections
270
con = newConnection();
271                 }
272             }
273             if (con != null) {
274                 checkedOut++;
275             }
276             return con;
277         }
278
279         /**
280          * Checks out a connection from the pool. If no free connection
281          * is available, a new connection is created unless the max
282          * number of connections has been reached. If a free connection
283          * has been closed by the database, it's removed from the pool
284          * and this method is called again recursively.
285          * <P>
286          * If no connection is available and the max number has been
287          * reached, this method waits the specified time for one to be
288          * checked in.
289          *
290          * @param timeout The timeout value in milliseconds
291          */

292         /**
293          * Note that this method is not synchronized since it relies on the getConnection(void) method
294          * I also believe that this method SHOULD NOT synchronized because I use #sleep() method
295          * @todo: check if we should synchronize this method and use wait instead of sleep ???
296          */

297         Connection getConnection(long timeout) {
298             long startTime = System.currentTimeMillis();
299             Connection con;
300             while ((con = getConnection()) == null) {
301                 long elapsedTime = System.currentTimeMillis() - startTime;
302                 if (elapsedTime >= timeout) {
303                     // Timeout has expired
304
return null;
305                 }
306
307                 long timeToWait = timeout - elapsedTime;
308                 if (timeToWait > TIME_BETWEEN_RETRIES) timeToWait = TIME_BETWEEN_RETRIES;// we dont want to wait for more than TIME_BETWEEN_RETRIES second each time
309
try {
310                     Thread.sleep(timeToWait);
311                 } catch (InterruptedException JavaDoc e) {}
312             }
313             return con;
314         }
315
316         /**
317          * Closes all available connections.
318          * @return true if the pool is empty and balance
319          * false if the pool has returned some connection to outside
320          */

321         synchronized boolean release() {
322             boolean retValue = true;
323             Enumeration JavaDoc allConnections = freeConnections.elements();
324             while (allConnections.hasMoreElements()) {
325                 Connection con = (Connection) allConnections.nextElement();
326                 try {
327                     con.close();
328                 } catch (SQLException e) {
329                     log.error("Cannot close connection in DBConnectionPool.");
330                 }
331             }
332             freeConnections.removeAllElements();
333             if (checkedOut != 0) {
334                 retValue = false;
335                 log.warn("DBConnectionManager: the built-in connection pool is not balanced.");
336             }
337             checkedOut = 0;
338             return retValue;
339         }
340
341         /**
342          * Creates a new connection, using a userid and password
343          * if specified.
344          * @todo: check if this method need synchronized
345          */

346         private Connection newConnection() {
347             Connection con = null;
348             try {
349                 if (user == null) {
350                     con = DriverManager.getConnection(URL);
351                 } else {
352                     con = DriverManager.getConnection(URL, user, password);
353                 }
354                 // Note that we dont need to call setAutoCommit here because we
355
// will call it at DBConnectionManager.getConnection()
356
//con.setAutoCommit(true);//thread 804 by trulore
357
} catch (SQLException e) {
358                 log.error("Cannot create a new connection in DBConnectionPool. URL = " + URL, e);
359                 return null;
360             }
361             return con;
362         }
363     }
364 }
365
Popular Tags