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