1 package org.jahia.services.database; 2 3 import java.io.FileInputStream ; 4 import java.io.FileNotFoundException ; 5 import java.io.IOException ; 6 import java.sql.Connection ; 7 import java.sql.SQLException ; 8 import java.util.Enumeration ; 9 import java.util.Properties ; 10 11 import javax.naming.Context ; 12 import javax.naming.InitialContext ; 13 import javax.naming.NamingException ; 14 import javax.sql.DataSource ; 15 import javax.transaction.HeuristicMixedException ; 16 import javax.transaction.HeuristicRollbackException ; 17 import javax.transaction.NotSupportedException ; 18 import javax.transaction.RollbackException ; 19 import javax.transaction.Status ; 20 import javax.transaction.SystemException ; 21 import javax.transaction.UserTransaction ; 22 23 import org.apache.commons.dbcp.BasicDataSource; 24 import org.jahia.exceptions.JahiaInitializationException; 25 import org.jahia.settings.SettingsBean; 26 import org.jahia.utils.BeanTools; 27 28 36 37 public class ConnectionDispenser { 38 39 protected static org.apache.log4j.Logger logger = 40 org.apache.log4j.Logger.getLogger(ConnectionDispenser.class); 41 42 private static final String DB_POOL_PROPERTIES_FILE = "dbpool.properties"; 43 private static final String DB_EMPTY_POOL = "No available connections in pool"; 44 45 private static final String DATASOURCE_NAME = "datasource.name"; 46 47 48 private static final String USER_TRANSACTION_NAME = "userTransaction.name"; 49 50 private static ThreadLocalConnectionInfo threadLocalConnectionInfo = new 51 ThreadLocalConnectionInfo(); 52 private static ThreadLocalConnectionInfo threadLocalTxConnectionInfo = new 53 ThreadLocalConnectionInfo(); 54 55 private static boolean autoCommit = true; 56 private String serviceName; 57 private static BasicDataSource basicDataSource = null; 58 private static DataSource ds = null; 59 protected static int count; 60 61 62 private static UserTransaction ut = null; 63 64 65 private static boolean usingDatasource = false; 66 67 protected static boolean usingUserTransactions = false; 68 69 protected static class ThreadLocalConnectionInfo extends ThreadLocal { 70 public Object initialValue () { 71 return null; 72 } 73 } 74 75 private static class ConnectionInfo { 76 private boolean alreadyTerminated = false; 77 private Connection connection = null; 78 79 public ConnectionInfo (Connection connection) { 80 this.connection = new ConnectionWrapper(connection); 81 } 82 83 public Connection getConnection () { 84 return connection; 85 } 86 87 public void setConnection (Connection connection) { 88 this.connection = new ConnectionWrapper(connection); 89 } 90 91 public void removeConnection () { 92 alreadyTerminated = true; 93 try { 94 if (!usingUserTransactions && !connection.getAutoCommit()){ 95 connection.rollback(); 96 } 97 } catch (SQLException sqle) { 98 logger.error("Error while rolling back database statements", sqle); 99 } finally { 100 try { 101 ((ConnectionWrapper)connection).realClose(); 102 } catch (SQLException sqle) { 103 logger.error("Error while closing database connection", sqle); 104 } 105 count--; 106 logger.debug("Connections still active :" + count); 107 } 108 connection = null; 109 } 110 111 public boolean isAlreadyTerminated () { 112 return alreadyTerminated; 113 } 114 115 public void setAlreadyTerminated (boolean alreadyTerminated) { 116 this.alreadyTerminated = alreadyTerminated; 117 } 118 } 119 120 public static void init (SettingsBean jSettings) 121 throws 122 JahiaInitializationException { 123 124 basicDataSource = new BasicDataSource(); 126 127 128 Context ctx = null; 130 try { 132 String dsName = jSettings.getPropertiesFile().getProperty(DATASOURCE_NAME); 133 if (dsName == null || dsName.length() == 0) { 134 logger.debug("Datasource not defined in properties file..."); 135 } else { 136 logger.info("Searching " + dsName + " datasource from app server context..."); 137 ctx = new InitialContext (); 139 ds = (javax.sql.DataSource ) ctx.lookup (dsName); 141 usingDatasource = true; 142 143 basicDataSource.setMaxActive(0); 145 basicDataSource.setMaxIdle(0); 146 147 logger.info("Datasource retrieved from context"); 148 } 149 } catch (NamingException ne) { 150 logger.error("Couldn't find datasource into server context", ne); 151 } 152 153 154 155 if (jSettings.isDb_transactions()) { 156 logger.debug( 157 "Transactions are supported by database or driver so activating support..."); 158 autoCommit = false; 159 160 try { 162 String utName = jSettings.getPropertiesFile().getProperty(USER_TRANSACTION_NAME); 163 if (utName == null || utName.length() == 0) { 164 logger.debug("User transaction not defined in properties file..."); 165 } else { 166 logger.info("Searching " + utName + " user transaction from app server context..."); 167 ctx = new InitialContext (); 169 ut = (UserTransaction ) ctx.lookup (utName); 171 usingUserTransactions = true; 172 logger.info("User transaction retrieved from context"); 173 } 174 } catch (NamingException ne) { 175 logger.error("Couldn't find user transaction into server context", ne); 176 } 177 178 } else { 179 autoCommit = true; 180 } 181 182 if (!usingDatasource) { 183 basicDataSource.setDriverClassName(jSettings.getDb_driver()); 184 basicDataSource.setUsername(jSettings.getDb_username()); 185 basicDataSource.setPassword(jSettings.getDb_password()); 186 basicDataSource.setUrl(jSettings.getDb_url()); 187 basicDataSource.setPoolPreparedStatements(jSettings.isDb_poolPreparedStatements()); 188 basicDataSource.setMaxOpenPreparedStatements(jSettings.getDb_maxOpenPreparedStatements()); 189 190 loadDBPoolConfiguration(jSettings); 191 192 ds = basicDataSource; 193 } 194 195 count = 0; 196 } 197 198 public static void shutdown() 199 throws SQLException { 200 if (basicDataSource != null) { 201 basicDataSource.close(); 202 } 203 } 204 205 private static void loadDBPoolConfiguration (SettingsBean jSettings) { 206 try { 207 Properties dbPoolProperties = new Properties (); 208 FileInputStream dbPoolPropertiesInput = new FileInputStream ( 209 jSettings.getJahiaEtcDiskPath() + "/config/" + 210 DB_POOL_PROPERTIES_FILE); 211 dbPoolProperties.load(dbPoolPropertiesInput); 212 Enumeration propertyNameEnum = dbPoolProperties.propertyNames(); 213 while (propertyNameEnum.hasMoreElements()) { 214 String propertyName = (String ) propertyNameEnum.nextElement(); 215 String propertyValue = dbPoolProperties.getProperty(propertyName); 216 if (!"".equals(propertyValue.trim())) { 217 if (propertyName.startsWith("boolean.")) { 218 propertyName = propertyName.substring("boolean.".length()); 219 BeanTools.setBooleanProperty(basicDataSource, 220 propertyName, propertyValue); 221 } else if (propertyName.startsWith("int.")) { 222 propertyName = propertyName.substring("int.".length()); 223 BeanTools.setIntProperty(basicDataSource, propertyName, 224 propertyValue); 225 } else if (propertyName.startsWith("long.")) { 226 propertyName = propertyName.substring("long.".length()); 227 BeanTools.setLongProperty(basicDataSource, propertyName, 228 propertyValue); 229 } else if (propertyName.startsWith("String.")) { 230 propertyName = propertyName.substring("String.".length()); 231 BeanTools.setStringProperty(basicDataSource, 232 propertyName, propertyValue); 233 } 234 } 235 } 236 } catch (FileNotFoundException fnfe) { 237 logger.error("Couldn't find database pool configuration file", fnfe); 238 } catch (IOException ioe) { 239 logger.error("Error while reading database pool configuration file", ioe); 240 } 241 } 242 243 public static javax.sql.DataSource getDataSource() { 244 return ds; 245 } 246 247 public static Connection getConnection () { 248 return getConnection(autoCommit == true ? threadLocalConnectionInfo : threadLocalTxConnectionInfo, autoCommit); 249 } 250 251 public static Connection getTxConnection () { 252 return getConnection(threadLocalTxConnectionInfo, false); 253 } 254 255 private static Connection getConnection (ThreadLocalConnectionInfo threadLocalConnectionInfo, boolean autoCommit) { 256 Connection newConnection = null; 257 258 ConnectionInfo connectionInfo = (ConnectionInfo) 259 threadLocalConnectionInfo.get(); 260 261 if (connectionInfo == null) { 262 try { 263 logger.debug("Retrieving new connection from pool..."+ basicDataSource.getNumActive() + "/" +basicDataSource.getNumIdle()); 264 265 if (usingUserTransactions) 266 ut.begin(); 267 newConnection = ds.getConnection(); 268 269 if (!usingUserTransactions) 270 newConnection.setAutoCommit(autoCommit); 271 connectionInfo = new ConnectionInfo(newConnection); 272 connectionInfo.setAlreadyTerminated(false); 274 count++; 275 threadLocalConnectionInfo.set(connectionInfo); 276 } catch (SQLException sqle) { 277 try { 278 279 if (usingUserTransactions && 280 ut.getStatus() != Status.STATUS_NO_TRANSACTION) { 281 ut.rollback(); 282 } 283 } 284 catch (Exception e) { 285 logger.error("Error while aborting transaction", e); 286 } 287 String msg = "Error while retrieving connection to database"; 288 logger.error(msg + ": ", sqle); 289 throw new IllegalStateException (msg); 290 291 } catch (SystemException syse) { 292 try { 293 if (newConnection != null) 294 newConnection.close(); 295 } 296 catch (SQLException sqle) { 297 logger.error("Error while closing database connection", sqle); 298 } 299 String msg = "Error while beginning user transaction"; 300 logger.error(msg + ": ", syse); 301 throw new IllegalStateException (msg); 302 } catch (NotSupportedException nse) { 303 try { 304 305 if (usingUserTransactions && 306 ut.getStatus() != Status.STATUS_NO_TRANSACTION) { 307 ut.rollback(); 308 } 309 } 310 catch (Exception e) { 311 logger.error("Error while aborting transaction", e); 312 } 313 try { 314 if (newConnection != null) 315 newConnection.close(); 316 } 317 catch (SQLException sqle) { 318 logger.error("Error while closing database connection", sqle); 319 } 320 String msg = "Error while beginning user transaction"; 321 logger.error(msg + ": ", nse); 322 throw new IllegalStateException (msg); 323 } 324 } else if (connectionInfo.getConnection() == null) { 325 try { 326 logger.debug("Connection was closed previously in this thread, retrieving new connection from pool..."+ basicDataSource.getNumActive() + "/" +basicDataSource.getNumIdle()); 327 328 if (usingUserTransactions && 329 ut.getStatus() != Status.STATUS_NO_TRANSACTION) { 330 ut.rollback(); 331 } 332 333 if (usingUserTransactions) 334 ut.begin(); 335 newConnection = ds.getConnection(); 336 337 if (!usingUserTransactions) 338 newConnection.setAutoCommit(autoCommit); 339 connectionInfo.setConnection(newConnection); 340 connectionInfo.setAlreadyTerminated(false); 341 threadLocalConnectionInfo.set(connectionInfo); 342 count++; 343 } catch (SQLException sqle) { 344 try { 345 346 if (usingUserTransactions && 347 ut.getStatus() != Status.STATUS_NO_TRANSACTION) { 348 ut.rollback(); 349 } 350 } 351 catch (Exception e) { 352 logger.error("Error while aborting transaction", e); 353 } 354 String msg = "Error while retrieving connection to database"; 355 logger.error(msg + ": ", sqle); 356 throw new IllegalStateException (msg); 357 358 } catch (SystemException syse) { 359 try { 360 if (newConnection != null) 361 newConnection.close(); 362 } 363 catch (SQLException sqle) { 364 logger.error("Error while closing database connection", sqle); 365 } 366 String msg = "Error while beginning user transaction"; 367 logger.error(msg + ": ", syse); 368 throw new IllegalStateException (msg); 369 } catch (NotSupportedException nse) { 370 try { 371 372 if (usingUserTransactions && 373 ut.getStatus() != Status.STATUS_NO_TRANSACTION) { 374 ut.rollback(); 375 } 376 } 377 catch (Exception e) { 378 logger.error("Error while aborting transaction", e); 379 } 380 try { 381 if (newConnection != null) 382 newConnection.close(); 383 } 384 catch (SQLException sqle) { 385 logger.error("Error while closing database connection", sqle); 386 } 387 String msg = "Error while beginning user transaction"; 388 logger.error(msg + ": ", nse); 389 throw new IllegalStateException (msg); 390 } 391 } 392 return connectionInfo.getConnection(); 393 } 394 395 public static boolean isAutoCommit () { 396 return autoCommit; 397 } 398 399 public static boolean isUsingUserTransactions () { 400 return usingUserTransactions; 401 } 402 403 public static void setAutoCommit (boolean defaultAutoCommit) { 404 autoCommit = defaultAutoCommit; 405 } 406 407 public static void abortConnection () { 408 if (autoCommit == true) { 409 abortConnection(threadLocalConnectionInfo); 410 } 411 abortConnection(threadLocalTxConnectionInfo); 412 } 413 414 private static void abortConnection (ThreadLocalConnectionInfo threadLocalConnectionInfo) { 415 logger.info("abortConnection : " + threadLocalConnectionInfo.get()); 416 ConnectionInfo curConnectionInfo = (ConnectionInfo) 417 threadLocalConnectionInfo.get(); 418 if (curConnectionInfo == null) { 419 logger.debug("No connection in thread, exiting abort call..."); 420 return; 421 } 422 if (curConnectionInfo.isAlreadyTerminated()) { 423 logger.debug( 424 "Connection was already previously terminated, ignoring !"); 425 return; 426 } 427 Connection curConnection = curConnectionInfo.getConnection(); 428 try { 429 if (!curConnection.getAutoCommit()) { 430 logger.debug("Rolling back database transactions..."); 431 432 433 if (usingUserTransactions) 434 ut.rollback(); 435 else 436 curConnection.rollback(); 437 } 438 } catch (SQLException sqle) { 439 logger.error("Error while rollbacking database transaction", sqle); 440 441 } catch (SystemException syse) { 442 logger.error("Error while rollbacking JTA transaction", syse); 443 } finally { 444 curConnectionInfo.removeConnection(); 445 } 446 } 447 448 public static void terminateConnection () { 449 if (autoCommit == true) { 450 terminateConnection(threadLocalConnectionInfo); 451 } 452 terminateConnection(threadLocalTxConnectionInfo); 453 } 454 455 private static void terminateConnection (ThreadLocalConnectionInfo threadLocalConnectionInfo) { 456 ConnectionInfo curConnectionInfo = (ConnectionInfo) 457 threadLocalConnectionInfo.get(); 458 if (curConnectionInfo == null) { 459 logger.debug("No connection in thread, exiting terminate call..."); 460 return; 461 } 462 if (curConnectionInfo.isAlreadyTerminated()) { 463 logger.debug( 464 "Connection was already previously terminated, ignoring !"); 465 return; 466 } 467 468 Connection curConnection = curConnectionInfo.getConnection(); 469 try { 470 if (!curConnection.getAutoCommit()) { 471 logger.debug("Commiting database transactions..."); 472 473 474 if (usingUserTransactions && 475 ut.getStatus() != Status.STATUS_NO_TRANSACTION) { 476 477 ut.commit(); 478 } 479 else 480 curConnection.commit(); 481 } 482 } catch (SQLException sqle) { 483 logger.error("Error while committing database transaction", sqle); 484 485 } catch (SystemException syse) { 486 logger.error("Error while committing JTA transaction", syse); 487 } catch (HeuristicRollbackException hre) { 488 logger.error("Heuristic Rollback Error while committing JTA transaction", hre); 489 } catch (RollbackException re) { 490 logger.error("Error while committing JTA transaction", re); 491 } catch (HeuristicMixedException hme) { 492 logger.error("Heuristic Mixed Error while committing JTA transaction", hme); 493 } finally { 494 curConnectionInfo.removeConnection(); 495 } 496 } 497 498 public static int getNbActive () { 499 return basicDataSource.getNumActive(); 500 } 501 502 public static int getMaxActive () { 503 return basicDataSource.getMaxActive(); 504 } 506 507 public static int getNbIdle () { 508 return basicDataSource.getNumIdle(); 509 } 510 511 public static int getMaxIdle () { 512 return basicDataSource.getMaxIdle(); 513 } 514 } | Popular Tags |