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): ______________________.
022: */package org.continuent.sequoia.controller.connection;
023:
024: import java.sql.Connection;
025: import java.util.NoSuchElementException;
026:
027: import org.continuent.sequoia.common.exceptions.UnreachableBackendException;
028: import org.continuent.sequoia.common.i18n.Translate;
029: import org.continuent.sequoia.common.xml.DatabasesXmlTags;
030:
031: /**
032: * This connection manager waits when the pool is empty. Requests are stacked
033: * using the Java wait/notify mechanism. Therefore the FIFO order is not
034: * guaranteed and the first request to get the freed connection is the thread
035: * that gets elected by the scheduler.
036: *
037: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
038: * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
039: * @version 1.0
040: */
041: public class RandomWaitPoolConnectionManager extends
042: AbstractPoolConnectionManager {
043: /** Time to wait for a connection in milliseconds (0 means wait forever). */
044: private int timeout;
045:
046: /**
047: * Creates a new <code>RandomWaitPoolConnectionManager</code> instance.
048: *
049: * @param backendUrl URL of the <code>DatabaseBackend</code> owning this
050: * connection manager
051: * @param backendName name of the <code>DatabaseBackend</code> owning this
052: * connection manager
053: * @param login backend connection login to be used by this connection manager
054: * @param password backend connection password to be used by this connection
055: * manager
056: * @param driverPath path for driver
057: * @param driverClassName class name for driver
058: * @param poolSize size of the connection pool
059: * @param timeout time to wait for a connection in seconds (0 means wait
060: * forever)
061: */
062: public RandomWaitPoolConnectionManager(String backendUrl,
063: String backendName, String login, String password,
064: String driverPath, String driverClassName, int poolSize,
065: int timeout) {
066: super (backendUrl, backendName, login, password, driverPath,
067: driverClassName, poolSize);
068: this .timeout = timeout * 1000;
069: }
070:
071: /**
072: * @see java.lang.Object#clone()
073: */
074: protected Object clone() throws CloneNotSupportedException {
075: return new RandomWaitPoolConnectionManager(backendUrl,
076: backendName, rLogin, rPassword, driverPath,
077: driverClassName, poolSize, timeout);
078: }
079:
080: /**
081: * @see org.continuent.sequoia.controller.connection.AbstractConnectionManager#clone(String,
082: * String)
083: */
084: public AbstractConnectionManager clone(String rLogin,
085: String rPassword) {
086: return new RandomWaitPoolConnectionManager(backendUrl,
087: backendName, rLogin, rPassword, driverPath,
088: driverClassName, poolSize, timeout);
089: }
090:
091: /**
092: * Gets the timeout.
093: *
094: * @return a <code>int</code> value.
095: */
096: public int getTimeout() {
097: return timeout;
098: }
099:
100: /**
101: * Gets a connection from the pool.
102: * <p>
103: * If the pool is empty, this methods blocks until a connection is freed or
104: * the timeout expires.
105: *
106: * @return a connection from the pool or <code>null</code> if the timeout
107: * has expired.
108: * @throws UnreachableBackendException if the backend must be disabled
109: * @see org.continuent.sequoia.controller.connection.AbstractConnectionManager#getConnection()
110: */
111: public synchronized PooledConnection getConnection()
112: throws UnreachableBackendException {
113: if (!initialized) {
114: logger
115: .error("Requesting a connection from a non-initialized connection manager");
116: return null;
117: }
118: if (isShutdown) {
119: return null;
120: }
121: long lTimeout = timeout;
122:
123: // We have to do a while loop() because there is a potential race here.
124: // When freeConnections is notified in releaseConnection, a new thread
125: // can
126: // take the lock on freeConnections before we wake up/reacquire the lock
127: // on freeConnections. Therefore, we could wake up and have no connection
128: // to take! We ensure that everything is correct with a while statement
129: // and recomputing the timeout between 2 wakeup.
130: while (freeConnections.isEmpty()) {
131: // Wait
132: try {
133: if (lTimeout > 0) {
134: long start = System.currentTimeMillis();
135: // Convert seconds to milliseconds for wait call
136: this .wait(timeout);
137: long end = System.currentTimeMillis();
138: lTimeout -= end - start;
139: if (lTimeout <= 0) {
140: if (activeConnections.size() == 0) { // No connection active and backend unreachable, the backend
141: // is probably dead
142: logger.error("Backend " + backendName
143: + " is no more accessible.");
144: throw new UnreachableBackendException();
145: }
146: if (logger.isWarnEnabled())
147: logger
148: .warn("Timeout expired for connection on backend '"
149: + backendName
150: + "', consider increasing pool size (current size is "
151: + poolSize
152: + ") or timeout (current timeout is "
153: + (timeout / 1000)
154: + " seconds)");
155: return null;
156: }
157: } else
158: this .wait();
159: } catch (InterruptedException e) {
160: logger
161: .error("Wait on freeConnections interrupted in RandomWaitPoolConnectionManager: "
162: + e);
163: return null;
164: }
165: }
166: if (isShutdown) {
167: return null;
168: }
169: // Get the connection
170: try {
171: PooledConnection c = (PooledConnection) freeConnections
172: .removeLast();
173: activeConnections.add(c);
174: return c;
175: } catch (NoSuchElementException e) {
176: int missing = poolSize
177: - (activeConnections.size() + freeConnections
178: .size());
179: if (missing > 0) { // Re-allocate missing connections
180: logger.info("Trying to reallocate " + missing
181: + " missing connections.");
182: PooledConnection connectionToBeReturned = null;
183: while (missing > 0) {
184: Connection c = getConnectionFromDriver();
185: if (c == null) {
186: if (missing == poolSize) {
187: String msg = Translate
188: .get(
189: "loadbalancer.backend.unreacheable",
190: backendName);
191: logger.error(msg);
192: throw new UnreachableBackendException(msg);
193: }
194: logger.warn("Unable to re-allocate " + missing
195: + " missing connections.");
196: break;
197: } else {
198: if (connectionToBeReturned == null)
199: connectionToBeReturned = new PooledConnection(
200: c);
201: else
202: freeConnections
203: .addLast(new PooledConnection(c));
204: }
205: missing--;
206: }
207: return connectionToBeReturned;
208: }
209: if (logger.isErrorEnabled())
210: logger.error("Failed to get a connection on backend '"
211: + backendName
212: + "' whereas an idle connection was expected");
213: return null;
214: }
215: }
216:
217: /**
218: * @see org.continuent.sequoia.controller.connection.AbstractConnectionManager#releaseConnection(org.continuent.sequoia.controller.connection.PooledConnection)
219: */
220: public synchronized void releaseConnection(PooledConnection c) {
221: if (!initialized) {
222: closeConnection(c);
223: return; // We probably have been disabled
224: }
225:
226: if (activeConnections.remove(c)) {
227: c.removeAllTemporaryTables();
228: freeConnections.addLast(c);
229: this .notify();
230: } else
231: logger.error("Failed to release connection " + c
232: + " (not found in active pool)");
233: }
234:
235: /**
236: * @see org.continuent.sequoia.controller.connection.AbstractConnectionManager#deleteConnection(org.continuent.sequoia.controller.connection.PooledConnection)
237: */
238: public synchronized void deleteConnection(PooledConnection c) {
239: closeConnection(c);
240: if (!initialized)
241: return; // We probably have been disabled
242:
243: if (activeConnections.remove(c)) {
244: Connection newConnection = getConnectionFromDriver();
245: if (newConnection == null) {
246: if (logger.isDebugEnabled())
247: logger
248: .error("Bad connection "
249: + c
250: + " has been removed but cannot be replaced.");
251: } else {
252: freeConnections.addLast(newConnection);
253: this .notify();
254: if (logger.isDebugEnabled())
255: logger
256: .debug("Bad connection "
257: + c
258: + " has been replaced by a new connection.");
259: }
260: } else
261: logger.error("Failed to release connection " + c
262: + " (not found in active pool)");
263: }
264:
265: /**
266: * @see org.continuent.sequoia.controller.connection.AbstractConnectionManager#getXmlImpl()
267: */
268: public String getXmlImpl() {
269: StringBuffer info = new StringBuffer();
270: info.append("<"
271: + DatabasesXmlTags.ELT_RandomWaitPoolConnectionManager
272: + " " + DatabasesXmlTags.ATT_poolSize + "=\""
273: + poolSize + "\" " + DatabasesXmlTags.ATT_timeout
274: + "=\"" + timeout / 1000 + "\"/>");
275: return info.toString();
276: }
277:
278: }
|