001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
006: * Contact: sequoia@continuent.org
007: *
008: * Licensed under the Apache License, Version 2.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: * Initial developer(s): Emmanuel Cecchet.
021: * Contributor(s): Mathieu Peltier.
022: */package org.continuent.sequoia.controller.connection;
023:
024: import java.sql.Connection;
025: import java.sql.SQLException;
026: import java.util.ArrayList;
027: import java.util.ConcurrentModificationException;
028: import java.util.Iterator;
029: import java.util.LinkedList;
030: import java.util.Map;
031:
032: import org.continuent.sequoia.common.i18n.Translate;
033:
034: /**
035: * This connection manager uses a pool of persistent connections with the
036: * database. The allocation/release policy is implemented by the subclasses
037: * (abstract
038: * {@link org.continuent.sequoia.controller.connection.AbstractConnectionManager#getConnection()}/
039: * {@link org.continuent.sequoia.controller.connection.AbstractConnectionManager#releaseConnection(PooledConnection)}
040: * from
041: * {@link org.continuent.sequoia.controller.connection.AbstractConnectionManager}).
042: *
043: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
044: * @author <a href="mailto:Mathieu.Peltier@inrialpes.fr">Mathieu Peltier </a>
045: * @version 1.0
046: */
047: public abstract class AbstractPoolConnectionManager extends
048: AbstractConnectionManager {
049: //
050: // How the code is organized ?
051: //
052: // 1. Member variables
053: // 2. Constructor(s)
054: // 3. Connection handling
055: // 4. Getter/Setter (possibly in alphabetical order)
056: //
057:
058: /** Stack of available <code>PooledConnection</code> */
059: protected transient LinkedList freeConnections;
060:
061: /**
062: * Pool of currently used connections (<code>Vector</code> type because
063: * synchronisation is needed). Uses <code>PooledConnection</code> objects.
064: */
065: protected transient ArrayList activeConnections;
066:
067: /** Size of the connection pool with the real database. */
068: protected int poolSize;
069:
070: /*
071: * Constructor(s)
072: */
073:
074: /**
075: * Creates a new <code>AbstractPoolConnectionManager</code> instance.
076: *
077: * @param backendUrl URL of the <code>DatabaseBackend</code> owning this
078: * connection manager.
079: * @param backendName name of the <code>DatabaseBackend</code> owning this
080: * connection manager.
081: * @param login backend connection login to be used by this connection
082: * manager.
083: * @param password backend connection password to be used by this connection
084: * manager.
085: * @param driverPath path for driver
086: * @param driverClassName class name for driver
087: * @param poolSize size of the connection pool.
088: */
089: public AbstractPoolConnectionManager(String backendUrl,
090: String backendName, String login, String password,
091: String driverPath, String driverClassName, int poolSize) {
092: super (backendUrl, backendName, login, password, driverPath,
093: driverClassName);
094:
095: // VariableConnectionPool can be initialized with no connections
096: if (!(this instanceof VariablePoolConnectionManager)
097: && poolSize < 1)
098: throw new IllegalArgumentException(
099: "Illegal value for size of the pool connection manager: "
100: + poolSize);
101:
102: this .poolSize = poolSize;
103: this .freeConnections = new LinkedList();
104: this .activeConnections = new ArrayList(poolSize);
105: this .initialized = false;
106:
107: if (logger.isDebugEnabled())
108: logger.debug(Translate.get(
109: "connection.backend.pool.created", new String[] {
110: backendName, String.valueOf(poolSize) }));
111: }
112:
113: /*
114: * Connection handling
115: */
116:
117: /**
118: * @see org.continuent.sequoia.controller.connection.AbstractConnectionManager#doConnectionInitialization()
119: */
120: protected synchronized void doConnectionInitialization()
121: throws SQLException {
122: doConnectionInitialization(poolSize);
123: if (idlePersistentConnectionPingInterval > 0) {
124: persistentConnectionPingerThread = new IdlePersistentConnectionsPingerThread(
125: backendName, this );
126: persistentConnectionPingerThread.start();
127: idlePersistentConnectionPingRunning = true;
128: }
129: }
130:
131: /**
132: * Initialize initPoolSize connections in the pool.
133: *
134: * @param initPoolSize number of connections to initialize
135: * @throws SQLException if an error occurs
136: */
137: protected synchronized void doConnectionInitialization(
138: int initPoolSize) throws SQLException {
139: if (initialized)
140: throw new SQLException("Connection pool for backend '"
141: + backendUrl + "' already initialized");
142:
143: if (initPoolSize > poolSize) {
144: logger.warn(Translate.get(
145: "connection.max.poolsize.reached", new String[] {
146: String.valueOf(initPoolSize),
147: String.valueOf(poolSize),
148: String.valueOf(poolSize) }));
149: initPoolSize = poolSize;
150: }
151:
152: poolSize = initConnections(initPoolSize);
153: initialized = true;
154:
155: if (poolSize == 0) // Should never happen
156: logger.error(Translate.get("connection.empty.pool"));
157: if (logger.isDebugEnabled())
158: logger.debug(Translate.get("connection.pool.initialized",
159: new String[] { String.valueOf(initPoolSize),
160: backendUrl }));
161:
162: }
163:
164: /**
165: * initConnections initializes the requested number of connections on the
166: * backend
167: *
168: * @param initPoolSize the requested number of connections to create
169: * @return the number of connections that were created
170: */
171: protected int initConnections(int initPoolSize) {
172: Connection c = null;
173:
174: boolean connectionsAvailable = true;
175: int i = 0;
176: while ((i < initPoolSize) && connectionsAvailable) {
177: c = getConnectionFromDriver();
178:
179: if (c == null)
180: connectionsAvailable = false;
181:
182: if (!connectionsAvailable) {
183: if (i > 0) {
184: logger.warn(Translate.get(
185: "connection.limit.poolsize", i));
186: } else {
187: logger.warn(Translate
188: .get("connection.initialize.pool.failed"));
189: poolSize = 0;
190: }
191: } else {
192: PooledConnection pc = new PooledConnection(c);
193: freeConnections.addLast(pc);
194: i++;
195: }
196: }
197: return i;
198: }
199:
200: /**
201: * @see org.continuent.sequoia.controller.connection.AbstractConnectionManager#doConnectionFinalization()
202: */
203: protected synchronized void doConnectionFinalization()
204: throws SQLException {
205: if (!initialized) {
206: String msg = Translate
207: .get("connection.pool.not.initialized");
208: logger.error(msg);
209: throw new SQLException(msg);
210: }
211:
212: PooledConnection c;
213: boolean error = false;
214:
215: // Close free connections
216: initialized = false;
217: int freed = 0;
218: while (!freeConnections.isEmpty()) {
219: c = (PooledConnection) freeConnections.removeLast();
220: try {
221: c.close();
222: } catch (SQLException e) {
223: error = true;
224: }
225: freed++;
226: }
227: if (logger.isInfoEnabled())
228: logger
229: .info(Translate.get("connection.freed.connection",
230: new String[] { String.valueOf(freed),
231: backendUrl }));
232:
233: // Close active connections
234: int size = activeConnections.size();
235: if (size > 0) {
236: logger.warn(Translate.get(
237: "connection.connections.still.active", size));
238: for (int i = 0; i < size; i++) {
239: c = (PooledConnection) activeConnections.get(i);
240:
241: // Try to remove connection from persistent connection map.
242: // This calls for a more evolved data structure such as a "two-way" map.
243: // If somebody closes a persistent connection while we are in the
244: // just retry
245: boolean retry;
246: do {
247: retry = false;
248: try {
249: for (Iterator iter = persistentConnections
250: .entrySet().iterator(); iter.hasNext();) {
251: Map.Entry element = (Map.Entry) iter.next();
252: if (element.getValue() == c) {
253: iter.remove();
254: }
255: }
256: } catch (ConcurrentModificationException e) {
257: retry = true;
258: }
259: } while (retry);
260: /*
261: * Closing a connection while it is use can lead to problems.
262: * MySQL can deadlock and Sybase produces NPEs. Either the request
263: * will complete and the Connection will be closed or the
264: */
265:
266: // try
267: // {
268: // c.close();
269: // }
270: // catch (SQLException e)
271: // {
272: // error = true;
273: // }
274: }
275: }
276:
277: // Clear connections to ensure that the eventually not closed connections
278: // will be closed when the objects will be garbage collected
279: freeConnections.clear();
280: activeConnections.clear();
281: this .notifyAll();
282: System.gc();
283:
284: if (error) {
285: String msg = Translate
286: .get("connection.free.connections.failed");
287: logger.error(msg);
288: throw new SQLException(msg);
289: }
290: }
291:
292: /**
293: * @see org.continuent.sequoia.controller.connection.AbstractConnectionManager#flagAllConnectionsForRenewal()
294: */
295: public synchronized void flagAllConnectionsForRenewal() {
296: if (!initialized) {
297: String msg = Translate
298: .get("connection.pool.not.initialized");
299: logger.error(msg);
300: throw new RuntimeException(msg);
301: }
302:
303: for (Iterator iter = freeConnections.iterator(); iter.hasNext();) {
304: PooledConnection c = (PooledConnection) iter.next();
305: c.setMustBeRenewed(true);
306: }
307:
308: for (Iterator iter = activeConnections.iterator(); iter
309: .hasNext();) {
310: PooledConnection c = (PooledConnection) iter.next();
311: c.setMustBeRenewed(true);
312: }
313: }
314:
315: /**
316: * @see org.continuent.sequoia.controller.connection.AbstractConnectionManager#getCurrentNumberOfConnections()
317: */
318: public int getCurrentNumberOfConnections() {
319: return poolSize;
320: }
321:
322: /**
323: * @see org.continuent.sequoia.controller.connection.AbstractConnectionManager#releasePersistentConnection(org.continuent.sequoia.controller.connection.PooledConnection)
324: */
325: protected void releasePersistentConnection(PooledConnection c) {
326: deleteConnection(c);
327: }
328: }
|