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