001/******************************************************************************* 002The MIT License (MIT) 003 004Copyright (c) 2024 KILLCODING.COM 005 006Permission is hereby granted, free of charge, to any person obtaining a copy 007of this software and associated documentation files (the "Software"), to deal 008in the Software without restriction, including without limitation the rights 009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010copies of the Software, and to permit persons to whom the Software is 011furnished to do so, subject to the following conditions: 012 013The above copyright notice and this permission notice shall be included in 014all copies or substantial portions of the Software. 015 016THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 022THE SOFTWARE. 023*****************************************************************************/ 024package com.killcoding.datasource; 025 026import java.io.PrintWriter; 027import java.sql.Connection; 028import java.sql.ConnectionBuilder; 029import java.sql.SQLException; 030import javax.sql.DataSource; 031import java.sql.SQLFeatureNotSupportedException; 032 033import com.killcoding.log.Logger; 034import com.killcoding.log.LoggerFactory; 035 036import java.util.Properties; 037import java.util.concurrent.Executors; 038import java.io.File; 039import java.net.URLClassLoader; 040import java.lang.reflect.Constructor; 041import java.net.URL; 042import java.sql.Driver; 043import java.util.Map; 044import java.util.concurrent.ConcurrentHashMap; 045import java.sql.DriverManager; 046import java.util.Iterator; 047import java.util.Date; 048import java.util.List; 049import java.util.ArrayList; 050import java.util.Collections; 051import java.util.Set; 052import com.killcoding.tool.ConfigProperties; 053import com.killcoding.tool.FileTools; 054import java.io.Reader; 055import java.io.IOException; 056import java.io.StringReader; 057import java.util.concurrent.ExecutorService; 058import com.killcoding.tool.CommonTools; 059import java.text.SimpleDateFormat; 060import java.util.stream.Stream; 061import java.util.Comparator; 062import java.util.concurrent.Callable; 063import java.util.concurrent.Future; 064import java.util.concurrent.Executor; 065import java.util.HashMap; 066import java.nio.file.Files; 067import java.nio.file.Paths; 068import java.util.Enumeration; 069 070/** 071 * Implement and enhance the datasource class 072 * Support write connection status log 073 * Support abort tiemout connection 074 * Support failover 075 * */ 076public final class DriverDataSource implements DataSource { 077 078 private final Logger log = LoggerFactory.getLogger(DriverDataSource.class); 079 080 private String DATASOURCE_ID = null; 081 private final long PID = ProcessHandle.current().pid(); 082 083 private long countCreatedConnection = 0L; 084 private long countAbortedIdle = 0L; 085 private long countAbortedTimeout = 0L; 086 private long countAbortedLifeCycle = 0L; 087 private long countAbortedInvalid = 0L; 088 private long countAbortedCloseFailed = 0L; 089 private long countClosed = 0L; 090 private double maxUsage = 0.0D; 091 092 private final Map<String, Driver> DRIVER_MARK = new ConcurrentHashMap<String, Driver>(); 093 private final Map<Integer, Long> CONN_ERROR_TIME = new ConcurrentHashMap<Integer, Long>(); 094 095 private boolean inited = false; 096 097 private Boolean enableStatusLog = false; 098 private Boolean enableFullStatusLog = true; 099 private Integer statusLogArchiveDays = 30; 100 private String statusLogFolder = null; 101 private File statusLogFile = null; 102 private Integer maxAveUsageSize = 10000; 103 104 private final List<Double> aveUsageSum = new ArrayList<Double>(); 105 private final List<String> urlList = new ArrayList<String>(); 106 107 private int urlIndex = 0; 108 109 private Integer maxPoolSize = Integer.MAX_VALUE; 110 private Integer minPoolSize = 0; 111 private Long lifeCycle = Long.MAX_VALUE; 112 private Long idleTimeout = 60L; 113 private Long timeout = 0L; 114 private Long transactionTimeout = 0L; 115 private Long refreshPoolTimer = 1000L; 116 private Long testValidTimeout = 5L; 117 118 private ConfigProperties configProperties = null; 119 private String driverJar; 120 private String connProps; 121 private String driverClassName; 122 private String url; 123 private String username; 124 private String password; 125 private String name = "DriverDataSource"; 126 private String abortMode = "abort"; 127 private String appDataSourcePath; 128 129 private boolean useExternalConfig = false; 130 private boolean loadedConfig = false; 131 private boolean closed = false; 132 133 private File appDataSource = null; 134 private String owner = null; 135 private List<DriverDataSource> syncDriverDataSource = null; 136 137 private final ConnectionPool connectionPool = new ConnectionPool(); 138 private final ExecutorService connectionPoolExecutor = Executors.newFixedThreadPool(1); 139 140 private long dynamicReloadConfigTime = System.currentTimeMillis(); 141 142 /** 143 * New a DriverDataSource object 144 * Use env APP_DATASOURCE=/path/DataSource.properties 145 * */ 146 public DriverDataSource() { 147 super(); 148 this.appDataSource = CommonTools.getSystemProperties(DriverDataSource.class, "APP_DATASOURCE", 149 "DataSource.properties"); 150 if (this.appDataSource == null) { 151 this.appDataSource = CommonTools.getSystemProperties(DriverDataSource.class, "APP_DATASOURCE", 152 "Datasource.properties"); 153 } 154 155 if (this.appDataSource != null) { 156 this.appDataSourcePath = this.appDataSource.getAbsolutePath(); 157 } 158 159 initDs(); 160 reloadConfig(); 161 } 162 163 /** 164 * New a DriverDataSource object 165 * @param appDataSource - It is file object (e.g. /your_path/DataSource.properties) 166 * */ 167 public DriverDataSource(File appDataSource) { 168 super(); 169 this.appDataSource = appDataSource; 170 171 if (this.appDataSource != null) { 172 if(!this.appDataSource.exists()){ 173 this.appDataSourcePath = this.appDataSource.getAbsolutePath(); 174 Logger.systemError(DriverDataSource.class, "The '{}' does not exists.",this.appDataSourcePath); 175 } 176 177 } 178 179 initDs(); 180 reloadConfig(); 181 } 182 183 /** 184 * New a DriverDataSource object 185 * @param configPropertiesContent - It is properties context 186 * */ 187 public DriverDataSource(String configPropertiesContent) throws IOException { 188 super(); 189 initDs(); 190 setConfig(configPropertiesContent); 191 } 192 193 /** 194 * New a DriverDataSource object 195 * @param configProperties - It is ConfigProperties object 196 * */ 197 public DriverDataSource(ConfigProperties configProperties) throws IOException { 198 super(); 199 initDs(); 200 setConfig(configProperties); 201 } 202 203 private void initDs() { 204 SimpleDateFormat datasourceIdFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS"); 205 DATASOURCE_ID = String.format("%s.%s.%s", datasourceIdFormat.format(new Date()), PID, 206 CommonTools.generateId(4).toUpperCase()); 207 } 208 209 /** 210 * Set app datasource properties file path 211 * @param appDataSourcePath - file path 212 * */ 213 public void setAppDataSourcePath(String appDataSourcePath) { 214 if (!CommonTools.isBlank(appDataSourcePath)) { 215 this.appDataSource = new File(appDataSourcePath); 216 this.appDataSourcePath = this.appDataSource.getAbsolutePath(); 217 } 218 } 219 220 /** 221 * Get app datasource properties file path 222 * */ 223 public String getAppDataSourcePath() { 224 return this.appDataSourcePath; 225 } 226 227 /** 228 * Check is loaded config 229 * @return boolean 230 * */ 231 public boolean isLoadedConfig() { 232 return loadedConfig; 233 } 234 235 /** 236 * Set enable status log 237 * @param enableStatusLog - 'true' is enable write status log 238 * */ 239 public void setEnableStatusLog(Boolean enableStatusLog) { 240 this.enableStatusLog = enableStatusLog == null ? false : enableStatusLog; 241 } 242 243 /** 244 * Set enable full status log 245 * @param enableFullStatusLog - 'true' is enable write status log when full connections 246 * */ 247 public void setEnableFullStatusLog(Boolean enableFullStatusLog) { 248 this.enableFullStatusLog = enableFullStatusLog == null ? true : enableFullStatusLog; 249 } 250 251 /** 252 * Set status log archive days 253 * @param statusLogArchiveDays - Delete before x days log 254 * */ 255 public void setStatusLogArchiveDays(Integer statusLogArchiveDays) { 256 this.statusLogArchiveDays = statusLogArchiveDays == null ? 30 : statusLogArchiveDays; 257 } 258 259 /** 260 * Set status log target folder 261 * @param _statusLogFolder - Target folder 262 * */ 263 public void setStatusLogFolder(String _statusLogFolder) { 264 this.statusLogFolder = CommonTools.isBlank(_statusLogFolder) ? null : _statusLogFolder; 265 if (this.statusLogFolder == null) { 266 statusLogFile = new File(String.format("%s/DriverDataSource_Status/%s.log", 267 CommonTools.getJarPath(DriverDataSource.class), DATASOURCE_ID)); 268 } else { 269 statusLogFile = new File(String.format("%s/%s.log", statusLogFolder, DATASOURCE_ID)); 270 } 271 } 272 273 /** 274 * Set connection life cycle 275 * @param lifeCycle - Unit is ms 276 * */ 277 public void setLifeCycle(Long lifeCycle) { 278 this.lifeCycle = (lifeCycle == null ? Long.MAX_VALUE : lifeCycle); 279 } 280 281 /** 282 * Get connection life cycle 283 * @return Long - Unit is ms 284 * */ 285 public Long getLifeCycle() { 286 return this.lifeCycle; 287 } 288 289 /** 290 * Set refresh pool timer 291 * @param refreshPoolTimer - Unit is ms 292 * */ 293 public void setRefreshPoolTimer(Long refreshPoolTimer) { 294 this.refreshPoolTimer = (refreshPoolTimer == null ? 1000L : refreshPoolTimer); 295 } 296 297 /** 298 * Get refresh pool timer 299 * @return Long - Unit is ms 300 * */ 301 public Long getRefreshPoolTimer() { 302 return this.refreshPoolTimer; 303 } 304 305 /** 306 * Set test valid timeout 307 * @param testValidTimeout Unit is seconds,default is 5s 308 * */ 309 public void setTestValidTimeout(Long testValidTimeout) { 310 this.testValidTimeout = (testValidTimeout == null ? 5L : testValidTimeout); 311 } 312 313 /** 314 * Get test valid timeout 315 * @retrn Long - Unit is seconds 316 * */ 317 public Long getTestValidTimeout() { 318 return this.testValidTimeout; 319 } 320 321 /** 322 * Set idle timeout 323 * @param idleTimeout - Unit is seconds, default is 60s 324 * */ 325 public void setIdleTimeout(Long idleTimeout) { 326 this.idleTimeout = (idleTimeout == null ? 60L : idleTimeout); 327 } 328 329 /** 330 * Get idle timeout 331 * @return Long - Unit is seconds 332 * */ 333 public Long getIdleTimeout() { 334 return this.idleTimeout; 335 } 336 337 /** 338 * Set execute timeout 339 * Default is 0s 340 * Disabled timeou if value is 0s 341 * @param timeout - Unit is seconds, 342 * */ 343 public void setTimeout(Long timeout) { 344 this.timeout = (timeout == null ? 0L : timeout); 345 } 346 347 /** 348 * Get execute timeout 349 * @return Long - Unit is seconds, 350 * */ 351 public Long getTimeout() { 352 return this.timeout; 353 } 354 355 /** 356 * Set execute transaction timeout 357 * Default is 0s 358 * Disabled timeou if value is 0s 359 * @param idleTimeout - Unit is seconds, 360 * */ 361 public void setTransactionTimeout(Long transactionTimeout) { 362 this.transactionTimeout = (transactionTimeout == null ? 0L : transactionTimeout); 363 } 364 365 /** 366 * Get execute transaction timeout 367 * @return Long - Unit is seconds, 368 * */ 369 public Long getTransactionTimeout() { 370 return this.transactionTimeout; 371 } 372 373 /** 374 * Set max connection pool size 375 * @param maxPoolSize 376 * */ 377 public void setMaxPoolSize(Integer maxPoolSize) { 378 this.maxPoolSize = (maxPoolSize == null ? Integer.MAX_VALUE : maxPoolSize); 379 } 380 381 /** 382 * Get max connection pool size 383 * @return Integer 384 * */ 385 public Integer getMaxPoolSize() { 386 return this.maxPoolSize; 387 } 388 389 /** 390 * Set jdbc driver jar path 391 * @param driverJar - Driver jar file or folder path 392 * */ 393 public void setDriverJar(String driverJar) { 394 this.driverJar = driverJar; 395 } 396 397 /** 398 * Get jdbc driver jar path 399 * @return String 400 * */ 401 public String getDriverJar() { 402 return this.driverJar; 403 } 404 405 /** 406 * Set connection properties 407 * @param connProps - Sample: autoReconnection=true;key=value 408 * */ 409 public void setConnProps(String connProps) { 410 this.connProps = connProps; 411 } 412 413 /** 414 * Get connection properties 415 * @return connProps - Sample: autoReconnection=true;key=value 416 * */ 417 public String getConnProps() { 418 return this.connProps; 419 } 420 421 /** 422 * Set driver class name 423 * @param driverClassName 424 * */ 425 public void setDriverClassName(String driverClassName) { 426 this.driverClassName = driverClassName; 427 } 428 429 /** 430 * Get driver class name 431 * @return String 432 * */ 433 public String getDriverClassName() { 434 return this.driverClassName; 435 } 436 437 /** 438 * Set connection url 439 * @param urlOrList - String or List 440 * */ 441 public void setUrl(Object urlOrList) { 442 if (urlOrList instanceof String) { 443 this.urlList.add((String) urlOrList); 444 this.urlIndex = 0; 445 this.url = this.urlList.get(this.urlIndex); 446 } 447 if (urlOrList instanceof List) { 448 this.urlList.addAll((List) urlOrList); 449 this.urlIndex = 0; 450 this.url = this.urlList.get(this.urlIndex); 451 } 452 } 453 454 /** 455 * Get connection url 456 * */ 457 public String getUrl() { 458 return this.url; 459 } 460 461 /** 462 * Set min connection pool size 463 * @param minPoolSize 464 * */ 465 public void setMinPoolSize(Integer minPoolSize) { 466 this.minPoolSize = (minPoolSize == null ? 0 : minPoolSize); 467 } 468 469 /** 470 * Get min connection pool size 471 * */ 472 public Integer getMinPoolSize() { 473 return this.minPoolSize; 474 } 475 476 /** 477 * Set abort mode 478 * @param abortMode - 'close' or 'abort', default is 'abort' 479 * */ 480 public void setAbortMode(String abortMode) { 481 if (abortMode == null) 482 this.abortMode = "abort"; 483 } 484 485 /** 486 * Get abort mode 487 * @return abortMode - 'close' or 'abort', default is 'abort' 488 * */ 489 public String getAbortMode() { 490 return this.abortMode; 491 } 492 493 /** 494 * Set thread name 495 * @param name - It is thread name, default is 'DriverDataSource' 496 * */ 497 public void setName(String name) { 498 if (name != null) 499 this.name = name; 500 } 501 502 /** 503 * Get thread name 504 * @return String 505 * */ 506 public String getName() { 507 return this.name; 508 } 509 510 /** 511 * Set database username 512 * @param username - Database username 513 * */ 514 public void setUsername(String username) { 515 this.username = username; 516 } 517 518 /** 519 * Set database password 520 * @param password - Database password 521 * */ 522 public void setPassword(String password) { 523 this.password = password; 524 } 525 526 private synchronized Integer getOpenConnectionSize() { 527 return connectionPool.getOpenConnectionSize(); 528 } 529 530 531 /** 532 * Get connection pool size 533 * @return Integer 534 * */ 535 public ConnectionPool getConnectionPool() { 536 return connectionPool; 537 } 538 539 /** 540 * Abort timeout connection 541 * Skip execute when 'timeout' is 0 or 'transactionTimeout' is 0 542 * */ 543 private synchronized void abortTimeoutConnection() { 544 try { 545 if (timeout > 0 || transactionTimeout > 0) { 546 Date currentTime = new Date(); 547 List<Integer> abortList = new ArrayList<Integer>(); 548 Set<Integer> hashCodeList = connectionPool.getConnectionOpenedAtMark().keySet(); 549 Map<Integer,Date> usingAtMark = connectionPool.getConnectionUsingAtMark(); 550 for (Integer hashCode : hashCodeList) { 551 Date openedAt = usingAtMark.get(hashCode); 552 if (openedAt != null) { 553 Connection conn = connectionPool.getConnectionMark().get(hashCode); 554 555 if (conn == null) { 556 connectionPool.removeConnectionUsingAtMark(hashCode); 557 continue; 558 } 559 560 long timeoutSec = (currentTime.getTime() - openedAt.getTime()) / 1000; 561 Long _timeout = conn.getAutoCommit() ? timeout : transactionTimeout; 562 563 if (_timeout == 0) 564 continue; 565 566 if (timeoutSec >= _timeout) { 567 if (!abortList.contains(hashCode)) 568 abortList.add(hashCode); 569 } 570 } 571 } 572 for (Integer hashCode : abortList) { 573 countAbortedTimeout++; 574 log.mark("AbortTimeoutConnection '{}', ConnectionPoolSize '{}', OpenConnectionSize '{}'.",hashCode,connectionPool.size(),getOpenConnectionSize()); 575 abort(hashCode); 576 } 577 } 578 } catch (Exception e) { 579 log.warn(e.getMessage(), e); 580 } 581 } 582 583 /** 584 * Abort life cycle connection 585 * Skip execute when 'lifeCycle' is 0 586 * */ 587 private synchronized void abortLifeCycleEndedConnection() { 588 if (lifeCycle > 0) { 589 Date currentTime = new Date(); 590 List<Integer> abortList = new ArrayList<Integer>(); 591 Map<Integer,Date> openedAtMark = connectionPool.getConnectionOpenedAtMark(); 592 Set<Integer> hashCodeList = openedAtMark.keySet(); 593 for (Integer hashCode : hashCodeList) { 594 Date openedAt = openedAtMark.get(hashCode); 595 if (openedAt != null) { 596 long lifeSec = (currentTime.getTime() - openedAt.getTime()) / 1000; 597 if (lifeSec >= lifeCycle && connectionPool.contains(hashCode)) { 598 if (!abortList.contains(hashCode)) 599 abortList.add(hashCode); 600 } 601 } 602 } 603 for (Integer hashCode : abortList) { 604 if (connectionPool.contains(hashCode)) { 605 countAbortedLifeCycle++; 606 log.mark("AbortLifeCycleEndedConnection '{}', ConnectionPoolSize '{}', OpenConnectionSize '{}'.",hashCode,connectionPool.size(),getOpenConnectionSize()); 607 abort(hashCode); 608 } 609 } 610 } 611 } 612 613 /** 614 * Abort idle timeout connection 615 * Skip execute when 'idleTimeout' is 0 616 * */ 617 private synchronized void abortIdleTimeoutConnection() { 618 if (idleTimeout > 0) { 619 Date currentTime = new Date(); 620 List<Integer> abortList = new ArrayList<Integer>(); 621 Map<Integer,Date> idleBeginMark = connectionPool.getConnectionIdleBeginMark(); 622 Set<Integer> hashCodeList = idleBeginMark.keySet(); 623 for (Integer hashCode : hashCodeList) { 624 Date openedAt = idleBeginMark.get(hashCode); 625 if (openedAt != null) { 626 long idleTime = (currentTime.getTime() - openedAt.getTime()) / 1000; 627 if (idleTime >= idleTimeout && connectionPool.contains(hashCode)) { 628 if (!abortList.contains(hashCode)) 629 abortList.add(hashCode); 630 } 631 } 632 } 633 for (Integer hashCode : abortList) { 634 if (connectionPool.contains(hashCode)) { 635 int loadPoolSize = connectionPool.size(); 636 boolean overMinPoolSize = loadPoolSize > minPoolSize; 637 log.debug("Check abort idle connection, overMinPoolSize[%s] = loadPoolSize[%s] > minPoolSize[%s].",overMinPoolSize, loadPoolSize,minPoolSize); 638 if (overMinPoolSize && connectionPool.contains(hashCode)) { 639 countAbortedIdle++; 640 log.mark("AbortIdleTimeoutConnection '{}', ConnectionPoolSize '{}', OpenConnectionSize '{}'.",hashCode,connectionPool.size(),getOpenConnectionSize()); 641 abort(hashCode); 642 } 643 } 644 } 645 } 646 } 647 648 /** 649 * Abort invalid connection 650 * Skip execute when 'testValidTimeout' is 0 651 * */ 652 private synchronized void abortInvalidConnection() { 653 if (testValidTimeout > 0) { 654 try { 655 List<Integer> abortList = new ArrayList<Integer>(); 656 Map<Integer,Date> idleBeginMark = connectionPool.getConnectionIdleBeginMark(); 657 Set<Integer> hashCodeList = idleBeginMark.keySet(); 658 for (Integer hashCode : hashCodeList) { 659 Connection conn = connectionPool.getConnectionMark().get(hashCode); 660 if (conn != null) { 661 boolean valid = conn.isValid(testValidTimeout.intValue()); 662 if (!valid && !abortList.contains(hashCode)) 663 abortList.add(hashCode); 664 } 665 } 666 for (Integer hashCode : abortList) { 667 if (connectionPool.contains(hashCode)) { 668 countAbortedInvalid++; 669 log.mark("AbortInvalidConnection '{}', ConnectionPoolSize '{}', OpenConnectionSize '{}'.",hashCode,connectionPool.size(),getOpenConnectionSize()); 670 abort(hashCode); 671 } 672 } 673 } catch (Exception e) { 674 log.warn(e.getMessage(), e); 675 676 } 677 } 678 } 679 680 /** 681 * Create connection to pool 682 * When, 683 * (loadPoolSize + usedPoolSize) < maxPoolSize 684 * and 685 * loadPoolSize < minPoolSize 686 * */ 687 private synchronized void createConnectionToPool() { 688 final String threadName = String.format("%s-createConnectionToPool", this.name); 689 690 boolean lessThanMaxPoolSize = getOpenConnectionSize() < maxPoolSize; 691 boolean lessThanMinPoolSize = connectionPool.size() < minPoolSize; 692 boolean lessThanMaxUsage = getUsage() < 1.0D; 693 694 if (log.isDebugEnabled()) { 695 log.debug("boolean lessThanMaxPoolSize[{}] = getOpenConnectionSize()[{}] < maxPoolSize[{}];", 696 lessThanMaxPoolSize, getOpenConnectionSize(), maxPoolSize); 697 log.debug("boolean lessThanMinPoolSize[{}] = connectionPool.size()[{}] < minPoolSize[{}];", 698 lessThanMinPoolSize, connectionPool.size(), minPoolSize); 699 log.debug("boolean lessThanMaxUsage[{}] = getUsage()[{}] < 1.0D;", lessThanMaxUsage, getUsage()); 700 log.debug("Checking create conditions,LessThanMaxPoolSize={}, LessThanMinPoolSize={}, lessThanMaxUsage={}", 701 lessThanMaxPoolSize, lessThanMinPoolSize, lessThanMaxUsage); 702 703 } 704 if (lessThanMaxPoolSize && lessThanMinPoolSize && lessThanMaxUsage) { 705 int willCreateConnTotal = minPoolSize - connectionPool.size(); 706 if (willCreateConnTotal > 0) { 707 log.debug("Will create connection total = {}", willCreateConnTotal); 708 } 709 for (int i = 0; i < willCreateConnTotal; i++) { 710 Callable callable = new Callable<Boolean>() { 711 @Override 712 public Boolean call() { 713 try { 714 Thread.currentThread().setName("call-" + threadName); 715 if (closed || Thread.currentThread().isInterrupted()){ 716 return false; 717 } 718 719 boolean lessThanMaxPoolSize = getOpenConnectionSize() < maxPoolSize; 720 boolean lessThanMinPoolSize = connectionPool.size() < minPoolSize; 721 boolean lessThanMaxUsage = getUsage() < 1.0D; 722 723 if (lessThanMaxPoolSize && lessThanMinPoolSize && lessThanMaxUsage) { 724 Thread.currentThread().setName(threadName); 725 loadConnectionToPool(); 726 log.debug("Loaded connection to pool"); 727 } 728 } catch (Exception e) { 729 log.warn(e.getMessage(), e); 730 } 731 return true; 732 } 733 }; 734 connectionPoolExecutor.submit(callable); 735 } 736 log.debug("Creating {} connection to pool.", willCreateConnTotal); 737 } 738 } 739 740 /** 741 * Set internal config 742 * @param _configProperties 743 * */ 744 private synchronized void setInternalConfig(ConfigProperties _configProperties) { 745 this.configProperties = _configProperties; 746 if (this.configProperties.containsKey("EnableStatusLog")) 747 setEnableStatusLog(this.configProperties.getBoolean("EnableStatusLog", false)); 748 749 if (this.configProperties.containsKey("EnableFullStatusLog")) 750 setEnableFullStatusLog(this.configProperties.getBoolean("EnableFullStatusLog", true)); 751 752 if (this.configProperties.containsKey("StatusLogArchiveDays")) 753 setStatusLogArchiveDays(this.configProperties.getInteger("StatusLogArchiveDays", 30)); 754 755 if (this.configProperties.containsKey("StatusLogFolder")) 756 setStatusLogFolder(this.configProperties.getString("StatusLogFolder", null)); 757 758 if (this.configProperties.containsKey("MaxPoolSize")) 759 setMaxPoolSize(this.configProperties.getInteger("MaxPoolSize", Integer.MAX_VALUE)); 760 761 if (this.configProperties.containsKey("MinPoolSize")) 762 setMinPoolSize(this.configProperties.getInteger("MinPoolSize", 0)); 763 764 if (this.configProperties.containsKey("LifeCycle")) 765 setLifeCycle(this.configProperties.getSeconds("LifeCycle", Long.MAX_VALUE)); 766 767 if (this.configProperties.containsKey("IdleTimeout")) 768 setIdleTimeout(this.configProperties.getSeconds("IdleTimeout", 0L)); 769 770 if (this.configProperties.containsKey("Timeout")) 771 setTimeout(this.configProperties.getSeconds("Timeout", 0L)); 772 773 if (this.configProperties.containsKey("TransactionTimeout")) 774 setTransactionTimeout(this.configProperties.getSeconds("TransactionTimeout", 0L)); 775 776 if (this.configProperties.containsKey("TestValidTimeout")) 777 setTestValidTimeout(this.configProperties.getSeconds("TestValidTimeout", 5L)); 778 779 if (this.configProperties.containsKey("RefreshPoolTimer")) 780 setRefreshPoolTimer(this.configProperties.getMilliSeconds("RefreshPoolTimer", 1000L)); 781 782 if (this.configProperties.containsKey("DriverJar")) 783 setDriverJar(this.configProperties.getString("DriverJar", null)); 784 785 if (this.configProperties.containsKey("ConnProps")) 786 setConnProps(this.configProperties.getString("ConnProps", null)); 787 788 if (this.configProperties.containsKey("ThreadName")) 789 setName(this.configProperties.getString("ThreadName", null)); 790 791 if (this.configProperties.containsKey("Driver")) 792 setDriverClassName(this.configProperties.getString("Driver", null)); 793 794 if (this.configProperties.containsKey("Url")) 795 setUrl(this.configProperties.getString("Url", null)); 796 797 if (this.configProperties.containsKey("Url[0]")) 798 setUrl(this.configProperties.getArray("Url")); 799 800 if (this.configProperties.containsKey("Username")) 801 setUsername(this.configProperties.getString("Username", null)); 802 803 if (this.configProperties.containsKey("Password")) 804 setPassword(this.configProperties.getString("Password", null)); 805 806 if (this.configProperties.containsKey("AbortMode")) 807 setAbortMode(this.configProperties.getString("AbortMode", "abort")); 808 809 if (this.configProperties.containsKey("MaxAveUsageSize")) 810 setMaxAveUsageSize(this.configProperties.getInteger("MaxAveUsageSize", 10000)); 811 812 loadedConfig = true; 813 } 814 815 /** 816 * Set internal config 817 * @param configPropertiesContent 818 * */ 819 private synchronized void setInternalConfig(String configPropertiesContent) throws IOException { 820 log.debug("setInternalConfig -> {}", configPropertiesContent); 821 ConfigProperties _configProperties = new ConfigProperties(); 822 _configProperties.load(new StringReader(configPropertiesContent)); 823 setInternalConfig(_configProperties); 824 } 825 826 /** 827 * Set config 828 * @param configPropertiesContent 829 * */ 830 public synchronized void setConfig(String configPropertiesContent) throws IOException { 831 setInternalConfig(configPropertiesContent); 832 } 833 834 /** 835 * Set config 836 * @param _configProperties 837 * */ 838 public synchronized void setConfig(ConfigProperties _configProperties) throws IOException { 839 setInternalConfig(_configProperties); 840 } 841 842 /** 843 * Reload config 844 * When 'appDataSource' is file object 845 * */ 846 private synchronized void reloadConfig() { 847 try{ 848 DriverDataSource the = this; 849 if (this.appDataSource != null) { 850 configProperties = new ConfigProperties(); 851 Runnable updateRun = new Runnable(){ 852 @Override 853 public void run(){ 854 try{ 855 the.setInternalConfig(configProperties); 856 Logger.systemMark(DriverDataSource.class, "DriverDataSource '{}' Reloaded", name); 857 }catch(Exception e){ 858 log.error(e.getMessage(),e); 859 } 860 } 861 }; 862 configProperties.load(this.appDataSource,updateRun); 863 updateRun.run(); 864 } 865 }catch(Exception e){ 866 log.error(e.getMessage(),e); 867 } 868 } 869 870 /** 871 * Archive log 872 * */ 873 private void archiveLog() { 874 if (statusLogFile != null && statusLogArchiveDays != null) { 875 File statusLogFileParent = statusLogFile.getParentFile(); 876 if (statusLogArchiveDays > 0 && statusLogFileParent != null) { 877 try { 878 long fileOutArchiveDaysMs = new Date().getTime() - (statusLogArchiveDays * 24 * 3600000L); 879 deleteFilesOlderThan(statusLogFileParent, fileOutArchiveDaysMs); 880 } catch (Exception e) { 881 log.warn(e); 882 883 } 884 } 885 } 886 } 887 888 /** 889 * Delete old archive log 890 * */ 891 private void deleteFilesOlderThan(File directory, long fileOutArchiveDaysMs) throws IOException { 892 if (directory.isDirectory()) { 893 File[] files = directory.listFiles(); 894 if (files != null) { 895 for (File file : files) { 896 if (file.isFile()) { 897 boolean isLogFile = file.getName().toLowerCase().endsWith(".log"); 898 if (isLogFile) { 899 boolean canWrite = file.canWrite(); 900 if (canWrite) { 901 long lastModified = file.lastModified(); 902 if (lastModified < fileOutArchiveDaysMs) { 903 Files.deleteIfExists(Paths.get(file.toURI())); 904 } 905 } 906 } 907 } 908 } 909 } 910 } 911 } 912 913 /** 914 * Write connection status log 915 * */ 916 private synchronized void statusLogOutput() { 917 File folder = statusLogFile.getParentFile(); 918 if (folder.exists()) { 919 if (!folder.canWrite()) 920 return; 921 } else { 922 folder.mkdirs(); 923 } 924 if (statusLogFile.exists()) { 925 if (!statusLogFile.canWrite()) 926 return; 927 } 928 929 SimpleDateFormat timeFormat = new SimpleDateFormat("yyyyMMdd HH:mm:ss.SSS"); 930 Map<Integer, String> CONNECTION_THREAD_MARK_SYNC = connectionPool.getConnectionThreadMark(); 931 Map<Integer, Date> CONNECTION_OPENED_AT_MARK_SYNC = connectionPool.getConnectionOpenedAtMark(); 932 Map<Integer, Date> CONNECTION_USING_AT_MARK_SYNC = connectionPool.getConnectionUsingAtMark(); 933 Map<Integer, Date> CONNECTION_IDLE_BEGIN_MARK_SYNC = connectionPool.getConnectionIdleBeginMark(); 934 935 int usingSize = CONNECTION_USING_AT_MARK_SYNC.keySet().size(); 936 int idleSize = CONNECTION_IDLE_BEGIN_MARK_SYNC.keySet().size(); 937 938 boolean isFull = (usingSize > idleSize && idleSize == 0 && usingSize >= maxPoolSize); 939 940 String driverInfo = String.format("Driver: %s%s", this.driverClassName, System.lineSeparator()); 941 StringBuffer countInfoSb = new StringBuffer(); 942 countInfoSb.append( 943 String.format("MinPoolSize: %s\tMaxPoolSize: %s%s", minPoolSize, maxPoolSize, System.lineSeparator())); 944 countInfoSb.append(String.format("RefreshPoolTimer: %sms%s", refreshPoolTimer, System.lineSeparator())); 945 countInfoSb.append(String.format("TestValidTimeout: %ss\tIdleTimeout: %ss%s", testValidTimeout, idleTimeout, 946 System.lineSeparator())); 947 countInfoSb.append(String.format("Timeout: %ss\tTransactionTimeout: %ss%s", timeout, transactionTimeout, 948 System.lineSeparator())); 949 countInfoSb.append( 950 String.format("LifeCycle: %ss\tCountClosed: %s%s", lifeCycle, countClosed, System.lineSeparator())); 951 countInfoSb.append(String.format("CountCreatedConnection: %s\tCountAbortedCloseFailed: %s%s", 952 countCreatedConnection, countAbortedCloseFailed, System.lineSeparator())); 953 countInfoSb.append(String.format("CountAbortedIdleTimeouted: %s\tCountAbortedTimeouted: %s%s", countAbortedIdle, 954 countAbortedTimeout, System.lineSeparator())); 955 countInfoSb.append(String.format("CountAbortedLifeCycleEnded: %s\tCountAbortedInvalid: %s%s", 956 countAbortedLifeCycle, countAbortedInvalid, System.lineSeparator())); 957 countInfoSb.append(String.format("MaxUsage: %.2f%%\tCurrentUsage: %.2f%%\tAveUsage: %.2f%%\tCounter: %s/%s%s", maxUsage * 100, 958 getUsage() * 100,getAveUsage() * 100, aveUsageSum.size(),maxAveUsageSize,System.lineSeparator())); 959 960 String urlInfo = String.format("URL: %s%s", this.url, System.lineSeparator()); 961 String usernameInfo = String.format("User: %s%s", this.username, System.lineSeparator()); 962 String header = String.format("%sLine\tHashCode\tCreatedTime\tChangedTime\tStatus\tLastUsingBy%s", 963 System.lineSeparator(), System.lineSeparator()); 964 String lineFormat = "%s\t%s\t%s\t%s\t%s\t%s%s"; 965 StringBuffer sbf = new StringBuffer(); 966 sbf.append(String.format("ThreadName: %s%s", this.name, System.lineSeparator())); 967 sbf.append(String.format("Host: %s\tPID: %s\tSystemTime: %s%s", CommonTools.getHostname(), PID, 968 timeFormat.format(new Date()), System.lineSeparator())); 969 sbf.append(urlInfo); 970 sbf.append(usernameInfo); 971 sbf.append(driverInfo); 972 sbf.append(countInfoSb.toString()); 973 sbf.append(header); 974 int lineNumber = 1; 975 976 Set<Integer> keys = CONNECTION_OPENED_AT_MARK_SYNC.keySet(); 977 List<Object[]> list = new ArrayList<Object[]>(); 978 for (Integer hashCode : keys) { 979 String status = "-"; 980 String thread = "-"; 981 Date changedTime = null; 982 Date createdTime = CONNECTION_OPENED_AT_MARK_SYNC.get(hashCode); 983 984 if (CONNECTION_THREAD_MARK_SYNC.containsKey(hashCode)) { 985 thread = CONNECTION_THREAD_MARK_SYNC.get(hashCode); 986 } 987 988 if (CONNECTION_USING_AT_MARK_SYNC.containsKey(hashCode)) { 989 changedTime = CONNECTION_USING_AT_MARK_SYNC.get(hashCode); 990 status = "Using"; 991 } else if (CONNECTION_IDLE_BEGIN_MARK_SYNC.containsKey(hashCode)) { 992 changedTime = CONNECTION_IDLE_BEGIN_MARK_SYNC.get(hashCode); 993 status = "Idle"; 994 } 995 996 Object[] objArr = new Object[] { hashCode, createdTime, changedTime, status, thread }; 997 list.add(objArr); 998 } 999 1000 Comparator comp = new Comparator() { 1001 @Override 1002 public int compare(Object arg0, Object arg1) { 1003 Object[] objArr0 = (Object[]) arg0; 1004 Object[] objArr1 = (Object[]) arg1; 1005 Date date0 = (Date) objArr0[1]; 1006 Date date1 = (Date) objArr1[1]; 1007 return date0.getTime() < date1.getTime() ? -1 : 1; 1008 } 1009 }; 1010 1011 Collections.sort(list, comp); 1012 1013 for (Object[] objArr : list) { 1014 Integer hashCode = (Integer) objArr[0]; 1015 Date createdTime = (Date) objArr[1]; 1016 String createdTimeStr = createdTime == null ? "-" : timeFormat.format(createdTime); 1017 Date changedTime = (Date) objArr[2]; 1018 String changedTimeStr = changedTime == null ? "-" : timeFormat.format(changedTime); 1019 String status = objArr[3].toString(); 1020 String thread = objArr[4].toString(); 1021 String line = String.format(lineFormat, lineNumber, hashCode, createdTimeStr, changedTimeStr, status, 1022 thread, System.lineSeparator()); 1023 sbf.append(line); 1024 lineNumber++; 1025 } 1026 1027 try { 1028 String msg = sbf.toString(); 1029 FileTools.write(statusLogFile, msg, false); 1030 1031 if (isFull && enableFullStatusLog) { 1032 SimpleDateFormat timeFullFormat = new SimpleDateFormat("yyyyMMddHHmm"); 1033 File fullStatusLogFile = new File(String.format("%s_full_%s", statusLogFile.getAbsolutePath(), 1034 timeFullFormat.format(new Date()))); 1035 FileTools.write(fullStatusLogFile, msg, false); 1036 1037 } 1038 } catch (IOException e) { 1039 log.warn(e.getMessage(), e); 1040 1041 } 1042 } 1043 1044 /** 1045 * Manage connection pool 1046 * */ 1047 private synchronized void manageConnectionPool() { 1048 if (!inited) { 1049 inited = true; 1050 1051 String threadName = this.name; 1052 ExecutorService executor = Executors.newFixedThreadPool(1); 1053 executor.execute(new Runnable() { 1054 1055 @Override 1056 public void run() { 1057 Thread.currentThread().setName(String.format("%s-manageConnectionPool-1", threadName)); 1058 while (true) { 1059 if (closed || Thread.currentThread().isInterrupted()) 1060 break; 1061 1062 try { 1063 abortLifeCycleEndedConnection(); 1064 abortTimeoutConnection(); 1065 abortIdleTimeoutConnection(); 1066 createConnectionToPool(); 1067 countUsage(true); 1068 if (enableStatusLog && statusLogFile != null) { 1069 statusLogOutput(); 1070 archiveLog(); 1071 } 1072 } catch (Exception e) { 1073 log.warn(e.getMessage(), e); 1074 } 1075 1076 try { 1077 Thread.sleep(refreshPoolTimer); 1078 } catch (InterruptedException e) { 1079 log.warn(e.getMessage(), e); 1080 } 1081 } 1082 } 1083 1084 }); 1085 1086 executor = Executors.newFixedThreadPool(1); 1087 executor.execute(new Runnable() { 1088 1089 @Override 1090 public void run() { 1091 Thread.currentThread().setName(String.format("%s-manageConnectionPool-2", threadName)); 1092 while (true) { 1093 if (closed || Thread.currentThread().isInterrupted()) 1094 break; 1095 1096 try { 1097 abortInvalidConnection(); 1098 } catch (Exception e) { 1099 log.warn(e.getMessage(), e); 1100 1101 } 1102 1103 try { 1104 Thread.sleep(refreshPoolTimer); 1105 } catch (InterruptedException e) { 1106 log.warn(e.getMessage(), e); 1107 } 1108 } 1109 } 1110 1111 }); 1112 } 1113 } 1114 1115 /** 1116 * Get connection 1117 * @exception SQLException 1118 * @return Connection 1119 * */ 1120 @Override 1121 public synchronized Connection getConnection() throws SQLException { 1122 try{ 1123 manageConnectionPool(); 1124 Date currentTime = new Date(); 1125 Connection conn = null; 1126 boolean allowedGetSingle = true; 1127 1128 if(connectionPool.size() == 0){ 1129 loadConnectionToPool(); 1130 } 1131 1132 conn = connectionPool.getConnection(); 1133 if (conn == null) { 1134 throwConnIsNull(); 1135 } 1136 countUsage(false); 1137 1138 if (conn == null) { 1139 throwConnIsNull(); 1140 } 1141 log.debug("Get Connection -> {}", conn.hashCode()); 1142 return new DriverConnection(this, conn); 1143 }catch(Exception e){ 1144 log.error(e.getMessage(),e); 1145 throwConnIsNull(); 1146 } 1147 return null; 1148 } 1149 1150 private void throwConnIsNull() throws SQLException { 1151 int errorCode = 99904; 1152 String errorMsg = "Connection is null."; 1153 log.warn(String.format("ERROR CODE: (%s) %s", errorCode, errorMsg)); 1154 throw new SQLException(errorMsg) { 1155 @Override 1156 public int getErrorCode() { 1157 return errorCode; 1158 } 1159 }; 1160 } 1161 1162 private synchronized void checkConnectionPoolSize() throws SQLException { 1163 int openConnectionSize = getOpenConnectionSize(); 1164 if (openConnectionSize >= maxPoolSize) { 1165 int errorCode = 99903; 1166 String errorMsg = String.format("Full connection pool,current size is %s.", openConnectionSize); 1167 log.warn(String.format("ERROR CODE: (%s) %s", errorCode, errorMsg)); 1168 throw new SQLException(errorMsg) { 1169 @Override 1170 public int getErrorCode() { 1171 return errorCode; 1172 } 1173 }; 1174 1175 } 1176 } 1177 1178 /** 1179 * Get connection properties 1180 * @return Properties 1181 * */ 1182 private Properties getConnectionProperties() { 1183 Properties props = new Properties(); 1184 props.setProperty("user", username); 1185 props.setProperty("password", password); 1186 1187 if (!CommonTools.isBlank(connProps)) { 1188 try { 1189 String[] ps = connProps.split(";"); 1190 int psSize = ps.length; 1191 for (int i = 0; i < psSize; i++) { 1192 String[] propKeyValue = ps[i].split("="); 1193 String pkey = propKeyValue[0].trim(); 1194 String pval = propKeyValue[1].trim(); 1195 props.setProperty(pkey, pval); 1196 } 1197 } catch (Exception e) { 1198 log.warn(e.getMessage(), e); 1199 1200 } 1201 } 1202 return props; 1203 } 1204 1205 /** 1206 * Get connection from driver 1207 * @exception SQLException 1208 * @return Connection 1209 * */ 1210 private synchronized Connection getConnectionFromDriver() throws SQLException { 1211 Properties props = getConnectionProperties(); 1212 Connection conn = null; 1213 if (driverJar == null) { 1214 conn = tryGetConnection(null, props); 1215 } else { 1216 Driver driver = loadJdbcDriver(driverJar, driverClassName); 1217 log.debug("Success init drive -> {}", driverClassName); 1218 conn = tryGetConnection(driver, props); 1219 } 1220 1221 if (conn == null) { 1222 throwConnIsNull(); 1223 } 1224 return conn; 1225 } 1226 1227 /** 1228 * Load connection to pool 1229 * @exception SQLException 1230 * */ 1231 private synchronized void loadConnectionToPool() throws SQLException { 1232 checkConnectionPoolSize(); 1233 log.debug("Opening(Pool)"); 1234 final Connection conn = getConnectionFromDriver(); 1235 final Integer hashCode = conn.hashCode(); 1236 log.debug("Created Connection -> {}", hashCode); 1237 connectionPool.add(conn); 1238 } 1239 1240 /** 1241 * Close connection 1242 * @exception SQLException 1243 * @param conn - Connection object 1244 * */ 1245 protected void closeConnection(Connection conn) throws SQLException { 1246 if (conn != null) 1247 closeConnection(conn.hashCode()); 1248 } 1249 1250 /** 1251 * Close connection 1252 * @exception SQLException 1253 * @param hashCode - Connection hash code 1254 * */ 1255 protected void closeConnection(Integer hashCode) throws SQLException { 1256 Connection conn = connectionPool.getConnectionMark().get(hashCode); 1257 if (conn != null) { 1258 try { 1259 synchronized (this) { 1260 connectionPool.removeConnectionUsingAtMark(hashCode); 1261 } 1262 if (lifeCycle <= 0) { 1263 conn.close(); 1264 connectionPool.removeAll(hashCode); 1265 countClosed++; 1266 log.debug("Closed Connection -> {}", hashCode); 1267 } else { 1268 if(closed){ 1269 conn.close(); 1270 connectionPool.removeAll(hashCode); 1271 countClosed++; 1272 log.debug("Closed Connection -> {}", hashCode); 1273 }else{ 1274 conn.setAutoCommit(true); 1275 connectionPool.freed(hashCode); 1276 log.debug("Freed Connection -> {}", hashCode); 1277 } 1278 } 1279 } catch (SQLException e) { 1280 addCountAbortedCloseFailed(+1); 1281 abort(hashCode); 1282 1283 throw e; 1284 } 1285 } 1286 } 1287 1288 /** 1289 * Add count abort close failed for write status log 1290 * @param value - Change value 1291 * */ 1292 private synchronized void addCountAbortedCloseFailed(int value) { 1293 countAbortedCloseFailed += value; 1294 } 1295 1296 /** 1297 * Close all connections 1298 * */ 1299 public synchronized void close() { 1300 closed = true; 1301 log.mark("Try Abort(On SystemExit,CLOSE-{}) connections.",name); 1302 Map<Integer,Connection> connectionMark = connectionPool.getConnectionMark(); 1303 Iterator<Integer> itr = connectionMark.keySet().iterator(); 1304 while (itr.hasNext()) { 1305 Integer key = itr.next(); 1306 Connection conn = connectionMark.get(key); 1307 if (conn != null) { 1308 log.mark("Abort(On SystemExit) connection '{}.{}'.",name,conn.hashCode()); 1309 abort(conn.hashCode()); 1310 } 1311 } 1312 List<Integer> connList = connectionPool.list(); 1313 for(Integer hashCode : connList){ 1314 log.mark("Abort(On SystemExit) connection '{}.{}'.",name,hashCode); 1315 abort(hashCode); 1316 } 1317 log.mark("Aborted all connections '{}'.",name); 1318 } 1319 1320 /** 1321 * Loop close all connections 1322 * */ 1323 public synchronized void closeAll() { 1324 closed = true; 1325 log.mark("Try Abort(On SystemExit,CLOSE_ALL-{}) connections.",name); 1326 List<Integer> connList = connectionPool.list(); 1327 for(Integer hashCode : connList){ 1328 log.mark("Abort(On SystemExit) connection '{}.{}'.",name,hashCode); 1329 abort(hashCode); 1330 } 1331 double currentUsage = getUsage(); 1332 log.mark("Checking usage for close all , the '{}' usage is '{}'.",name,currentUsage); 1333 if(currentUsage > 0){ 1334 try{ 1335 Thread.sleep(1000L); 1336 }catch(InterruptedException e){ 1337 log.error(e.getMessage(),e); 1338 } 1339 closeAll(); 1340 } 1341 log.mark("Aborted all connections '{}'.",name); 1342 } 1343 1344 /** 1345 * Deregister JDBC Driver 1346 * For system exit 1347 * */ 1348 public synchronized static void deregisterDriver(){ 1349 LoggerFactory.getLogger(DriverDataSource.class).mark("DeregisterDriver processing."); 1350 Enumeration<Driver> drivers = DriverManager.getDrivers(); 1351 if(drivers != null){ 1352 while (drivers.hasMoreElements()) { 1353 Driver driver = drivers.nextElement(); 1354 try{ 1355 LoggerFactory.getLogger(DriverDataSource.class).mark("Driver: '{}'",driver.hashCode()); 1356 DriverManager.deregisterDriver(driver); 1357 }catch(Exception e){ 1358 LoggerFactory.getLogger(DriverDataSource.class).error(e.getMessage(),e); 1359 } 1360 } 1361 } 1362 LoggerFactory.getLogger(DriverDataSource.class).mark("DeregisterDriver processed."); 1363 } 1364 1365 /** 1366 * Try get connection 1367 * Support multi url 1368 * @param driver - Driver 1369 * @param props - Connection properties ,var name is 'connProps' 1370 * */ 1371 private synchronized Connection tryGetConnection(Driver driver, Properties props) { 1372 Connection conn = null; 1373 int urlLastIndex = urlList.size() - 1; 1374 1375 do { 1376 if (closed) 1377 break; 1378 1379 if (connectionPool.size() >= maxPoolSize) 1380 break; 1381 1382 if (!CONN_ERROR_TIME.containsKey(urlIndex)) { 1383 CONN_ERROR_TIME.put(urlIndex, 0L); 1384 } 1385 1386 Long connErrorTime = CONN_ERROR_TIME.get(urlIndex); 1387 if (connErrorTime > 0) { 1388 ConfigProperties cp = getConfigProperties(); 1389 long connCheckMs = 10000L; 1390 if (cp != null) { 1391 connCheckMs = cp.getLong("TryGetConnectionCheckTime", 10000L); 1392 } 1393 boolean isSkipConn = connErrorTime > 0 && (System.currentTimeMillis() - connErrorTime) <= connCheckMs; 1394 if (isSkipConn) { 1395 try { 1396 Thread.sleep(100L); 1397 } catch (InterruptedException e) { 1398 log.debug(e.getMessage(), e); 1399 } 1400 continue; 1401 } 1402 } 1403 1404 url = urlList.get(urlIndex); 1405 try { 1406 if (driver == null) { 1407 Class.forName(driverClassName); 1408 conn = DriverManager.getConnection(url, props); 1409 } else { 1410 conn = driver.connect(url, props); 1411 } 1412 conn.setAutoCommit(true); 1413 1414 countCreatedConnection++; 1415 1416 CONN_ERROR_TIME.put(urlIndex, 0L); 1417 1418 break; 1419 } catch (Exception e) { 1420 1421 CONN_ERROR_TIME.put(urlIndex, System.currentTimeMillis()); 1422 1423 urlIndex += 1; 1424 1425 Logger.systemError(DriverDataSource.class,"Failed connect to '{}'.", url); 1426 Logger.systemError(DriverDataSource.class,e.getMessage(), e); 1427 1428 } 1429 } while (urlIndex <= urlLastIndex); 1430 1431 if (conn == null) { 1432 urlIndex = 0; 1433 url = urlList.get(urlIndex); 1434 } 1435 1436 return conn; 1437 } 1438 1439 /** 1440 * Abort connection by hash code 1441 * @param hashCode - Connection hash code 1442 * */ 1443 protected void abort(Integer hashCode) { 1444 log.debug("Abort Connection {}", hashCode); 1445 Connection conn = connectionPool.getConnectionMark().get(hashCode); 1446 if (conn != null) { 1447 try { 1448 callAbortConn(conn); 1449 log.debug("Aborted Connection -> {}", hashCode); 1450 connectionPool.removeAll(hashCode); 1451 log.debug("Abort(If Aborted,{}), UsedPoolSize={}.", hashCode, getOpenConnectionSize()); 1452 } catch (Exception e) { 1453 log.warn(e.getMessage(), e); 1454 1455 } 1456 } 1457 } 1458 1459 /** 1460 * Call abort connection 1461 * Please see 'abortMode' options (rollback,close,abort) 1462 * Default is 'abort' 1463 * @param conn - Connection 1464 * */ 1465 private void callAbortConn(Connection conn) { 1466 try { 1467 if (abortMode.equalsIgnoreCase("rollback")) { 1468 if (!conn.getAutoCommit()) 1469 conn.rollback(); 1470 1471 conn.close(); 1472 } else if (abortMode.equalsIgnoreCase("close")) { 1473 conn.close(); 1474 } else { 1475 conn.abort(Executors.newFixedThreadPool(1)); 1476 } 1477 } catch (SQLException e) { 1478 log.warn("EROR CODE: {}", e.getErrorCode()); 1479 log.warn(e.getMessage(), e); 1480 1481 } catch (Exception e) { 1482 log.warn(e.getMessage(), e); 1483 1484 } 1485 } 1486 1487 /** 1488 * Load jdbc driver jar 1489 * @param driverJar - It is jar file path or jar dir path 1490 * @param driver 1491 * @exception SQLException 1492 * @return Driver 1493 * */ 1494 private synchronized Driver loadJdbcDriver(String driverJar, String driver) throws SQLException { 1495 try { 1496 1497 if (driverJar == null) { 1498 int errorCode = 99901; 1499 String errorMsg = "Driver jar is null."; 1500 log.warn(String.format("ERROR CODE: (%s) %s", errorCode, errorMsg)); 1501 throw new SQLException(errorMsg) { 1502 @Override 1503 public int getErrorCode() { 1504 return errorCode; 1505 } 1506 }; 1507 } 1508 1509 Driver driverNewInstance = DRIVER_MARK.get(driverJar); 1510 1511 if (driverNewInstance == null) { 1512 URLClassLoader driverClassLoader = CommonTools.loadJars(driverJar); 1513 Class classToLoad = Class.forName(driver, true, driverClassLoader); 1514 Constructor driverConstructor = classToLoad.getConstructor(); 1515 driverConstructor.setAccessible(true); 1516 driverNewInstance = (Driver) driverConstructor.newInstance(); 1517 1518 DRIVER_MARK.put(driverJar, driverNewInstance); 1519 } 1520 1521 return driverNewInstance; 1522 } catch (Exception e) { 1523 int errorCode = 99902; 1524 String errorMsg = e.getMessage(); 1525 log.warn(String.format("ERROR CODE: (%s) %s", errorCode, errorMsg)); 1526 throw new SQLException(e) { 1527 1528 @Override 1529 public int getErrorCode() { 1530 return errorCode; 1531 } 1532 1533 }; 1534 } 1535 } 1536 1537 /** 1538 * Get owner thread name 1539 * For use sync DataSource 1540 * @return String 1541 * */ 1542 public String getOwner() { 1543 return owner; 1544 } 1545 1546 /** 1547 * Get config properties 1548 * @return ConfigProperties 1549 * */ 1550 public ConfigProperties getConfigProperties() { 1551 return configProperties; 1552 } 1553 1554 /** 1555 * Get sync data source list 1556 * @exception SQLException 1557 * @param owner - From master data source thread name 1558 * @return List<DriverDataSource> 1559 * */ 1560 protected final synchronized List<DriverDataSource> getSyncDataSourceList(String owner) throws SQLException { 1561 if (syncDriverDataSource == null) { 1562 syncDriverDataSource = new ArrayList<DriverDataSource>(); 1563 List<String> syncDsPropList = configProperties.getArray("SyncDataSource"); 1564 1565 if (syncDsPropList == null) 1566 return null; 1567 1568 for (String syncDsProp : syncDsPropList) { 1569 1570 if (CommonTools.isBlank(syncDsProp)) 1571 continue; 1572 1573 File _appDsFile = new File(syncDsProp); 1574 if (_appDsFile.exists()) { 1575 DriverDataSource dds = new DriverDataSource(_appDsFile); 1576 dds.owner = owner; 1577 syncDriverDataSource.add(dds); 1578 } else { 1579 int errorCode = 99905; 1580 String errorMsg = String.format("SyncDatasource is null - %s .", syncDsProp); 1581 throw new SQLException(errorMsg) { 1582 @Override 1583 public int getErrorCode() { 1584 return errorCode; 1585 } 1586 }; 1587 } 1588 1589 } 1590 } 1591 return syncDriverDataSource; 1592 } 1593 1594 /** 1595 * For checksum master and sync data source connection url 1596 * Pause sync where same connection url 1597 * @return String 1598 * */ 1599 public String getFingerprint() { 1600 return CommonTools.md5(String.format("%s-%s", this.url, this.username)); 1601 } 1602 1603 public synchronized double getAveUsage(){ 1604 int size = aveUsageSum.size(); 1605 double sum = 0.0D; 1606 for(int i = 0; i < size;i++){ 1607 sum += aveUsageSum.get(i); 1608 } 1609 1610 if(size <= 0) return 0.0D; 1611 1612 return sum/size; 1613 } 1614 1615 public void setMaxAveUsageSize(Integer maxAveUsageSize){ 1616 this.maxAveUsageSize = maxAveUsageSize == null ? 10000 : maxAveUsageSize; 1617 } 1618 1619 private synchronized void countUsage(boolean calAve) { 1620 double usage = getUsage(); 1621 if(calAve){ 1622 if(aveUsageSum.size() >= maxAveUsageSize){ 1623 aveUsageSum.remove(0); 1624 } 1625 aveUsageSum.add(usage); 1626 } 1627 if (usage > maxUsage) { 1628 maxUsage = usage; 1629 } 1630 } 1631 1632 /** 1633 * Get pool using 1634 * @return double 1635 * */ 1636 public synchronized double getUsage() { 1637 Map<Integer, Date> CONNECTION_USING_AT_MARK_SYNC = connectionPool.getConnectionUsingAtMark(); 1638 int usingSize = CONNECTION_USING_AT_MARK_SYNC.keySet().size(); 1639 1640 if (usingSize <= 0) 1641 return 0.0D; 1642 1643 if (maxPoolSize <= 0) 1644 return 0.0D; 1645 1646 return usingSize / (maxPoolSize * 1.0D); 1647 } 1648 1649 /** Abandon the following methods **/ 1650 1651 @Override 1652 public java.lang.Object unwrap(java.lang.Class arg0) throws java.sql.SQLException { 1653 return null; 1654 } 1655 1656 @Override 1657 public boolean isWrapperFor(java.lang.Class arg0) throws java.sql.SQLException { 1658 return false; 1659 } 1660 1661 @Override 1662 public java.util.logging.Logger getParentLogger() throws java.sql.SQLFeatureNotSupportedException { 1663 return null; 1664 } 1665 1666 @Override 1667 public void setLogWriter(PrintWriter arg0) throws SQLException { 1668 } 1669 1670 @Override 1671 public PrintWriter getLogWriter() throws SQLException { 1672 return null; 1673 } 1674 1675 @Override 1676 public int getLoginTimeout() throws SQLException { 1677 return 0; 1678 } 1679 1680 @Override 1681 public void setLoginTimeout(int arg0) throws SQLException { 1682 } 1683 1684 @Override 1685 public Connection getConnection(String arg0, String arg1) throws SQLException { 1686 return null; 1687 } 1688 1689 @Override 1690 public ConnectionBuilder createConnectionBuilder() throws SQLException { 1691 return null; 1692 } 1693 1694}