001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: ConnectionPool.java 3634 2007-01-08 21:42:24Z gbevin $
007: */
008: package com.uwyn.rife.database;
009:
010: import com.uwyn.rife.database.DbConnection;
011: import com.uwyn.rife.database.exceptions.DatabaseException;
012: import java.util.ArrayList;
013: import java.util.HashMap;
014:
015: /**
016: * This is a class designed for database connection pooling. By storing
017: * connections, along with the thread that they are assigned to, thread-aware
018: * operations can be performed safely, securely, and more efficiently.
019: *
020: * @author JR Boyens (jboyens[remove] at uwyn dot com)
021: * @author Geert Bevin (gbevin[remove] at uwyn dot com)
022: * @version $Revision: 3634 $
023: * @since 1.0
024: */
025: public class ConnectionPool {
026: private int mPoolsize = 0;
027: private ArrayList<DbConnection> mConnectionPool = new ArrayList<DbConnection>();
028: private HashMap<Thread, DbConnection> mThreadConnections = new HashMap<Thread, DbConnection>();
029:
030: /**
031: * Create a new ConnectionPool
032: *
033: * @since 1.0
034: */
035: ConnectionPool() {
036: }
037:
038: /**
039: * Set the size of the connection pool
040: *
041: * @param poolsize the new size of the pool
042: * @since 1.0
043: */
044: void setPoolsize(int poolsize) {
045: synchronized (this ) {
046: if (mConnectionPool.size() > 0) {
047: cleanup();
048: }
049:
050: mPoolsize = poolsize;
051: }
052: }
053:
054: /**
055: * Get the size of the connection pool
056: *
057: * @return int the size of the connection pool
058: * @since 1.0
059: */
060: int getPoolsize() {
061: return mPoolsize;
062: }
063:
064: /**
065: * Check if the connection pool is initialized
066: *
067: * @return boolean true if initialized; false if not
068: * @since 1.0
069: */
070: boolean isInitialized() {
071: return mConnectionPool.size() > 0;
072: }
073:
074: /**
075: * Fill the pool with connections. Prepare the pool by filling it with
076: * connections from the provided datasource
077: *
078: * @param datasource the {@link Datasource} to fill the pool with
079: * connections from
080: * @exception DatabaseException when an error occured during the
081: * preparation of the pool
082: * @since 1.0
083: */
084: void preparePool(Datasource datasource) throws DatabaseException {
085: synchronized (this ) {
086: cleanup();
087:
088: mConnectionPool.ensureCapacity(mPoolsize);
089: for (int i = 0; i < mPoolsize; i++) {
090: mConnectionPool.add(datasource.createConnection());
091: }
092:
093: assert mPoolsize == mConnectionPool.size();
094: this .notifyAll();
095: }
096: }
097:
098: /**
099: * Cleans up all connections that have been reserved by this
100: * datasource.
101: *
102: * @exception DatabaseException when an error occured during the
103: * clearing of the pool
104: * @since 1.0
105: */
106: public void cleanup() throws DatabaseException {
107: synchronized (this ) {
108: if (0 == mConnectionPool.size()) {
109: return;
110: }
111:
112: ArrayList<DbConnection> previous_pool = null;
113:
114: previous_pool = mConnectionPool;
115: mConnectionPool = new ArrayList<DbConnection>();
116:
117: if (previous_pool != null) {
118: for (DbConnection connection : previous_pool) {
119: connection.cleanup();
120: }
121:
122: previous_pool.clear();
123: }
124:
125: mThreadConnections.clear();
126: }
127: }
128:
129: /**
130: * Remembers which connection has been reserved for a particular
131: * thread. This makes sequential operations within the same
132: * transaction in the same thread be executed on the same connection.
133: * Otherwise, transaction deadlocks might appear.
134: *
135: * @param thread the {@link Thread} to which the connection should be
136: * registered to
137: * @param connection the {@link DbConnection} that should be
138: * registered to the thread
139: * @since 1.0
140: */
141: void registerThreadConnection(Thread thread, DbConnection connection) {
142: synchronized (this ) {
143: mThreadConnections.put(thread, connection);
144: }
145: }
146:
147: /**
148: * Removes the dedication of a connection for a specific thread.
149: *
150: * @param thread the {@link Thread} whose {@link DbConnection} should
151: * be unregistered.
152: * @since 1.0
153: */
154: void unregisterThreadConnection(Thread thread) {
155: synchronized (this ) {
156: mThreadConnections.remove(thread);
157: this .notifyAll();
158: }
159: }
160:
161: /**
162: * Check if a connection reserved for a specific thread.
163: *
164: * @param thread the {@link Thread} to check for a reserved connection
165: * @return true if the passed-in thread has a connection; false if not
166: * @since 1.0
167: */
168: boolean hasThreadConnection(Thread thread) {
169: synchronized (this ) {
170: return mThreadConnections.containsKey(thread);
171: }
172: }
173:
174: /**
175: * Recreate the connection.
176: *
177: * @param connection the {@link DbConnection} to be recreated
178: * @exception DatabaseException when there is a problem recreating the
179: * connection or cleaning up the old connection
180: * @since 1.0
181: */
182: void recreateConnection(DbConnection connection)
183: throws DatabaseException {
184: synchronized (this ) {
185: if (mConnectionPool.remove(connection)) {
186: mConnectionPool.add(connection.getDatasource()
187: .createConnection());
188: }
189: connection.cleanup();
190: }
191: }
192:
193: /**
194: * Retrieve this thread's connection.
195: * <p>Connections are allocated from the pool and assigned to the
196: * current calling thread. If the thread does not have a current
197: * connection or the pool size is 0, then a new connection is created
198: * and assigned to the calling thread.
199: *
200: * @param datasource the datasource to create the connection to
201: * @exception DatabaseException when an error occurred retrieving or
202: * creating the connection
203: * @return the created or retrieved DbConnection
204: * @since 1.0
205: */
206: DbConnection getConnection(Datasource datasource)
207: throws DatabaseException {
208: synchronized (this ) {
209: // check if the connection threads contains an entry for the
210: // current thread so that transactions are treated in a
211: // continuous fashion
212: if (mThreadConnections.containsKey(Thread.currentThread())) {
213: DbConnection connection = mThreadConnections.get(Thread
214: .currentThread());
215: if (connection != null) {
216: return connection;
217: }
218: }
219:
220: // if there's no pool, create a new connection
221: if (0 == mPoolsize) {
222: return datasource.createConnection();
223: }
224: // otherwise, try to obtain a free connection in the pool
225: else {
226: DbConnection connection = null;
227:
228: // iterate over the available connections and try to obtain the
229: // first free one
230: DbConnection possible_connection = null;
231: while (null == connection) {
232: // prepare the pool if it's currently empty
233: if (mConnectionPool.size() < mPoolsize) {
234: preparePool(datasource);
235: }
236:
237: for (int i = 0; i < mConnectionPool.size()
238: && null == connection; i++) {
239: possible_connection = mConnectionPool.get(i);
240: if (null == possible_connection
241: || possible_connection.isCleanedUp()) {
242: connection = datasource.createConnection();
243: mConnectionPool.set(i, connection);
244: break;
245: } else if (null != possible_connection
246: && possible_connection.isFree()) {
247: connection = possible_connection;
248: break;
249: }
250: }
251:
252: if (null == connection) {
253: try {
254: this .wait();
255: } catch (InterruptedException e) {
256: Thread.yield();
257: }
258: }
259: }
260:
261: // move the obtained connection to the end of the connection
262: // pool list
263: mConnectionPool.remove(connection);
264: mConnectionPool.add(connection);
265:
266: return connection;
267: }
268: }
269: }
270:
271: /**
272: * Ensures that the pool is cleared when this <code>Datasource</code>
273: * is garbage collected.
274: *
275: * @exception Throwable when an error occured during the clearing of
276: * the pool
277: * @since 1.0
278: */
279: protected void finalize() throws Throwable {
280: cleanup();
281:
282: super.finalize();
283: }
284: }
|