1 11 12 package org.jivesoftware.database; 13 14 import org.jivesoftware.util.ClassUtils; 15 import org.jivesoftware.util.Log; 16 import org.jivesoftware.util.JiveGlobals; 17 18 import java.io.IOException ; 19 import java.sql.*; 20 import java.util.*; 21 22 27 public class ConnectionPool implements Runnable { 28 29 private String driver; 30 private String serverURL; 31 private String username; 32 private String password; 33 private int minCon; 34 private int maxCon; 35 private int conTimeout; 36 private boolean mysqlUseUnicode; 37 38 private Thread houseKeeper; 39 private boolean shutdownStarted = false; 40 41 private int conCount = 0; 42 private int waitingForCon = 0; 43 private Connection[] cons; 44 private ConnectionWrapper[] wrappers; 45 private Object waitLock = new Object (); 46 private Object conCountLock = new Object (); 47 48 public ConnectionPool(String driver, String serverURL, String username, 49 String password, int minCon, int maxCon, 50 double conTimeout, boolean mysqlUseUnicode) throws IOException { 51 this.driver = driver; 52 this.serverURL = serverURL; 53 this.username = username; 54 this.password = password; 55 this.minCon = minCon; 56 this.maxCon = maxCon; 57 this.conTimeout = (int)(conTimeout * 1000 * 60 * 60 * 3); this.mysqlUseUnicode = mysqlUseUnicode; 60 61 if (driver == null) { 62 Log.error("JDBC driver value is null."); 63 } 64 try { 65 ClassUtils.forName(driver); 66 DriverManager.getDriver(serverURL); 67 } 68 catch (ClassNotFoundException e) { 69 Log.error("Could not load JDBC driver class: " + driver); 70 } 71 catch (SQLException e) { 72 Log.error("Error starting connection pool.", e); 73 } 74 75 wrappers = new ConnectionWrapper[maxCon]; 77 cons = new Connection[maxCon]; 78 79 boolean success = false; 80 int maxTry = 3; 81 82 for (int i = 0; i < maxTry; i++) { 83 try { 84 for (int j = 0; j < minCon; j++) { 85 createCon(j); 86 conCount++; 87 } 88 89 success = true; 90 break; 91 } 92 catch (SQLException e) { 93 for (int j = 0; j < minCon; j++) { 95 if (cons[j] != null) { 96 try { 97 cons[j].close(); 98 cons[j] = null; 99 wrappers[j] = null; 100 conCount--; 101 } 102 catch (SQLException e1) { 103 } 104 } 105 } 106 107 Log.error("Failed to create new connections on startup. " + 109 "Attempt " + i + " of " + maxTry, e); 110 111 try { 112 Thread.sleep(10000); 113 } 114 catch (InterruptedException e1) { 115 } 116 } 117 } 118 119 if (!success) { 120 throw new IOException (); 121 } 122 123 houseKeeper = new Thread (this); 125 houseKeeper.setDaemon(true); 126 houseKeeper.start(); 127 } 128 129 public Connection getConnection() throws SQLException { 130 if (shutdownStarted) { 132 return null; 133 } 134 135 ConnectionWrapper con = getCon(); 138 139 if (con != null) { 140 synchronized (con) { 141 con.checkedout = true; 142 con.lockTime = System.currentTimeMillis(); 143 } 144 return con; 145 } 146 else { 147 synchronized (waitLock) { 148 try { 149 waitingForCon++; 150 while (true) { 151 con = getCon(); 152 153 if (con != null) { 154 --waitingForCon; 155 synchronized (con) { 156 con.checkedout = true; 157 con.lockTime = System.currentTimeMillis(); 158 } 159 return con; 160 } 161 else { 162 waitLock.wait(); 163 } 164 } 165 } 166 catch (InterruptedException ex) { 167 --waitingForCon; 168 waitLock.notify(); 169 170 throw new SQLException("Interrupted while waiting for connection to " + 171 "become available."); 172 } 173 } 174 } 175 } 176 177 public void freeConnection() { 178 synchronized (waitLock) { 179 if (waitingForCon > 0) { 180 waitLock.notify(); 181 } 182 } 183 } 184 185 public void destroy() throws SQLException { 186 shutdownStarted = true; 188 189 houseKeeper.interrupt(); 191 192 try { 194 houseKeeper.join(500); 195 } 196 catch (InterruptedException e) { 197 } 198 199 for (int i = 0; i < conCount; i++) { 201 ConnectionWrapper wrapper = wrappers[i]; 202 203 if (wrapper == null) { 206 break; 207 } 208 209 if (wrapper.checkedout) { 211 try { 212 Thread.sleep(500); 213 } 214 catch (InterruptedException e) { 215 } 216 217 if (wrapper.checkedout) { 218 Log.info("Forcefully closing connection " + i); 219 } 220 } 221 222 cons[i].close(); 223 cons[i] = null; 224 wrappers[i] = null; 225 } 226 } 227 228 public int getSize() { 229 return conCount; 230 } 231 232 245 public void run() { 246 while (true) { 247 for (int i = 0; i < maxCon; i++) { 249 if (cons[i] == null) { 250 continue; 251 } 252 253 try { 254 SQLWarning warning = cons[i].getWarnings(); 255 if (warning != null) { 256 Log.warn("Connection " + i + " had warnings: " + warning); 257 cons[i].clearWarnings(); 258 } 259 } 260 catch (SQLException e) { 261 Log.warn("Unable to get warning for connection: ", e); 262 } 263 } 264 265 int lastOpen = -1; 266 267 for (int i = maxCon - 1; i >= 0; i--) { 269 if (wrappers[i] == null) { 270 continue; 271 } 272 273 try { 274 long time = System.currentTimeMillis(); 275 276 synchronized (wrappers[i]) { 277 if (wrappers[i].checkedout) { 278 if (lastOpen < i) { 279 lastOpen = i; 280 } 281 282 283 if ("true".equals(JiveGlobals.getXMLProperty("database.defaultProvider.checkOpenConnections")) 287 && !wrappers[i].hasLoggedException) 288 { 289 int timeout = 600; 290 try { timeout = Integer.parseInt(JiveGlobals.getXMLProperty("database.defaultProvider.openConnectionTimeLimit")); } 291 catch (Exception e) { } 292 293 if (time - wrappers[i].lockTime > timeout * 1000) { 294 wrappers[i].hasLoggedException = true; 295 Log.warn("Connection has been held open for too long: ", 296 wrappers[i].exception); 297 } 298 } 299 300 continue; 301 } 302 wrappers[i].checkedout = true; 303 } 304 305 Statement stmt = null; 307 try { 308 stmt = cons[i].createStatement(); 309 } 310 finally { 311 if (stmt != null) { 312 stmt.close(); 313 } 314 } 315 316 if (cons[i].isClosed()) { 318 throw new SQLException(); 319 } 320 321 if (time - wrappers[i].createTime > conTimeout) { 323 throw new SQLException(); 324 } 325 326 if ((time - wrappers[i].checkinTime > 60 * 1000) && i > minCon && 329 lastOpen <= i) { 330 synchronized (conCountLock) { 331 cons[i].close(); 332 wrappers[i] = null; 333 cons[i] = null; 334 conCount--; 335 } 336 } 337 338 lastOpen = i; 340 341 if (wrappers[i] != null) { 343 wrappers[i].checkedout = false; 344 } 345 346 } 347 catch (SQLException e) { 348 try { 349 synchronized (conCountLock) { 350 cons[i].close(); 351 wrappers[i] = createCon(i); 352 353 wrappers[i].checkedout = false; 355 } 356 } 357 catch (SQLException sqle) { 358 Log.warn("Failed to reopen connection", sqle); 359 360 synchronized (conCountLock) { 361 wrappers[i] = null; 362 cons[i] = null; 363 conCount--; 364 } 365 } 366 } 367 } 368 369 try { 370 Thread.sleep(30 * 1000); 371 } 372 catch (InterruptedException e) { 373 return; 374 } 375 } 376 } 377 378 private synchronized ConnectionWrapper getCon() throws SQLException { 379 for (int i = 0; i < conCount; i++) { 381 ConnectionWrapper wrapper = wrappers[i]; 382 383 if (wrapper == null) { 386 break; 387 } 388 389 synchronized (wrapper) { 390 if (!wrapper.checkedout) { 391 wrapper.setConnection(cons[i]); 392 wrapper.checkedout = true; 393 wrapper.lockTime = System.currentTimeMillis(); 394 if ("true".equals(JiveGlobals.getXMLProperty("database.defaultProvider.checkOpenConnections"))) { 395 wrapper.exception = new Exception (); 396 wrapper.hasLoggedException = false; 397 } 398 399 return wrapper; 400 } 401 } 402 } 403 404 synchronized (conCountLock) { 406 if (conCount >= maxCon) { 407 return null; 408 } 409 410 ConnectionWrapper con = createCon(conCount); 411 conCount++; 412 return con; 413 } 414 } 415 416 419 private ConnectionWrapper createCon(int index) throws SQLException { 420 try { 421 Connection con = null; 422 ClassUtils.forName(driver); 423 424 if (mysqlUseUnicode) { 425 Properties props = new Properties(); 426 props.put("characterEncoding", "UTF-8"); 427 props.put("useUnicode", "true"); 428 if (username != null) { 429 props.put("user", username); 430 } 431 if (password != null) { 432 props.put("password", password); 433 } 434 con = DriverManager.getConnection(serverURL, props); 435 } 436 else { 437 con = DriverManager.getConnection(serverURL, username, password); 438 } 439 440 if (con == null) { 441 throw new SQLException("Unable to retrieve connection from DriverManager"); 442 } 443 444 445 try { 446 con.setAutoCommit(true); 447 } 448 catch (SQLException e) { 449 } 450 451 452 try { 457 if (con.getMetaData().supportsTransactions()) { 459 con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); 460 } 461 } 462 catch (SQLException e) { 463 } 466 467 ConnectionWrapper wrapper = new ConnectionWrapper(con, this); 469 if ("true".equals(JiveGlobals.getXMLProperty("database.defaultProvider.checkOpenConnections"))) { 470 wrapper.exception = new Exception (); 471 } 472 473 synchronized (conCountLock) { 474 cons[index] = con; 475 wrappers[index] = wrapper; 476 } 477 478 return wrapper; 479 } 480 catch (ClassNotFoundException e) { 481 Log.error(e); 482 throw new SQLException(e.getMessage()); 483 } 484 } 485 486 } 487 | Popular Tags |