1 17 18 package org.apache.james.util.mordred; 19 20 import java.io.PrintWriter ; 21 import java.io.StringWriter ; 22 23 import java.sql.Connection ; 24 import java.sql.SQLException ; 25 26 import java.util.ArrayList ; 27 28 import org.apache.avalon.excalibur.datasource.DataSourceComponent; 29 import org.apache.avalon.framework.activity.Disposable; 30 import org.apache.avalon.framework.configuration.Configurable; 31 import org.apache.avalon.framework.configuration.Configuration; 32 import org.apache.avalon.framework.configuration.ConfigurationException; 33 import org.apache.avalon.framework.logger.AbstractLogEnabled; 34 35 36 62 public class JdbcDataSource extends AbstractLogEnabled 63 implements Configurable, 64 Runnable , 65 Disposable, 66 DataSourceComponent { 67 public static final long ACTIVE_CONN_TIME_LIMIT = 60000; public static final long ACTIVE_CONN_HARD_TIME_LIMIT = 2*ACTIVE_CONN_TIME_LIMIT; 70 public static final long CONN_IDLE_LIMIT = 600000; private static final boolean DEEP_DEBUG = false; 73 private static int total_served = 0; 74 private int connCreationsInProgress = 0; 78 private String connErrorMessage = null; 80 private long connLastCreated = 0; 82 private int connectionCount; 84 private String jdbcDriver; 86 private String jdbcPassword; 88 private String jdbcURL; 90 private String jdbcUsername; 92 private int maxConn = 0; 94 private ArrayList pool; 96 private Thread reaper; 98 private boolean reaperActive = false; 100 private String verifyConnSQL; 102 103 112 public Connection getConnection() throws SQLException { 113 if(connErrorMessage != null) { 115 throw new SQLException (connErrorMessage); 116 } 117 int count = total_served++; 120 if(DEEP_DEBUG) { 121 StringBuffer deepDebugBuffer = 122 new StringBuffer (128) 123 .append((new java.util.Date ()).toString()) 124 .append(" trying to get a connection (") 125 .append(count) 126 .append(")"); 127 System.out.println(deepDebugBuffer.toString()); 128 } 129 for(int attempts = 1; attempts <= 100; attempts++) { 130 synchronized(pool) { 131 for(int i = 0; i < pool.size(); i++) { 132 PoolConnEntry entry = (PoolConnEntry)pool.get(i); 133 try { 136 if(entry.lock()) { 137 if(DEEP_DEBUG) { 138 StringBuffer deepDebugBuffer = 139 new StringBuffer (128) 140 .append((new java.util.Date ()).toString()) 141 .append(" return a connection (") 142 .append(count) 143 .append(")"); 144 System.out.println(deepDebugBuffer.toString()); 145 } 146 return entry; 147 } 148 } catch(SQLException se) { 149 finalizeEntry(entry); 152 continue; 153 } 154 } if(DEEP_DEBUG) { 158 System.out.println(pool.size()); 159 } 160 try { 161 if(pool.size() == 0) { 162 PoolConnEntry entry = createConn(); 164 if(entry != null) { 165 if(DEEP_DEBUG) { 166 StringBuffer deepDebugBuffer = 167 new StringBuffer (128) 168 .append((new java.util.Date ()).toString()) 169 .append(" returning new connection (") 170 .append(count) 171 .append(")"); 172 System.out.println(deepDebugBuffer.toString()); 173 } 174 return entry; 175 } 176 } else { 178 if((attempts == 2) && (pool.size() < maxConn || maxConn == 0)) { 183 PoolConnEntry entry = createConn(); 184 if(entry != null) { 185 if(DEEP_DEBUG) { 186 StringBuffer deepDebugBuffer = 187 new StringBuffer (32) 188 .append(" returning new connection (") 189 .append(count) 190 .append(")"); 191 System.out.println(deepDebugBuffer.toString()); 192 } 193 return entry; 194 } else { 195 attempts = 1; 196 } 197 } 198 } 199 } catch(SQLException sqle) { 200 StringWriter sout = new StringWriter (); 202 PrintWriter pout = new PrintWriter (sout, true); 203 pout.println("Error creating connection: "); 204 sqle.printStackTrace(pout); 205 if (getLogger().isErrorEnabled()) { 206 getLogger().error(sout.toString()); 207 } 208 } 209 } 210 try { 212 Thread.currentThread().sleep(50); 213 } catch(InterruptedException ie) { 214 } 215 } 216 throw new SQLException ("Giving up... no connections available."); 218 } 219 220 223 public void configure(final Configuration configuration) 224 throws ConfigurationException { 225 try { 226 jdbcDriver = configuration.getChild("driver").getValue(null); 227 jdbcURL = configuration.getChild("dburl").getValue(null); 228 jdbcUsername = configuration.getChild("user").getValue(null); 229 jdbcPassword = configuration.getChild("password").getValue(null); 230 maxConn = configuration.getChild("max").getValueAsInteger(2); 231 verifyConnSQL = configuration.getChild("keep-alive").getValue(null); 233 if(jdbcDriver == null) { 236 throw new ConfigurationException("You need to specify a valid driver, e.g., <driver>my.class</driver>"); 237 } 238 try { 239 if (getLogger().isDebugEnabled()) { 240 getLogger().debug("Loading new driver: " + jdbcDriver); 241 } 242 Class.forName(jdbcDriver, true, Thread.currentThread().getContextClassLoader()); 247 } catch(ClassNotFoundException cnfe) { 251 StringBuffer exceptionBuffer = 252 new StringBuffer (128) 253 .append("'") 254 .append(jdbcDriver) 255 .append("' could not be found in classloader. Please specify a valid JDBC driver"); 256 String exceptionMessage = exceptionBuffer.toString(); 257 getLogger().error(exceptionMessage); 258 throw new ConfigurationException(exceptionMessage); 259 } 260 if(jdbcURL == null) { 261 throw new ConfigurationException("You need to specify a valid JDBC connection string, e.g., <dburl>jdbc:driver:database</dburl>"); 262 } 263 if(maxConn < 0) { 264 throw new ConfigurationException("Maximum number of connections specified must be at least 1 (0 means no limit)."); 265 } 266 if (getLogger().isDebugEnabled()) { 267 getLogger().debug("Starting connection pooler"); 268 getLogger().debug("driver = " + jdbcDriver); 269 getLogger().debug("dburl = " + jdbcURL); 270 getLogger().debug("username = " + jdbcUsername); 271 getLogger().debug("max connections = " + maxConn); 273 } 274 pool = new ArrayList (); 275 reaperActive = true; 276 reaper = new Thread (this); 277 reaper.setDaemon(true); 278 reaper.start(); 279 } catch(ConfigurationException ce) { 280 throw ce; 282 } 283 catch(Exception e) { 284 throw new ConfigurationException("Error configuring JdbcDataSource", e); 285 } 286 } 287 288 294 public void dispose() { 295 if(reaper != null) { 297 reaperActive = false; 298 reaper.interrupt(); 300 reaper = null; 301 } 302 } 305 306 312 public void killAllConnections() { 313 synchronized (pool) { pool.clear(); } 316 } 317 318 327 public void killConnection(PoolConnEntry entry) { 328 if(entry != null) { 329 if(verifyConnSQL != null) { 332 try { 333 java.sql.Statement stmt = null; 335 try { 336 stmt = entry.createStatement(); 337 stmt.execute(verifyConnSQL); 338 } finally { 339 try { 340 if (stmt != null) { 341 stmt.close(); 342 } 343 } catch (SQLException sqle) { 344 } 346 } 347 entry.unlock(); 349 } catch(SQLException e1) { 350 finalizeEntry(entry); 352 } 353 } else { 354 finalizeEntry(entry); 356 } 357 return; 358 } else { 359 if (getLogger().isWarnEnabled()) { 360 getLogger().warn("----> Could not find connection to kill!!!"); 361 } 362 return; 363 } 364 } 365 366 372 public void releaseConnection(PoolConnEntry entry) { 373 if(entry != null) { 375 entry.unlock(); 376 return; 377 } else { 378 if (getLogger().isWarnEnabled()) { 379 getLogger().warn("----> Could not find the connection to free!!!"); 380 } 381 return; 382 } 383 } 384 385 389 public void run() { 390 try { 391 while(reaperActive) { 392 synchronized(pool) { 393 for(int i = 0; i < pool.size(); i++) try { 394 PoolConnEntry entry = (PoolConnEntry)pool.get(i); 395 long age = System.currentTimeMillis() - entry.getLastActivity(); 396 synchronized(entry) { 397 if((entry.getStatus() == PoolConnEntry.ACTIVE) && 398 (age > ACTIVE_CONN_HARD_TIME_LIMIT)) { 399 StringBuffer logBuffer = 400 new StringBuffer (128) 401 .append(" ***** connection ") 402 .append(entry.getId()) 403 .append(" is way too old: ") 404 .append(age) 405 .append(" > ") 406 .append(ACTIVE_CONN_HARD_TIME_LIMIT) 407 .append(" and will be closed."); 408 getLogger().info(logBuffer.toString()); 409 finalizeEntry(entry); 412 continue; 413 } 414 if((entry.getStatus() == PoolConnEntry.ACTIVE) && 415 (age > ACTIVE_CONN_TIME_LIMIT)) { 416 StringBuffer logBuffer = 417 new StringBuffer (128) 418 .append(" ***** connection ") 419 .append(entry.getId()) 420 .append(" is way too old: ") 421 .append(age) 422 .append(" > ") 423 .append(ACTIVE_CONN_TIME_LIMIT); 424 getLogger().info(logBuffer.toString()); 425 continue; 428 } 429 if((entry.getStatus() == PoolConnEntry.AVAILABLE) && 430 (age > CONN_IDLE_LIMIT)) { 431 finalizeEntry(entry); 433 continue; 434 } 435 } 436 } 437 catch (Throwable ex) 438 { 439 StringWriter sout = new StringWriter (); 440 PrintWriter pout = new PrintWriter (sout, true); 441 pout.println("Reaper Error: "); 442 ex.printStackTrace(pout); 443 if (getLogger().isErrorEnabled()) { 444 getLogger().error(sout.toString()); 445 } 446 } 447 } 448 try { 449 Thread.sleep(5000L); 451 } catch(InterruptedException ex) { 452 } 453 } 454 } finally { 455 Thread.currentThread().interrupted(); 456 } 457 } 458 459 protected void debug(String message) { 460 getLogger().debug(message); 461 } 462 463 protected void info(String message) { 464 getLogger().info(message); 465 } 466 467 470 protected void warn(String message) { 471 getLogger().warn(message); 472 } 473 474 480 private PoolConnEntry createConn() throws SQLException { 481 PoolConnEntry entry = null; 482 synchronized(pool) { 483 if(connCreationsInProgress > 0) { 484 return null; 486 } 487 long now = System.currentTimeMillis(); 488 if((now - connLastCreated) < (1000 * pool.size())) { 489 if(DEEP_DEBUG) { 491 System.err.println("We don't want to scale up too quickly"); 492 } 493 return null; 494 } 495 if((maxConn == 0) || (pool.size() < maxConn)) { 496 connCreationsInProgress++; 497 connLastCreated = now; 498 } else { 499 if (getLogger().isDebugEnabled()) 501 { 502 StringBuffer logBuffer = 503 new StringBuffer (128) 504 .append("Connection limit hit... ") 505 .append(pool.size()) 506 .append(" in pool and ") 507 .append(connCreationsInProgress) 508 .append(" + on the way."); 509 getLogger().debug(logBuffer.toString()); 510 } 511 return null; 512 } 513 try { 514 entry = new PoolConnEntry(this, 515 java.sql.DriverManager.getConnection(jdbcURL, jdbcUsername, 516 jdbcPassword), 517 ++connectionCount); 518 if (getLogger().isDebugEnabled()) 519 { 520 getLogger().debug("Opening connection " + entry); 521 } 522 entry.lock(); 523 pool.add(entry); 524 return entry; 525 } catch(SQLException sqle) { 526 StringWriter sout = new StringWriter (); 529 PrintWriter pout = new PrintWriter (sout, true); 530 pout.println("Error creating connection: "); 531 sqle.printStackTrace(pout); 532 if (getLogger().isErrorEnabled()) { 533 getLogger().error(sout.toString()); 534 } 535 return null; 536 } finally { 537 connCreationsInProgress--; 538 } 539 } 540 } 541 542 547 private void finalizeEntry(PoolConnEntry entry) { 548 synchronized(pool) { 549 try { 550 entry.finalize(); 551 } catch(Exception fe) { 552 } 553 pool.remove(entry); 554 } 555 } 556 } 557 | Popular Tags |