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