00001
00025
package org.objectweb.cjdbc.controller.connection;
00026
00027
import java.io.Serializable;
00028
import java.sql.Connection;
00029
import java.sql.SQLException;
00030
import java.util.EmptyStackException;
00031
import java.util.Iterator;
00032
import java.util.Stack;
00033
00034
import org.objectweb.cjdbc.common.exceptions.UnreachableBackendException;
00035
import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
00036
00054 public class VariablePoolConnectionManager extends
00055
AbstractPoolConnectionManager implements Serializable
00056 {
00058 public static final int DEFAULT_MAX_POOL_SIZE = 0;
00059
00064 public static final int DEFAULT_IDLE_TIMEOUT = 0;
00065
00070 public static final int DEFAULT_WAIT_TIMEOUT = 0;
00071
00073 private int initPoolSize;
00074
00076 private int minPoolSize;
00077
00079 private int maxPoolSize;
00080
00085 private int idleTimeout;
00086
00088 private int waitTimeout;
00089
00091 private Stack
releaseTimes;
00092
00094 private RemoveIdleConnectionsThread removeIdleConnectionsThread;
00095
00118 public VariablePoolConnectionManager(String backendUrl, String backendName,
00119 String rLogin, String rPassword, String driverPath,
00120 String driverClassName,
int minPoolSize,
int maxPoolSize,
00121
int idleTimeout,
int waitTimeout)
00122 {
00123
this(backendUrl, backendName, rLogin, rPassword, driverPath,
00124 driverClassName, minPoolSize, minPoolSize, maxPoolSize, idleTimeout,
00125 waitTimeout);
00126 }
00127
00131 protected Object
clone() throws CloneNotSupportedException
00132 {
00133
return new VariablePoolConnectionManager(backendUrl, backendName, rLogin,
00134 rPassword, driverPath, driverClassName,
minPoolSize,
maxPoolSize,
00135
idleTimeout,
waitTimeout);
00136 }
00137
00160 public VariablePoolConnectionManager(String backendUrl, String backendName,
00161 String rLogin, String rPassword, String driverPath,
00162 String driverClassName,
int initPoolSize,
int minPoolSize,
00163
int maxPoolSize,
int idleTimeout,
int waitTimeout)
00164 {
00165 super(backendUrl, backendName, rLogin, rPassword, driverPath,
00166 driverClassName, maxPoolSize == 0 ? (initPoolSize > minPoolSize
00167 ? initPoolSize
00168 : minPoolSize) : maxPoolSize);
00169
this.initPoolSize = initPoolSize;
00170
this.minPoolSize = minPoolSize;
00171
this.maxPoolSize = maxPoolSize;
00172
this.idleTimeout = idleTimeout * 1000;
00173
this.waitTimeout = waitTimeout * 1000;
00174 }
00175
00181 public int getMaxPoolSize()
00182 {
00183
return maxPoolSize;
00184 }
00185
00191 public int getMinPoolSize()
00192 {
00193
return minPoolSize;
00194 }
00195
00202 public int getIdleTimeout()
00203 {
00204
return idleTimeout;
00205 }
00206
00213 public int getWaitTimeout()
00214 {
00215
return waitTimeout;
00216 }
00217
00221 public synchronized void initializeConnections() throws SQLException
00222 {
00223 super.initializeConnections(
initPoolSize);
00224
00225
if (
idleTimeout != 0)
00226 {
00227
00228
removeIdleConnectionsThread =
new RemoveIdleConnectionsThread(
this.backendName);
00229
00230
00231
00232
releaseTimes =
new Stack();
00233 Iterator it = freeConnections.iterator();
00234 Long currentTime =
new Long(System.currentTimeMillis());
00235
while (it.hasNext())
00236 {
00237 it.next();
00238
releaseTimes.push(currentTime);
00239 }
00240
00241
00242
removeIdleConnectionsThread.start();
00243
00244
synchronized (
removeIdleConnectionsThread)
00245 {
00246
if (
releaseTimes.size() > 0)
00247 {
00248
removeIdleConnectionsThread.notify();
00249 }
00250 }
00251 }
00252 }
00253
00257 public synchronized void finalizeConnections() throws SQLException
00258 {
00259
if (
removeIdleConnectionsThread != null)
00260 {
00261
synchronized (
removeIdleConnectionsThread)
00262 {
00263
removeIdleConnectionsThread.
isKilled =
true;
00264
idleTimeout = 0;
00265
removeIdleConnectionsThread.notify();
00266 }
00267
try
00268 {
00269
removeIdleConnectionsThread.join();
00270 }
00271
catch (InterruptedException e)
00272 {
00273 }
00274 }
00275 super.finalizeConnections();
00276 }
00277
00293 public Connection
getConnection() throws
UnreachableBackendException
00294 {
00295
if (!initialized)
00296 {
00297 logger
00298 .
error(
"Requesting a connection from a non-initialized connection manager");
00299
return null;
00300 }
00301
00302
long lTimeout =
waitTimeout;
00303
synchronized (freeConnections)
00304 {
00305
if (freeConnections.isEmpty())
00306 {
00307
if ((
maxPoolSize == 0) || (activeConnections.size() <
maxPoolSize))
00308 {
00309 Connection c =
getConnectionFromDriver();
00310
if (c == null)
00311 {
00312
if (activeConnections.size() == 0)
00313 {
00314
00315 logger
00316 .
error(
"Backend " + backendName +
" is no more accessible.");
00317
throw new UnreachableBackendException();
00318 }
00319
00320
if (logger.
isWarnEnabled())
00321 logger.
warn(
"Failed to create new connection on backend '"
00322 + backendName +
"', waiting for a connection to be freed.");
00323 }
00324
else
00325 {
00326 freeConnections.add(c);
00327
if (
idleTimeout != 0)
00328 {
00329
releaseTimes.add(
new Long(System.currentTimeMillis()));
00330 }
00331 poolSize++;
00332 }
00333 }
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
while (freeConnections.isEmpty())
00344 {
00345
00346
try
00347 {
00348
if (lTimeout > 0)
00349 {
00350
long start = System.currentTimeMillis();
00351
00352 freeConnections.wait(waitTimeout);
00353
long end = System.currentTimeMillis();
00354 lTimeout -= end - start;
00355
if (lTimeout <= 0)
00356 {
00357
if (logger.
isWarnEnabled())
00358 logger.
warn(
"Timeout expired for connection on backend '"
00359 + backendName
00360 +
"', consider increasing pool size (current size is "
00361 + poolSize +
") or timeout (current timeout is "
00362 + (waitTimeout / 1000) +
" seconds)");
00363
return null;
00364 }
00365 }
00366
else
00367 {
00368 freeConnections.wait();
00369 }
00370 }
00371
catch (InterruptedException e)
00372 {
00373 logger
00374 .
error(
"Wait on freeConnections interrupted in VariablePoolConnectionManager");
00375
return null;
00376 }
00377 }
00378 }
00379
00380
00381
try
00382 {
00383 Connection c = (Connection) freeConnections.pop();
00384
if (
idleTimeout != 0)
00385 {
00386
releaseTimes.pop();
00387 }
00388 activeConnections.add(c);
00389
return c;
00390 }
00391
catch (EmptyStackException e)
00392 {
00393
if (logger.
isErrorEnabled())
00394 logger.
error(
"Failed to get a connection on backend '" + backendName
00395 +
"' but an idle connection was expected");
00396
return null;
00397 }
00398 }
00399 }
00400
00404 public void releaseConnection(Connection c)
00405 {
00406
if (!initialized)
00407
return;
00408
00409
boolean notifyThread =
false;
00410
synchronized (freeConnections)
00411 {
00412
if (activeConnections.remove(c))
00413 {
00414
if (
idleTimeout != 0)
00415 {
00416 notifyThread = freeConnections.isEmpty()
00417 || (freeConnections.size() ==
minPoolSize);
00418 freeConnections.push(c);
00419 freeConnections.notify();
00420
releaseTimes.push(
new Long(System.currentTimeMillis()));
00421 }
00422
else
00423 {
00424 freeConnections.push(c);
00425 freeConnections.notify();
00426 }
00427 }
00428
else
00429 logger.
error(
"Failed to release connection " + c
00430 +
" (not found in active pool)");
00431 }
00432
00433
if (notifyThread)
00434
synchronized (
removeIdleConnectionsThread)
00435 {
00436
removeIdleConnectionsThread.notify();
00437 }
00438 }
00439
00443 public void deleteConnection(Connection c)
00444 {
00445
if (!initialized)
00446
return;
00447
00448
synchronized (freeConnections)
00449 {
00450
if (activeConnections.remove(c))
00451 {
00452 poolSize--;
00453
if (poolSize <
minPoolSize)
00454 {
00455 Connection newConnection =
getConnectionFromDriver();
00456
if (newConnection == null)
00457 {
00458
if (logger.
isDebugEnabled())
00459 logger.
error(
"Bad connection " + c
00460 +
" has been removed but cannot be replaced.");
00461 }
00462
else
00463 {
00464 freeConnections.push(newConnection);
00465 freeConnections.notify();
00466
if (logger.
isDebugEnabled())
00467 logger.
debug(
"Bad connection " + c
00468 +
" has been replaced by a new connection.");
00469 }
00470 }
00471
else if (logger.
isDebugEnabled())
00472 logger.
debug(
"Bad connection " + c +
" has been removed.");
00473 }
00474
else
00475 logger.
error(
"Failed to release connection " + c
00476 +
" (not found in active pool)");
00477 }
00478 }
00479
00483 public String
getXmlImpl()
00484 {
00485 StringBuffer info =
new StringBuffer();
00486 info.append(
"<" +
DatabasesXmlTags.ELT_VariablePoolConnectionManager +
" "
00487 +
DatabasesXmlTags.ATT_initPoolSize +
"=\"" +
initPoolSize +
"\" "
00488 +
DatabasesXmlTags.ATT_minPoolSize +
"=\"" +
minPoolSize +
"\" "
00489 +
DatabasesXmlTags.ATT_maxPoolSize +
"=\"" +
maxPoolSize +
"\" "
00490 +
DatabasesXmlTags.ATT_idleTimeout +
"=\"" +
idleTimeout / 1000 +
"\" "
00491 +
DatabasesXmlTags.ATT_waitTimeout +
"=\"" +
waitTimeout / 1000
00492 +
"\"/>");
00493
return info.toString();
00494 }
00495
00502 protected class RemoveIdleConnectionsThread extends Thread
00503 {
00504 private boolean isKilled =
false;
00505
00506 protected RemoveIdleConnectionsThread(String pBackendName)
00507 {
00508 super(
"RemoveIdleConnectionsThread for backend:" + pBackendName);
00509 }
00510
00514 public void run()
00515 {
00516
long idleTime, releaseTime;
00517
synchronized (
this)
00518 {
00519
try
00520 {
00521
while (!
isKilled)
00522 {
00523
00524
00525
if (freeConnections.isEmpty()
00526 || (freeConnections.size() == minPoolSize))
00527 {
00528 wait();
00529 }
00530
00531 Connection c = null;
00532
synchronized (freeConnections)
00533 {
00534
if (releaseTimes.isEmpty())
00535
continue;
00536
00537 releaseTime = ((Long) releaseTimes.get(0)).longValue();
00538 idleTime = System.currentTimeMillis() - releaseTime;
00539
00540
if (idleTime >= idleTimeout)
00541 c = (Connection) freeConnections.remove(0);
00542 }
00543
00544
if (c == null)
00545 {
00546 wait(idleTimeout - idleTime);
00547 }
00548
else
00549 {
00550
try
00551 {
00552 c.close();
00553 }
00554
catch (SQLException e)
00555 {
00556 String msg =
"An error occured while closing idle connection after the timeout: "
00557 + e;
00558 logger.error(msg);
00559 }
00560 finally
00561 {
00562 releaseTimes.remove(0);
00563 poolSize--;
00564 }
00565 logger.debug(
"Released idle connection (idle timeout reached)");
00566
continue;
00567
00568 }
00569 }
00570 }
00571
catch (InterruptedException e)
00572 {
00573 logger
00574 .error(
"Wait on removeIdleConnectionsThread interrupted in VariablePoolConnectionManager: "
00575 + e);
00576 }
00577 }
00578 }
00579 }
00580
00581 }