00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
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
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054 public class VariablePoolConnectionManager
00055 extends
00056 AbstractPoolConnectionManager implements Serializable
00057 {
00058
00059 public static final int DEFAULT_MAX_POOL_SIZE = 0;
00060
00061
00062
00063
00064
00065 public static final int DEFAULT_IDLE_TIMEOUT = 0;
00066
00067
00068
00069
00070
00071 public static final int DEFAULT_WAIT_TIMEOUT = 0;
00072
00073
00074 private int initPoolSize;
00075
00076
00077 private int minPoolSize;
00078
00079
00080 private int maxPoolSize;
00081
00082
00083
00084
00085
00086 private int idleTimeout;
00087
00088
00089 private int waitTimeout;
00090
00091
00092 private Stack releaseTimes;
00093
00094
00095 private RemoveIdleConnectionsThread removeIdleConnectionsThread;
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119 public VariablePoolConnectionManager(String backendUrl, String backendName,
00120 String rLogin, String rPassword, String driverPath,
00121 String driverClassName, int minPoolSize, int maxPoolSize,
00122 int idleTimeout, int waitTimeout)
00123 {
00124 this(backendUrl, backendName, rLogin, rPassword, driverPath,
00125 driverClassName, minPoolSize, minPoolSize, maxPoolSize, idleTimeout,
00126 waitTimeout);
00127 }
00128
00129
00130
00131
00132 protected Object clone() throws CloneNotSupportedException
00133 {
00134 return new VariablePoolConnectionManager(backendUrl, backendName, rLogin,
00135 rPassword, driverPath, driverClassName, minPoolSize, maxPoolSize,
00136 idleTimeout, waitTimeout);
00137 }
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161 public VariablePoolConnectionManager(String backendUrl, String backendName,
00162 String rLogin, String rPassword, String driverPath,
00163 String driverClassName, int initPoolSize, int minPoolSize,
00164 int maxPoolSize, int idleTimeout, int waitTimeout)
00165 {
00166 super(backendUrl, backendName, rLogin, rPassword, driverPath,
00167 driverClassName, maxPoolSize == 0 ? (initPoolSize > minPoolSize
00168 ? initPoolSize
00169 : minPoolSize) : maxPoolSize);
00170 this.initPoolSize = initPoolSize;
00171 this.minPoolSize = minPoolSize;
00172 this.maxPoolSize = maxPoolSize;
00173 this.idleTimeout = idleTimeout * 1000;
00174 this.waitTimeout = waitTimeout * 1000;
00175 }
00176
00177
00178
00179
00180
00181
00182 public int getMaxPoolSize()
00183 {
00184 return maxPoolSize;
00185 }
00186
00187
00188
00189
00190
00191
00192 public int getMinPoolSize()
00193 {
00194 return minPoolSize;
00195 }
00196
00197
00198
00199
00200
00201
00202 public int getIdleTimeout()
00203 {
00204 return idleTimeout;
00205 }
00206
00207
00208
00209
00210
00211
00212 public int getWaitTimeout()
00213 {
00214 return waitTimeout;
00215 }
00216
00217
00218
00219
00220 public synchronized void initializeConnections() throws SQLException
00221 {
00222 poolSize = maxPoolSize == 0 ? (initPoolSize > minPoolSize
00223 ? initPoolSize
00224 : minPoolSize) : maxPoolSize;
00225 super.initializeConnections(initPoolSize);
00226
00227 if (idleTimeout != 0)
00228 {
00229
00230 removeIdleConnectionsThread = new RemoveIdleConnectionsThread(
00231 this.backendName);
00232
00233
00234
00235 releaseTimes = new Stack();
00236 Iterator it = freeConnections.iterator();
00237 Long currentTime = new Long(System.currentTimeMillis());
00238 while (it.hasNext())
00239 {
00240 it.next();
00241 releaseTimes.push(currentTime);
00242 }
00243
00244
00245 removeIdleConnectionsThread.start();
00246
00247 synchronized (removeIdleConnectionsThread)
00248 {
00249 if (releaseTimes.size() > 0)
00250 {
00251 removeIdleConnectionsThread.notify();
00252 }
00253 }
00254 }
00255 }
00256
00257
00258
00259
00260 public synchronized void finalizeConnections() throws SQLException
00261 {
00262 if (removeIdleConnectionsThread != null)
00263 {
00264 synchronized (removeIdleConnectionsThread)
00265 {
00266 removeIdleConnectionsThread.isKilled = true;
00267 idleTimeout = 0;
00268 removeIdleConnectionsThread.notify();
00269 }
00270 try
00271 {
00272 removeIdleConnectionsThread.join();
00273 }
00274 catch (InterruptedException e)
00275 {
00276 }
00277 }
00278 super.finalizeConnections();
00279 }
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296 public Connection getConnection() throws UnreachableBackendException
00297 {
00298 if (!initialized)
00299 {
00300 logger
00301 .error("Requesting a connection from a non-initialized connection manager");
00302 return null;
00303 }
00304
00305 long lTimeout = waitTimeout;
00306 synchronized (freeConnections)
00307 {
00308 if (freeConnections.isEmpty())
00309 {
00310 if ((maxPoolSize == 0) || (activeConnections.size() < maxPoolSize))
00311 {
00312 Connection c = getConnectionFromDriver();
00313 if (c == null)
00314 {
00315 if (activeConnections.size() == 0)
00316 {
00317
00318 logger
00319 .error("Backend " + backendName + " is no more accessible.");
00320 throw new UnreachableBackendException();
00321 }
00322
00323 if (logger.isWarnEnabled())
00324 logger.warn("Failed to create new connection on backend '"
00325 + backendName + "', waiting for a connection to be freed.");
00326 }
00327 else
00328 {
00329 freeConnections.add(c);
00330 if (idleTimeout != 0)
00331 {
00332 releaseTimes.add(new Long(System.currentTimeMillis()));
00333 }
00334 poolSize++;
00335 }
00336 }
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346 while (freeConnections.isEmpty())
00347 {
00348
00349 try
00350 {
00351 if (lTimeout > 0)
00352 {
00353 long start = System.currentTimeMillis();
00354
00355 freeConnections.wait(waitTimeout);
00356 long end = System.currentTimeMillis();
00357 lTimeout -= end - start;
00358 if (lTimeout <= 0)
00359 {
00360 if (logger.isWarnEnabled())
00361 logger.warn("Timeout expired for connection on backend '"
00362 + backendName
00363 + "', consider increasing pool size (current size is "
00364 + poolSize + ") or timeout (current timeout is "
00365 + (waitTimeout / 1000) + " seconds)");
00366 return null;
00367 }
00368 }
00369 else
00370 {
00371 freeConnections.wait();
00372 }
00373 }
00374 catch (InterruptedException e)
00375 {
00376 logger
00377 .error("Wait on freeConnections interrupted in VariablePoolConnectionManager");
00378 return null;
00379 }
00380 }
00381 }
00382
00383
00384 try
00385 {
00386 Connection c = (Connection) freeConnections.pop();
00387 if (idleTimeout != 0)
00388 {
00389 releaseTimes.pop();
00390 }
00391 activeConnections.add(c);
00392 return c;
00393 }
00394 catch (EmptyStackException e)
00395 {
00396 if (logger.isErrorEnabled())
00397 logger.error("Failed to get a connection on backend '" + backendName
00398 + "' but an idle connection was expected");
00399 return null;
00400 }
00401 }
00402 }
00403
00404
00405
00406
00407 public void releaseConnection(Connection c)
00408 {
00409 if (!initialized)
00410 return;
00411
00412 boolean notifyThread = false;
00413 synchronized (freeConnections)
00414 {
00415 if (activeConnections.remove(c))
00416 {
00417 if (idleTimeout != 0)
00418 {
00419 notifyThread = freeConnections.isEmpty()
00420 || (freeConnections.size() == minPoolSize);
00421 freeConnections.push(c);
00422 freeConnections.notify();
00423 releaseTimes.push(new Long(System.currentTimeMillis()));
00424 }
00425 else
00426 {
00427 freeConnections.push(c);
00428 freeConnections.notify();
00429 }
00430 }
00431 else
00432 logger.error("Failed to release connection " + c
00433 + " (not found in active pool)");
00434 }
00435
00436 if (notifyThread)
00437 synchronized (removeIdleConnectionsThread)
00438 {
00439 removeIdleConnectionsThread.notify();
00440 }
00441 }
00442
00443
00444
00445
00446 public void deleteConnection(Connection c)
00447 {
00448 if (!initialized)
00449 return;
00450
00451 synchronized (freeConnections)
00452 {
00453 if (activeConnections.remove(c))
00454 {
00455 poolSize--;
00456 if (poolSize < minPoolSize)
00457 {
00458 Connection newConnection = getConnectionFromDriver();
00459 if (newConnection == null)
00460 {
00461 if (logger.isDebugEnabled())
00462 logger.error("Bad connection " + c
00463 + " has been removed but cannot be replaced.");
00464 }
00465 else
00466 {
00467 freeConnections.push(newConnection);
00468 freeConnections.notify();
00469 if (logger.isDebugEnabled())
00470 logger.debug("Bad connection " + c
00471 + " has been replaced by a new connection.");
00472 }
00473 }
00474 else if (logger.isDebugEnabled())
00475 logger.debug("Bad connection " + c + " has been removed.");
00476 }
00477 else
00478 logger.error("Failed to release connection " + c
00479 + " (not found in active pool)");
00480 }
00481 }
00482
00483
00484
00485
00486 public String getXmlImpl()
00487 {
00488 StringBuffer info = new StringBuffer();
00489 info.append("<" + DatabasesXmlTags.ELT_VariablePoolConnectionManager + " "
00490 + DatabasesXmlTags.ATT_initPoolSize + "=\"" + initPoolSize + "\" "
00491 + DatabasesXmlTags.ATT_minPoolSize + "=\"" + minPoolSize + "\" "
00492 + DatabasesXmlTags.ATT_maxPoolSize + "=\"" + maxPoolSize + "\" "
00493 + DatabasesXmlTags.ATT_idleTimeout + "=\"" + idleTimeout / 1000 + "\" "
00494 + DatabasesXmlTags.ATT_waitTimeout + "=\"" + waitTimeout / 1000
00495 + "\"/>");
00496 return info.toString();
00497 }
00498
00499
00500
00501
00502
00503
00504 protected class RemoveIdleConnectionsThread extends Thread
00505 {
00506 private boolean isKilled = false;
00507
00508 protected RemoveIdleConnectionsThread(String pBackendName)
00509 {
00510 super("RemoveIdleConnectionsThread for backend:" + pBackendName);
00511 }
00512
00513
00514
00515
00516 public void run()
00517 {
00518 long idleTime, releaseTime;
00519 synchronized (this)
00520 {
00521 try
00522 {
00523 while (!isKilled)
00524 {
00525
00526
00527 if (freeConnections.isEmpty()
00528 || (freeConnections.size() == minPoolSize))
00529 {
00530 wait();
00531 }
00532
00533 Connection c = null;
00534 synchronized (freeConnections)
00535 {
00536 if (releaseTimes.isEmpty())
00537 continue;
00538
00539 releaseTime = ((Long) releaseTimes.get(0)).longValue();
00540 idleTime = System.currentTimeMillis() - releaseTime;
00541
00542 if (idleTime >= idleTimeout)
00543 c = (Connection) freeConnections.remove(0);
00544 }
00545
00546 if (c == null)
00547 {
00548 wait(idleTimeout - idleTime);
00549 }
00550 else
00551 {
00552 try
00553 {
00554 c.close();
00555 }
00556 catch (SQLException e)
00557 {
00558 String msg = "An error occured while closing idle connection after the timeout: "
00559 + e;
00560 logger.error(msg);
00561 }
00562 finally
00563 {
00564 releaseTimes.remove(0);
00565 poolSize--;
00566 }
00567 logger.debug("Released idle connection (idle timeout reached)");
00568 continue;
00569
00570 }
00571 }
00572 }
00573 catch (InterruptedException e)
00574 {
00575 logger
00576 .error("Wait on removeIdleConnectionsThread interrupted in VariablePoolConnectionManager: "
00577 + e);
00578 }
00579 }
00580 }
00581 }
00582
00583 }