001package org.avaje.datasource; 002 003import java.sql.Connection; 004import java.util.LinkedHashMap; 005import java.util.Map; 006import java.util.Properties; 007 008/** 009 * Configuration information for a DataSource. 010 */ 011public class DataSourceConfig { 012 013 private String url; 014 015 private String username; 016 017 private String password; 018 019 private String driver; 020 021 private int minConnections = 2; 022 023 private int maxConnections = 200; 024 025 private int isolationLevel = Connection.TRANSACTION_READ_COMMITTED; 026 027 private boolean autoCommit; 028 029 private boolean readOnly; 030 031 private String heartbeatSql; 032 033 private int heartbeatFreqSecs = 30; 034 035 private int heartbeatTimeoutSeconds = 3; 036 037 private boolean captureStackTrace; 038 039 private int maxStackTraceSize = 5; 040 041 private int leakTimeMinutes = 30; 042 043 private int maxInactiveTimeSecs = 300; 044 045 private int maxAgeMinutes = 0; 046 047 private int trimPoolFreqSecs = 59; 048 049 private int pstmtCacheSize = 20; 050 051 private int cstmtCacheSize = 20; 052 053 private int waitTimeoutMillis = 1000; 054 055 private String poolListener; 056 057 private boolean offline; 058 059 private Map<String, String> customProperties; 060 061 private DataSourceAlert alert; 062 063 private DataSourcePoolListener listener; 064 065 /** 066 * Default the values for driver, url, username and password from another config if 067 * they have not been set. 068 */ 069 public void setDefaults(DataSourceConfig other) { 070 if (driver == null) { 071 driver = other.driver; 072 } 073 if (url == null) { 074 url = other.url; 075 } 076 if (username == null) { 077 username = other.username; 078 } 079 if (password == null) { 080 password = other.password; 081 } 082 } 083 084 /** 085 * Return true if there are no values set for any of url, driver, username and password. 086 */ 087 public boolean isEmpty() { 088 return url == null 089 && driver == null 090 && username == null 091 && password == null; 092 } 093 094 /** 095 * Return the connection URL. 096 */ 097 public String getUrl() { 098 return url; 099 } 100 101 /** 102 * Set the connection URL. 103 */ 104 public void setUrl(String url) { 105 this.url = url; 106 } 107 108 /** 109 * Return the database username. 110 */ 111 public String getUsername() { 112 return username; 113 } 114 115 /** 116 * Set the database username. 117 */ 118 public void setUsername(String username) { 119 this.username = username; 120 } 121 122 /** 123 * Return the database password. 124 */ 125 public String getPassword() { 126 return password; 127 } 128 129 /** 130 * Set the database password. 131 */ 132 public void setPassword(String password) { 133 this.password = password; 134 } 135 136 /** 137 * Return the database driver. 138 */ 139 public String getDriver() { 140 return driver; 141 } 142 143 /** 144 * Set the database driver. 145 */ 146 public void setDriver(String driver) { 147 this.driver = driver; 148 } 149 150 /** 151 * Return the transaction isolation level. 152 */ 153 public int getIsolationLevel() { 154 return isolationLevel; 155 } 156 157 /** 158 * Set the transaction isolation level. 159 */ 160 public void setIsolationLevel(int isolationLevel) { 161 this.isolationLevel = isolationLevel; 162 } 163 164 /** 165 * Return autoCommit setting. 166 */ 167 public boolean isAutoCommit() { 168 return autoCommit; 169 } 170 171 /** 172 * Set to true to turn on autoCommit. 173 */ 174 public void setAutoCommit(boolean autoCommit) { 175 this.autoCommit = autoCommit; 176 } 177 178 /** 179 * Return the read only setting. 180 */ 181 public boolean isReadOnly() { 182 return readOnly; 183 } 184 185 /** 186 * Set to true to for read only. 187 */ 188 public void setReadOnly(boolean readOnly) { 189 this.readOnly = readOnly; 190 } 191 192 /** 193 * Return the minimum number of connections the pool should maintain. 194 */ 195 public int getMinConnections() { 196 return minConnections; 197 } 198 199 /** 200 * Set the minimum number of connections the pool should maintain. 201 */ 202 public void setMinConnections(int minConnections) { 203 this.minConnections = minConnections; 204 } 205 206 /** 207 * Return the maximum number of connections the pool can reach. 208 */ 209 public int getMaxConnections() { 210 return maxConnections; 211 } 212 213 /** 214 * Set the maximum number of connections the pool can reach. 215 */ 216 public void setMaxConnections(int maxConnections) { 217 this.maxConnections = maxConnections; 218 } 219 220 /** 221 * Return the alert implementation to use. 222 */ 223 public DataSourceAlert getAlert() { 224 return alert; 225 } 226 227 /** 228 * Set the alert implementation to use. 229 */ 230 public void setAlert(DataSourceAlert alert) { 231 this.alert = alert; 232 } 233 234 /** 235 * Return the listener to use. 236 */ 237 public DataSourcePoolListener getListener() { 238 return listener; 239 } 240 241 /** 242 * Set the listener to use. 243 */ 244 public void setListener(DataSourcePoolListener listener) { 245 this.listener = listener; 246 } 247 248 /** 249 * Return a SQL statement used to test the database is accessible. 250 * <p> 251 * Note that if this is not set then it can get defaulted from the 252 * DatabasePlatform. 253 * </p> 254 */ 255 public String getHeartbeatSql() { 256 return heartbeatSql; 257 } 258 259 /** 260 * Set a SQL statement used to test the database is accessible. 261 * <p> 262 * Note that if this is not set then it can get defaulted from the 263 * DatabasePlatform. 264 * </p> 265 */ 266 public void setHeartbeatSql(String heartbeatSql) { 267 this.heartbeatSql = heartbeatSql; 268 } 269 270 /** 271 * Return the heartbeat frequency in seconds. 272 * <p> 273 * This is the expected frequency in which the DataSource should be checked to 274 * make sure it is healthy and trim idle connections. 275 * </p> 276 */ 277 public int getHeartbeatFreqSecs() { 278 return heartbeatFreqSecs; 279 } 280 281 /** 282 * Set the expected heartbeat frequency in seconds. 283 */ 284 public void setHeartbeatFreqSecs(int heartbeatFreqSecs) { 285 this.heartbeatFreqSecs = heartbeatFreqSecs; 286 } 287 288 /** 289 * Return the heart beat timeout in seconds. 290 */ 291 public int getHeartbeatTimeoutSeconds() { 292 return heartbeatTimeoutSeconds; 293 } 294 295 /** 296 * Set the heart beat timeout in seconds. 297 */ 298 public void setHeartbeatTimeoutSeconds(int heartbeatTimeoutSeconds) { 299 this.heartbeatTimeoutSeconds = heartbeatTimeoutSeconds; 300 } 301 302 /** 303 * Return true if a stack trace should be captured when obtaining a connection 304 * from the pool. 305 * <p> 306 * This can be used to diagnose a suspected connection pool leak. 307 * </p> 308 * <p> 309 * Obviously this has a performance overhead. 310 * </p> 311 */ 312 public boolean isCaptureStackTrace() { 313 return captureStackTrace; 314 } 315 316 /** 317 * Set to true if a stack trace should be captured when obtaining a connection 318 * from the pool. 319 * <p> 320 * This can be used to diagnose a suspected connection pool leak. 321 * </p> 322 * <p> 323 * Obviously this has a performance overhead. 324 * </p> 325 */ 326 public void setCaptureStackTrace(boolean captureStackTrace) { 327 this.captureStackTrace = captureStackTrace; 328 } 329 330 /** 331 * Return the max size for reporting stack traces on busy connections. 332 */ 333 public int getMaxStackTraceSize() { 334 return maxStackTraceSize; 335 } 336 337 /** 338 * Set the max size for reporting stack traces on busy connections. 339 */ 340 public void setMaxStackTraceSize(int maxStackTraceSize) { 341 this.maxStackTraceSize = maxStackTraceSize; 342 } 343 344 /** 345 * Return the time in minutes after which a connection could be considered to 346 * have leaked. 347 */ 348 public int getLeakTimeMinutes() { 349 return leakTimeMinutes; 350 } 351 352 /** 353 * Set the time in minutes after which a connection could be considered to 354 * have leaked. 355 */ 356 public void setLeakTimeMinutes(int leakTimeMinutes) { 357 this.leakTimeMinutes = leakTimeMinutes; 358 } 359 360 /** 361 * Return the size of the PreparedStatement cache (per connection). 362 */ 363 public int getPstmtCacheSize() { 364 return pstmtCacheSize; 365 } 366 367 /** 368 * Set the size of the PreparedStatement cache (per connection). 369 */ 370 public void setPstmtCacheSize(int pstmtCacheSize) { 371 this.pstmtCacheSize = pstmtCacheSize; 372 } 373 374 /** 375 * Return the size of the CallableStatement cache (per connection). 376 */ 377 public int getCstmtCacheSize() { 378 return cstmtCacheSize; 379 } 380 381 /** 382 * Set the size of the CallableStatement cache (per connection). 383 */ 384 public void setCstmtCacheSize(int cstmtCacheSize) { 385 this.cstmtCacheSize = cstmtCacheSize; 386 } 387 388 /** 389 * Return the time in millis to wait for a connection before timing out once 390 * the pool has reached its maximum size. 391 */ 392 public int getWaitTimeoutMillis() { 393 return waitTimeoutMillis; 394 } 395 396 /** 397 * Set the time in millis to wait for a connection before timing out once the 398 * pool has reached its maximum size. 399 */ 400 public void setWaitTimeoutMillis(int waitTimeoutMillis) { 401 this.waitTimeoutMillis = waitTimeoutMillis; 402 } 403 404 /** 405 * Return the time in seconds a connection can be idle after which it can be 406 * trimmed from the pool. 407 * <p> 408 * This is so that the pool after a busy period can trend over time back 409 * towards the minimum connections. 410 * </p> 411 */ 412 public int getMaxInactiveTimeSecs() { 413 return maxInactiveTimeSecs; 414 } 415 416 /** 417 * Return the maximum age a connection is allowed to be before it is closed. 418 * <p> 419 * This can be used to close really old connections. 420 * </p> 421 */ 422 public int getMaxAgeMinutes() { 423 return maxAgeMinutes; 424 } 425 426 /** 427 * Set the maximum age a connection can be in minutes. 428 */ 429 public void setMaxAgeMinutes(int maxAgeMinutes) { 430 this.maxAgeMinutes = maxAgeMinutes; 431 } 432 433 /** 434 * Set the time in seconds a connection can be idle after which it can be 435 * trimmed from the pool. 436 * <p> 437 * This is so that the pool after a busy period can trend over time back 438 * towards the minimum connections. 439 * </p> 440 */ 441 public void setMaxInactiveTimeSecs(int maxInactiveTimeSecs) { 442 this.maxInactiveTimeSecs = maxInactiveTimeSecs; 443 } 444 445 446 /** 447 * Return the minimum time gap between pool trim checks. 448 * <p> 449 * This defaults to 59 seconds meaning that the pool trim check will run every 450 * minute assuming the heart beat check runs every 30 seconds. 451 * </p> 452 */ 453 public int getTrimPoolFreqSecs() { 454 return trimPoolFreqSecs; 455 } 456 457 /** 458 * Set the minimum trim gap between pool trim checks. 459 */ 460 public void setTrimPoolFreqSecs(int trimPoolFreqSecs) { 461 this.trimPoolFreqSecs = trimPoolFreqSecs; 462 } 463 464 /** 465 * Return the pool listener. 466 */ 467 public String getPoolListener() { 468 return poolListener; 469 } 470 471 /** 472 * Set a pool listener. 473 */ 474 public void setPoolListener(String poolListener) { 475 this.poolListener = poolListener; 476 } 477 478 /** 479 * Return true if the DataSource should be left offline. 480 * <p> 481 * This is to support DDL generation etc without having a real database. 482 * </p> 483 */ 484 public boolean isOffline() { 485 return offline; 486 } 487 488 /** 489 * Set to true if the DataSource should be left offline. 490 */ 491 public void setOffline(boolean offline) { 492 this.offline = offline; 493 } 494 495 /** 496 * Return a map of custom properties for the jdbc driver connection. 497 */ 498 public Map<String, String> getCustomProperties() { 499 return customProperties; 500 } 501 502 /** 503 * Set custom properties for the jdbc driver connection. 504 */ 505 public void setCustomProperties(Map<String, String> customProperties) { 506 this.customProperties = customProperties; 507 } 508 509 /** 510 * Load the settings from the properties supplied. 511 * <p> 512 * You can use this when you have your own properties to use for configuration. 513 * </p> 514 * 515 * @param properties the properties to configure the dataSource 516 * @param serverName the name of the specific dataSource (optional) 517 */ 518 public void loadSettings(Properties properties, String serverName) { 519 ConfigPropertiesHelper dbProps = new ConfigPropertiesHelper("datasource", serverName, properties); 520 loadSettings(dbProps); 521 } 522 523 /** 524 * Load the settings from the PropertiesWrapper. 525 */ 526 private void loadSettings(ConfigPropertiesHelper properties) { 527 528 username = properties.get("username", username); 529 password = properties.get("password", password); 530 driver = properties.get("driver", properties.get("databaseDriver", driver)); 531 url = properties.get("url", properties.get("databaseUrl", url)); 532 autoCommit = properties.getBoolean("autoCommit", autoCommit); 533 readOnly = properties.getBoolean("readOnly", readOnly); 534 captureStackTrace = properties.getBoolean("captureStackTrace", captureStackTrace); 535 maxStackTraceSize = properties.getInt("maxStackTraceSize", maxStackTraceSize); 536 leakTimeMinutes = properties.getInt("leakTimeMinutes", leakTimeMinutes); 537 maxInactiveTimeSecs = properties.getInt("maxInactiveTimeSecs", maxInactiveTimeSecs); 538 trimPoolFreqSecs = properties.getInt("trimPoolFreqSecs", trimPoolFreqSecs); 539 maxAgeMinutes = properties.getInt("maxAgeMinutes", maxAgeMinutes); 540 541 minConnections = properties.getInt("minConnections", minConnections); 542 maxConnections = properties.getInt("maxConnections", maxConnections); 543 pstmtCacheSize = properties.getInt("pstmtCacheSize", pstmtCacheSize); 544 cstmtCacheSize = properties.getInt("cstmtCacheSize", cstmtCacheSize); 545 546 waitTimeoutMillis = properties.getInt("waitTimeout", waitTimeoutMillis); 547 548 heartbeatSql = properties.get("heartbeatSql", heartbeatSql); 549 heartbeatTimeoutSeconds = properties.getInt("heartbeatTimeoutSeconds", heartbeatTimeoutSeconds); 550 poolListener = properties.get("poolListener", poolListener); 551 offline = properties.getBoolean("offline", offline); 552 553 String isoLevel = properties.get("isolationLevel", getTransactionIsolationLevel(isolationLevel)); 554 this.isolationLevel = getTransactionIsolationLevel(isoLevel); 555 556 String customProperties = properties.get("customProperties", null); 557 if (customProperties != null && customProperties.length() > 0) { 558 this.customProperties = parseCustom(customProperties); 559 } 560 } 561 562 Map<String, String> parseCustom(String customProperties) { 563 564 Map<String,String> propertyMap = new LinkedHashMap<String, String>(); 565 String[] pairs = customProperties.split(";"); 566 for (String pair : pairs) { 567 String[] split = pair.split("="); 568 if (split.length == 2) { 569 propertyMap.put(split[0], split[1]); 570 } 571 } 572 return propertyMap; 573 } 574 575 /** 576 * Return the isolation level description from the associated Connection int value. 577 */ 578 private String getTransactionIsolationLevel(int level) { 579 switch (level) { 580 case Connection.TRANSACTION_NONE : return "NONE"; 581 case Connection.TRANSACTION_READ_COMMITTED : return "READ_COMMITTED"; 582 case Connection.TRANSACTION_READ_UNCOMMITTED : return "READ_UNCOMMITTED"; 583 case Connection.TRANSACTION_REPEATABLE_READ : return "REPEATABLE_READ"; 584 case Connection.TRANSACTION_SERIALIZABLE : return "SERIALIZABLE"; 585 default: throw new RuntimeException("Transaction Isolation level [" + level + "] is not known."); 586 } 587 } 588 589 /** 590 * Return the isolation level for a given string description. 591 */ 592 private int getTransactionIsolationLevel(String level) { 593 level = level.toUpperCase(); 594 if (level.startsWith("TRANSACTION")) { 595 level = level.substring("TRANSACTION".length()); 596 } 597 level = level.replace("_", ""); 598 if ("NONE".equalsIgnoreCase(level)) { 599 return Connection.TRANSACTION_NONE; 600 } 601 if ("READCOMMITTED".equalsIgnoreCase(level)) { 602 return Connection.TRANSACTION_READ_COMMITTED; 603 } 604 if ("READUNCOMMITTED".equalsIgnoreCase(level)) { 605 return Connection.TRANSACTION_READ_UNCOMMITTED; 606 } 607 if ("REPEATABLEREAD".equalsIgnoreCase(level)) { 608 return Connection.TRANSACTION_REPEATABLE_READ; 609 } 610 if ("SERIALIZABLE".equalsIgnoreCase(level)) { 611 return Connection.TRANSACTION_SERIALIZABLE; 612 } 613 614 throw new RuntimeException("Transaction Isolation level [" + level + "] is not known."); 615 } 616}