1 7 package winstone.jndi.resourceFactories; 8 9 import java.io.PrintWriter ; 10 import java.sql.Connection ; 11 import java.sql.Driver ; 12 import java.sql.PreparedStatement ; 13 import java.sql.SQLException ; 14 import java.util.ArrayList ; 15 import java.util.Iterator ; 16 import java.util.List ; 17 import java.util.Map ; 18 import java.util.Properties ; 19 20 import javax.sql.DataSource ; 21 22 import winstone.Logger; 23 import winstone.WebAppConfiguration; 24 import winstone.WinstoneResourceBundle; 25 26 42 public class WinstoneDataSource implements DataSource , Runnable { 43 44 public static final WinstoneResourceBundle DS_RESOURCES = 45 new WinstoneResourceBundle("winstone.jndi.resourceFactories.LocalStrings"); 46 47 private String name; 48 49 private String url; 50 private Driver driver; 51 private Properties connectProps; 52 53 private int maxIdleCount; 54 private int maxHeldCount; 55 private int retryCount; 56 private int retryPeriod; 57 58 private String keepAliveSQL; 59 private int keepAlivePeriod; 60 private boolean checkBeforeGet; 61 private int killInactivePeriod; 62 63 private List usedWrappers; 64 private List unusedRealConnections; private List usedRealConnections; 66 67 private Thread managementThread; 68 69 private int loginTimeout; 70 private PrintWriter logWriter; 71 72 75 public WinstoneDataSource(String name, Map args, ClassLoader loader) { 76 this.name = name; 77 78 this.usedWrappers = new ArrayList (); 79 this.usedRealConnections = new ArrayList (); 80 this.unusedRealConnections = new ArrayList (); 81 this.connectProps = new Properties (); 82 83 this.keepAliveSQL = WebAppConfiguration.stringArg(args, "keepAliveSQL", ""); 85 this.keepAlivePeriod = WebAppConfiguration.intArg(args, "keepAlivePeriod", -1); 86 this.checkBeforeGet = WebAppConfiguration.booleanArg(args, "checkBeforeGet", 87 !this.keepAliveSQL.equals("")); 88 this.killInactivePeriod = WebAppConfiguration.intArg(args, "killInactivePeriod", -1); 89 90 this.url = WebAppConfiguration.stringArg(args, "url", null); 91 String driverClassName = WebAppConfiguration.stringArg(args, "driverClassName", ""); 92 if (args.get("username") != null) 93 this.connectProps.put("user", args.get("username")); 94 if (args.get("password") != null) 95 this.connectProps.put("password", args.get("password")); 96 97 this.maxHeldCount = WebAppConfiguration.intArg(args, "maxConnections", 20); 98 this.maxIdleCount = WebAppConfiguration.intArg(args, "maxIdle", 10); 99 int startCount = WebAppConfiguration.intArg(args, "startConnections", 1); 100 101 this.retryCount = WebAppConfiguration.intArg(args, "retryCount", 1); 102 this.retryPeriod = WebAppConfiguration.intArg(args, "retryPeriod", 1000); 103 104 log(Logger.FULL_DEBUG, "DBConnectionPool.Init", this.url, null); 105 106 try { 107 synchronized (this.unusedRealConnections) { 108 if (!driverClassName.equals("")) { 109 Class driverClass = Class.forName(driverClassName.trim(), true, loader); 110 this.driver = (Driver ) driverClass.newInstance(); 111 112 for (int n = 0; n < startCount; n++) { 113 makeNewRealConnection(this.unusedRealConnections); 114 } 115 } 116 } 117 } catch (Throwable err) { 118 log(Logger.ERROR, "DBConnectionPool.ErrorInCreate", this.name, err); 119 } 120 121 this.managementThread = new Thread (this, "DBConnectionPool management thread"); 123 this.managementThread.start(); 124 } 125 126 129 public void destroy() { 130 if (this.managementThread != null) { 131 this.managementThread.interrupt(); 132 this.managementThread = null; 133 } 134 135 synchronized (this.unusedRealConnections) { 136 killPooledConnections(this.unusedRealConnections, 0); 137 killPooledConnections(this.usedRealConnections, 0); 138 } 139 140 this.usedRealConnections.clear(); 141 this.unusedRealConnections.clear(); 142 this.usedWrappers.clear(); 143 } 144 145 148 public Connection getConnection(String username, String password) 149 throws SQLException { 150 Properties newProps = new Properties (); 151 newProps.put("user", username); 152 newProps.put("password", password); 153 Connection conn = this.driver.connect(this.url, newProps); 154 WinstoneConnection wrapper = null; 155 synchronized (this.unusedRealConnections) { 156 wrapper = new WinstoneConnection(conn, this); 157 this.usedWrappers.add(wrapper); 158 } 159 return wrapper; 160 } 161 162 public Connection getConnection() throws SQLException { 163 return getConnection(this.retryCount); 164 } 165 166 169 protected Connection getConnection(int retriesAllowed) throws SQLException { 170 Connection realConnection = null; 171 172 synchronized (this.unusedRealConnections) { 173 if (this.unusedRealConnections.size() > 0) { 175 realConnection = (Connection ) this.unusedRealConnections.get(0); 176 this.unusedRealConnections.remove(realConnection); 177 this.usedRealConnections.add(realConnection); 178 log(Logger.FULL_DEBUG, "DBConnectionPool.UsingPooled", 179 new String [] {"" + this.usedRealConnections.size(), 180 "" + this.unusedRealConnections.size()}, null); 181 try { 182 return prepareWrapper(realConnection); 183 } catch (SQLException err) { 184 } 186 } 187 188 else if (this.usedRealConnections.size() < maxHeldCount) { 190 realConnection = makeNewRealConnection(this.usedRealConnections); 191 log(Logger.FULL_DEBUG, "DBConnectionPool.UsingNew", 192 new String [] {"" + this.usedRealConnections.size(), 193 "" + this.unusedRealConnections.size()}, null); 194 try { 195 return prepareWrapper(realConnection); 196 } catch (SQLException err) { 197 } 199 } 200 } 201 202 if (realConnection != null) { 203 realConnection = null; 205 return getConnection(retriesAllowed); 206 } else if (retriesAllowed <= 0) { 207 throw new SQLException (DS_RESOURCES.getString("DBConnectionPool.Exceeded", "" + maxHeldCount)); 209 } else { 210 log(Logger.FULL_DEBUG, "DBConnectionPool.Retrying", new String [] { 211 "" + maxHeldCount, "" + retriesAllowed, "" + retryPeriod}, null); 212 213 try { 215 Thread.sleep(retryPeriod); 216 } catch (InterruptedException err) {} 217 return getConnection(retriesAllowed - 1); 218 } 219 } 220 221 private Connection prepareWrapper(Connection realConnection) throws SQLException { 222 if (this.checkBeforeGet) { 224 try { 225 executeKeepAlive(realConnection); 226 } catch (SQLException err) { 227 killConnection(this.usedRealConnections, realConnection); 229 throw err; 230 } 231 } 232 realConnection.setAutoCommit(false); 233 WinstoneConnection wrapper = new WinstoneConnection(realConnection, this); 234 this.usedWrappers.add(wrapper); 235 return wrapper; 236 } 237 238 241 void releaseConnection(WinstoneConnection wrapper, Connection realConnection) throws SQLException { 242 synchronized (this.unusedRealConnections) { 243 if (wrapper != null) { 244 this.usedWrappers.remove(wrapper); 245 } 246 if (realConnection != null) { 247 if (this.usedRealConnections.contains(realConnection)) { 248 this.usedRealConnections.remove(realConnection); 249 this.unusedRealConnections.add(realConnection); 250 log(Logger.FULL_DEBUG, "DBConnectionPool.Releasing", 251 new String [] {"" + this.usedRealConnections.size(), 252 "" + this.unusedRealConnections.size()}, null); 253 } else { 254 log(Logger.WARNING, "DBConnectionPool.ReleasingUnknown", null); 255 realConnection.close(); 256 } 257 } 258 } 259 } 260 261 public int getLoginTimeout() { 262 return this.loginTimeout; 263 } 264 265 public PrintWriter getLogWriter() { 266 return this.logWriter; 267 } 268 269 public void setLoginTimeout(int timeout) { 270 this.loginTimeout = timeout; 271 } 272 273 public void setLogWriter(PrintWriter writer) { 274 this.logWriter = writer; 275 } 276 277 281 public void run() { 282 log(Logger.DEBUG, "DBConnectionPool.MaintenanceStart", null); 283 284 int keepAliveCounter = -1; 285 int killInactiveCounter = -1; 286 boolean threadRunning = true; 287 288 while (threadRunning) { 289 try { 290 long startTime = System.currentTimeMillis(); 291 292 if ((this.keepAlivePeriod != -1) && threadRunning) { 294 keepAliveCounter++; 295 296 if (this.keepAlivePeriod <= keepAliveCounter) { 297 synchronized (this.unusedRealConnections) { 298 executeKeepAliveOnUnused(); 299 } 300 keepAliveCounter = 0; 301 } 302 } 303 304 if (Thread.interrupted()) { 305 threadRunning = false; 306 } 307 308 if ((this.killInactivePeriod != -1) && threadRunning) { 310 killInactiveCounter++; 311 312 if (this.killInactivePeriod <= killInactiveCounter) { 313 synchronized (this.unusedRealConnections) { 314 killPooledConnections(this.unusedRealConnections, this.maxIdleCount); 315 } 316 317 killInactiveCounter = 0; 318 } 319 } 320 321 if ((killInactiveCounter == 0) || (keepAliveCounter == 0)) { 322 log(Logger.FULL_DEBUG, "DBConnectionPool.MaintenanceTime", 323 "" + (System.currentTimeMillis() - startTime), null); 324 } 325 326 if (Thread.interrupted()) { 327 threadRunning = false; 328 } else { 329 Thread.sleep(60000); } 331 332 if (Thread.interrupted()) { 333 threadRunning = false; 334 } 335 } catch (InterruptedException err) { 336 threadRunning = false; 337 continue; 338 } 339 } 340 341 log(Logger.DEBUG, "DBConnectionPool.MaintenanceFinish", null); 342 } 343 344 347 protected void executeKeepAliveOnUnused() { 348 List dead = new ArrayList (); 350 351 for (Iterator i = this.unusedRealConnections.iterator(); i.hasNext();) { 352 Connection conn = (Connection ) i.next(); 353 354 try { 355 executeKeepAlive(conn); 356 } catch (SQLException errSQL) { 357 dead.add(conn); 358 } 359 } 360 361 for (Iterator i = dead.iterator(); i.hasNext(); ) { 362 killConnection(this.unusedRealConnections, (Connection ) i.next()); 363 } 364 365 log(Logger.FULL_DEBUG, "DBConnectionPool.KeepAliveFinished", "" + 366 this.unusedRealConnections.size(), null); 367 } 368 369 protected void executeKeepAlive(Connection connection) throws SQLException { 370 if (!this.keepAliveSQL.equals("")) { 371 PreparedStatement qryKeepAlive = null; 372 try { 373 qryKeepAlive = connection.prepareStatement(keepAliveSQL); 374 qryKeepAlive.execute(); 375 } catch (SQLException err) { 376 log(Logger.WARNING, "DBConnectionPool.KeepAliveFailed", err); 377 throw err; 378 } finally { 379 if (qryKeepAlive != null) { 380 qryKeepAlive.close(); 381 } 382 } 383 } 384 } 385 386 390 protected Connection makeNewRealConnection(List pool) throws SQLException { 391 if (this.url == null) { 392 throw new SQLException ("No JDBC URL supplied"); 393 } 394 395 Connection realConnection = this.driver.connect(this.url, this.connectProps); 396 pool.add(realConnection); 397 log(Logger.FULL_DEBUG, "DBConnectionPool.AddingNew", 398 new String [] {"" + this.usedRealConnections.size(), 399 "" + this.unusedRealConnections.size()}, null); 400 401 return realConnection; 402 } 403 404 408 protected void killPooledConnections(List pool, int maxIdleCount) { 409 int killed = 0; 411 412 while (pool.size() > maxIdleCount) { 413 killed++; 414 Connection conn = (Connection ) pool.get(0); 415 killConnection(pool, conn); 416 } 417 418 if (killed > 0) { 419 log(Logger.FULL_DEBUG, "DBConnectionPool.Killed", "" + killed, null); 420 } 421 } 422 423 private static void killConnection(List pool, Connection conn) { 424 pool.remove(conn); 425 try { 426 conn.close(); 427 } catch (SQLException err) { 428 } 429 } 430 431 private void log(int level, String msgKey, Throwable err) { 432 if (getLogWriter() != null) { 433 getLogWriter().println(DS_RESOURCES.getString(msgKey)); 434 if (err != null) { 435 err.printStackTrace(getLogWriter()); 436 } 437 } else { 438 Logger.log(level, DS_RESOURCES, msgKey, err); 439 } 440 } 441 442 private void log(int level, String msgKey, String arg, Throwable err) { 443 if (getLogWriter() != null) { 444 getLogWriter().println(DS_RESOURCES.getString(msgKey, arg)); 445 if (err != null) { 446 err.printStackTrace(getLogWriter()); 447 } 448 } else { 449 Logger.log(level, DS_RESOURCES, msgKey, arg, err); 450 } 451 } 452 453 private void log(int level, String msgKey, String arg[], Throwable err) { 454 if (getLogWriter() != null) { 455 getLogWriter().println(DS_RESOURCES.getString(msgKey, arg)); 456 if (err != null) { 457 err.printStackTrace(getLogWriter()); 458 } 459 } else { 460 Logger.log(level, DS_RESOURCES, msgKey, arg, err); 461 } 462 } 463 } 464 | Popular Tags |