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 Collections.synchronizedMap(CONNECTION_OPENED_AT_MARK).keySet().size(); 551 return connectionPool.getOpenConnectionSize(); 552 } 553 554 555 /** 556 * Get connection pool size 557 * @return Integer 558 * */ 559 public ConnectionPool getConnectionPool() { 560 return connectionPool; 561 } 562 563 /** 564 * Abort timeout connection 565 * Skip execute when 'timeout' is 0 or 'transactionTimeout' is 0 566 * */ 567 private synchronized void abortTimeoutConnection() { 568 try { 569 if (timeout > 0 || transactionTimeout > 0) { 570 Date currentTime = new Date(); 571 List<Integer> abortList = new ArrayList<Integer>(); 572 Set<Integer> hashCodeList = connectionPool.getConnectionOpenedAtMark().keySet(); 573 Map<Integer,Date> usingAtMark = connectionPool.getConnectionUsingAtMark(); 574 for (Integer hashCode : hashCodeList) { 575 Date openedAt = usingAtMark.get(hashCode); 576 if (openedAt != null) { 577 Connection conn = connectionPool.getConnectionMark().get(hashCode); 578 579 if (conn == null) { 580 connectionPool.removeConnectionUsingAtMark(hashCode); 581 continue; 582 } 583 584 long timeoutSec = (currentTime.getTime() - openedAt.getTime()) / 1000; 585 Long _timeout = conn.getAutoCommit() ? timeout : transactionTimeout; 586 587 if (_timeout == 0) 588 continue; 589 590 if (timeoutSec >= _timeout) { 591 if (!abortList.contains(hashCode)) 592 abortList.add(hashCode); 593 } 594 } 595 } 596 for (Integer hashCode : abortList) { 597 countAbortedTimeout++; 598 log.mark("AbortTimeoutConnection '{}', ConnectionPoolSize '{}', OpenConnectionSize '{}'.",hashCode,connectionPool.size(),getOpenConnectionSize()); 599 abort(hashCode); 600 } 601 } 602 } catch (Exception e) { 603 log.warn(e.getMessage(), e); 604 } 605 } 606 607 /** 608 * Abort life cycle connection 609 * Skip execute when 'lifeCycle' is 0 610 * */ 611 private synchronized void abortLifeCycleEndedConnection() { 612 if (lifeCycle > 0) { 613 Date currentTime = new Date(); 614 List<Integer> abortList = new ArrayList<Integer>(); 615 Map<Integer,Date> openedAtMark = connectionPool.getConnectionOpenedAtMark(); 616 Set<Integer> hashCodeList = openedAtMark.keySet(); 617 for (Integer hashCode : hashCodeList) { 618 Date openedAt = openedAtMark.get(hashCode); 619 if (openedAt != null) { 620 long lifeSec = (currentTime.getTime() - openedAt.getTime()) / 1000; 621 if (lifeSec >= lifeCycle && connectionPool.contains(hashCode)) { 622 if (!abortList.contains(hashCode)) 623 abortList.add(hashCode); 624 } 625 } 626 } 627 for (Integer hashCode : abortList) { 628 if (connectionPool.contains(hashCode)) { 629 countAbortedLifeCycle++; 630 log.mark("AbortLifeCycleEndedConnection '{}', ConnectionPoolSize '{}', OpenConnectionSize '{}'.",hashCode,connectionPool.size(),getOpenConnectionSize()); 631 abort(hashCode); 632 } 633 } 634 } 635 } 636 637 /** 638 * Abort idle timeout connection 639 * Skip execute when 'idleTimeout' is 0 640 * */ 641 private synchronized void abortIdleTimeoutConnection() { 642 if (idleTimeout > 0) { 643 Date currentTime = new Date(); 644 List<Integer> abortList = new ArrayList<Integer>(); 645 Map<Integer,Date> idleBeginMark = connectionPool.getConnectionIdleBeginMark(); 646 Set<Integer> hashCodeList = idleBeginMark.keySet(); 647 for (Integer hashCode : hashCodeList) { 648 Date openedAt = idleBeginMark.get(hashCode); 649 if (openedAt != null) { 650 long idleTime = (currentTime.getTime() - openedAt.getTime()) / 1000; 651 if (idleTime >= idleTimeout && connectionPool.contains(hashCode)) { 652 if (!abortList.contains(hashCode)) 653 abortList.add(hashCode); 654 } 655 } 656 } 657 for (Integer hashCode : abortList) { 658 if (connectionPool.contains(hashCode)) { 659 int loadPoolSize = connectionPool.size(); 660 boolean overMinPoolSize = loadPoolSize > minPoolSize; 661 log.debug("Check abort idle connection, overMinPoolSize[%s] = loadPoolSize[%s] > minPoolSize[%s].",overMinPoolSize, loadPoolSize,minPoolSize); 662 if (overMinPoolSize && connectionPool.contains(hashCode)) { 663 countAbortedIdle++; 664 log.mark("AbortIdleTimeoutConnection '{}', ConnectionPoolSize '{}', OpenConnectionSize '{}'.",hashCode,connectionPool.size(),getOpenConnectionSize()); 665 abort(hashCode); 666 } 667 } 668 } 669 } 670 } 671 672 /** 673 * Abort invalid connection 674 * Skip execute when 'testValidTimeout' is 0 675 * */ 676 private synchronized void abortInvalidConnection() { 677 if (testValidTimeout > 0) { 678 try { 679 List<Integer> abortList = new ArrayList<Integer>(); 680 Map<Integer,Date> idleBeginMark = connectionPool.getConnectionIdleBeginMark(); 681 Set<Integer> hashCodeList = idleBeginMark.keySet(); 682 for (Integer hashCode : hashCodeList) { 683 Connection conn = connectionPool.getConnectionMark().get(hashCode); 684 if (conn != null) { 685 boolean valid = conn.isValid(testValidTimeout.intValue()); 686 if (!valid && !abortList.contains(hashCode)) 687 abortList.add(hashCode); 688 } 689 } 690 for (Integer hashCode : abortList) { 691 if (connectionPool.contains(hashCode)) { 692 countAbortedInvalid++; 693 log.mark("AbortInvalidConnection '{}', ConnectionPoolSize '{}', OpenConnectionSize '{}'.",hashCode,connectionPool.size(),getOpenConnectionSize()); 694 abort(hashCode); 695 } 696 } 697 } catch (Exception e) { 698 log.warn(e.getMessage(), e); 699 700 } 701 } 702 } 703 704 /** 705 * Create connection to pool 706 * When, 707 * (loadPoolSize + usedPoolSize) < maxPoolSize 708 * and 709 * loadPoolSize < minPoolSize 710 * */ 711 private synchronized void createConnectionToPool() { 712 final String threadName = String.format("%s-createConnectionToPool", this.name); 713 714 boolean lessThanMaxPoolSize = getOpenConnectionSize() < maxPoolSize; 715 boolean lessThanMinPoolSize = connectionPool.size() < minPoolSize; 716 boolean lessThanMaxUsage = getUsage() < 1.0D; 717 718 if (log.isDebugEnabled()) { 719 log.debug("boolean lessThanMaxPoolSize[{}] = getOpenConnectionSize()[{}] < maxPoolSize[{}];", 720 lessThanMaxPoolSize, getOpenConnectionSize(), maxPoolSize); 721 log.debug("boolean lessThanMinPoolSize[{}] = connectionPool.size()[{}] < minPoolSize[{}];", 722 lessThanMinPoolSize, connectionPool.size(), minPoolSize); 723 log.debug("boolean lessThanMaxUsage[{}] = getUsage()[{}] < 1.0D;", lessThanMaxUsage, getUsage()); 724 log.debug("Checking create conditions,LessThanMaxPoolSize={}, LessThanMinPoolSize={}, lessThanMaxUsage={}", 725 lessThanMaxPoolSize, lessThanMinPoolSize, lessThanMaxUsage); 726 727 } 728 if (lessThanMaxPoolSize && lessThanMinPoolSize && lessThanMaxUsage) { 729 int willCreateConnTotal = minPoolSize - connectionPool.size(); 730 if (willCreateConnTotal > 0) { 731 log.debug("Will create connection total = {}", willCreateConnTotal); 732 } 733 for (int i = 0; i < willCreateConnTotal; i++) { 734 Callable callable = new Callable<Boolean>() { 735 @Override 736 public Boolean call() { 737 try { 738 Thread.currentThread().setName("call-" + threadName); 739 if (closed || Thread.currentThread().isInterrupted()){ 740 return false; 741 } 742 743 boolean lessThanMaxPoolSize = getOpenConnectionSize() < maxPoolSize; 744 boolean lessThanMinPoolSize = connectionPool.size() < minPoolSize; 745 boolean lessThanMaxUsage = getUsage() < 1.0D; 746 747 if (lessThanMaxPoolSize && lessThanMinPoolSize && lessThanMaxUsage) { 748 Thread.currentThread().setName(threadName); 749 loadConnectionToPool(); 750 log.debug("Loaded connection to pool"); 751 } 752 } catch (Exception e) { 753 log.warn(e.getMessage(), e); 754 } 755 return true; 756 } 757 }; 758 connectionPoolExecutor.submit(callable); 759 } 760 log.debug("Creating {} connection to pool.", willCreateConnTotal); 761 } 762 } 763 764 /** 765 * Set internal config 766 * @param _configProperties 767 * */ 768 private synchronized void setInternalConfig(ConfigProperties _configProperties) { 769 this.configProperties = _configProperties; 770 if (this.configProperties.containsKey("EnableStatusLog")) 771 setEnableStatusLog(this.configProperties.getBoolean("EnableStatusLog", false)); 772 773 if (this.configProperties.containsKey("EnableFullStatusLog")) 774 setEnableFullStatusLog(this.configProperties.getBoolean("EnableFullStatusLog", true)); 775 776 if (this.configProperties.containsKey("StatusLogArchiveDays")) 777 setStatusLogArchiveDays(this.configProperties.getInteger("StatusLogArchiveDays", 30)); 778 779 if (this.configProperties.containsKey("StatusLogFolder")) 780 setStatusLogFolder(this.configProperties.getString("StatusLogFolder", null)); 781 782 if (this.configProperties.containsKey("DynamicReloadConfig")) 783 setDynamicReloadConfig(this.configProperties.getBoolean("DynamicReloadConfig", false)); 784 785 if (this.configProperties.containsKey("MaxPoolSize")) 786 setMaxPoolSize(this.configProperties.getInteger("MaxPoolSize", Integer.MAX_VALUE)); 787 788 if (this.configProperties.containsKey("MinPoolSize")) 789 setMinPoolSize(this.configProperties.getInteger("MinPoolSize", 0)); 790 791 if (this.configProperties.containsKey("LifeCycle")) 792 setLifeCycle(this.configProperties.getSeconds("LifeCycle", Long.MAX_VALUE)); 793 794 if (this.configProperties.containsKey("IdleTimeout")) 795 setIdleTimeout(this.configProperties.getSeconds("IdleTimeout", 0L)); 796 797 if (this.configProperties.containsKey("Timeout")) 798 setTimeout(this.configProperties.getSeconds("Timeout", 0L)); 799 800 if (this.configProperties.containsKey("TransactionTimeout")) 801 setTransactionTimeout(this.configProperties.getSeconds("TransactionTimeout", 0L)); 802 803 if (this.configProperties.containsKey("TestValidTimeout")) 804 setTestValidTimeout(this.configProperties.getSeconds("TestValidTimeout", 5L)); 805 806 if (this.configProperties.containsKey("RefreshPoolTimer")) 807 setRefreshPoolTimer(this.configProperties.getMilliSeconds("RefreshPoolTimer", 1000L)); 808 809 if (this.configProperties.containsKey("DriverJar")) 810 setDriverJar(this.configProperties.getString("DriverJar", null)); 811 812 if (this.configProperties.containsKey("ConnProps")) 813 setConnProps(this.configProperties.getString("ConnProps", null)); 814 815 if (this.configProperties.containsKey("ThreadName")) 816 setName(this.configProperties.getString("ThreadName", null)); 817 818 if (this.configProperties.containsKey("Driver")) 819 setDriverClassName(this.configProperties.getString("Driver", null)); 820 821 if (this.configProperties.containsKey("Url")) 822 setUrl(this.configProperties.getString("Url", null)); 823 824 if (this.configProperties.containsKey("Url[0]")) 825 setUrl(this.configProperties.getArray("Url")); 826 827 if (this.configProperties.containsKey("Username")) 828 setUsername(this.configProperties.getString("Username", null)); 829 830 if (this.configProperties.containsKey("Password")) 831 setPassword(this.configProperties.getString("Password", null)); 832 833 if (this.configProperties.containsKey("AbortMode")) 834 setAbortMode(this.configProperties.getString("AbortMode", "abort")); 835 836 if (this.configProperties.containsKey("MaxAveUsageSize")) 837 setMaxAveUsageSize(this.configProperties.getInteger("MaxAveUsageSize", 10000)); 838 839 loadedConfig = true; 840 } 841 842 /** 843 * Set internal config 844 * @param configPropertiesContent 845 * */ 846 private synchronized void setInternalConfig(String configPropertiesContent) throws IOException { 847 log.debug("setInternalConfig -> {}", configPropertiesContent); 848 ConfigProperties _configProperties = new ConfigProperties(); 849 _configProperties.load(new StringReader(configPropertiesContent)); 850 setInternalConfig(_configProperties); 851 } 852 853 /** 854 * Set config 855 * @param configPropertiesContent 856 * */ 857 public synchronized void setConfig(String configPropertiesContent) throws IOException { 858 setInternalConfig(configPropertiesContent); 859 setDynamicReloadConfig(false); 860 } 861 862 /** 863 * Set config 864 * @param _configProperties 865 * */ 866 public synchronized void setConfig(ConfigProperties _configProperties) throws IOException { 867 setInternalConfig(_configProperties); 868 setDynamicReloadConfig(false); 869 } 870 871 /** 872 * Reload config 873 * When 'appDataSource' is file object 874 * */ 875 private synchronized void reloadConfig() { 876 try { 877 if (this.appDataSource != null) { 878 ConfigProperties loadedConfigProperties = this.configProperties; 879 String absPath = this.appDataSource.getAbsolutePath(); 880 Logger.systemDebug(DriverDataSource.class, "Loading config(For System Init Debug) -> {}", absPath); 881 log.debug("Loading config -> {}", absPath); 882 String dsCnfText = FileTools.text(absPath); 883 if (dsCnfText != null) { 884 setInternalConfig(dsCnfText); 885 } 886 boolean same = ConfigProperties.compareProperties(loadedConfigProperties,this.configProperties); 887 if(!same){ 888 Logger.systemMark(DriverDataSource.class, "DriverDataSource '{}' Reloaded", name); 889 } 890 } 891 } catch (Exception e) { 892 log.warn(e); 893 } 894 } 895 896 /** 897 * Archive log 898 * */ 899 private void archiveLog() { 900 if (statusLogFile != null && statusLogArchiveDays != null) { 901 File statusLogFileParent = statusLogFile.getParentFile(); 902 if (statusLogArchiveDays > 0 && statusLogFileParent != null) { 903 try { 904 long fileOutArchiveDaysMs = new Date().getTime() - (statusLogArchiveDays * 24 * 3600000L); 905 deleteFilesOlderThan(statusLogFileParent, fileOutArchiveDaysMs); 906 } catch (Exception e) { 907 log.warn(e); 908 909 } 910 } 911 } 912 } 913 914 /** 915 * Delete old archive log 916 * */ 917 private void deleteFilesOlderThan(File directory, long fileOutArchiveDaysMs) throws IOException { 918 if (directory.isDirectory()) { 919 File[] files = directory.listFiles(); 920 if (files != null) { 921 for (File file : files) { 922 if (file.isFile()) { 923 boolean isLogFile = file.getName().toLowerCase().endsWith(".log"); 924 if (isLogFile) { 925 boolean canWrite = file.canWrite(); 926 if (canWrite) { 927 long lastModified = file.lastModified(); 928 if (lastModified < fileOutArchiveDaysMs) { 929 Files.deleteIfExists(Paths.get(file.toURI())); 930 } 931 } 932 } 933 } 934 } 935 } 936 } 937 } 938 939 /** 940 * Write connection status log 941 * */ 942 private synchronized void statusLogOutput() { 943 File folder = statusLogFile.getParentFile(); 944 if (folder.exists()) { 945 if (!folder.canWrite()) 946 return; 947 } else { 948 folder.mkdirs(); 949 } 950 if (statusLogFile.exists()) { 951 if (!statusLogFile.canWrite()) 952 return; 953 } 954 955 SimpleDateFormat timeFormat = new SimpleDateFormat("yyyyMMdd HH:mm:ss.SSS"); 956 Map<Integer, String> CONNECTION_THREAD_MARK_SYNC = connectionPool.getConnectionThreadMark(); 957 Map<Integer, Date> CONNECTION_OPENED_AT_MARK_SYNC = connectionPool.getConnectionOpenedAtMark(); 958 Map<Integer, Date> CONNECTION_USING_AT_MARK_SYNC = connectionPool.getConnectionUsingAtMark(); 959 Map<Integer, Date> CONNECTION_IDLE_BEGIN_MARK_SYNC = connectionPool.getConnectionIdleBeginMark(); 960 961 int usingSize = CONNECTION_USING_AT_MARK_SYNC.keySet().size(); 962 int idleSize = CONNECTION_IDLE_BEGIN_MARK_SYNC.keySet().size(); 963 964 boolean isFull = (usingSize > idleSize && idleSize == 0 && usingSize >= maxPoolSize); 965 966 String driverInfo = String.format("Driver: %s%s", this.driverClassName, System.lineSeparator()); 967 StringBuffer countInfoSb = new StringBuffer(); 968 countInfoSb.append( 969 String.format("MinPoolSize: %s\tMaxPoolSize: %s%s", minPoolSize, maxPoolSize, System.lineSeparator())); 970 countInfoSb.append(String.format("RefreshPoolTimer: %sms%s", refreshPoolTimer, System.lineSeparator())); 971 countInfoSb.append(String.format("TestValidTimeout: %ss\tIdleTimeout: %ss%s", testValidTimeout, idleTimeout, 972 System.lineSeparator())); 973 countInfoSb.append(String.format("Timeout: %ss\tTransactionTimeout: %ss%s", timeout, transactionTimeout, 974 System.lineSeparator())); 975 countInfoSb.append( 976 String.format("LifeCycle: %ss\tCountClosed: %s%s", lifeCycle, countClosed, System.lineSeparator())); 977 countInfoSb.append(String.format("CountCreatedConnection: %s\tCountAbortedCloseFailed: %s%s", 978 countCreatedConnection, countAbortedCloseFailed, System.lineSeparator())); 979 countInfoSb.append(String.format("CountAbortedIdleTimeouted: %s\tCountAbortedTimeouted: %s%s", countAbortedIdle, 980 countAbortedTimeout, System.lineSeparator())); 981 countInfoSb.append(String.format("CountAbortedLifeCycleEnded: %s\tCountAbortedInvalid: %s%s", 982 countAbortedLifeCycle, countAbortedInvalid, System.lineSeparator())); 983 countInfoSb.append(String.format("MaxUsage: %.2f%%\tCurrentUsage: %.2f%%\tAveUsage: %.2f%%\tCounter: %s/%s%s", maxUsage * 100, 984 getUsage() * 100,getAveUsage() * 100, aveUsageSum.size(),maxAveUsageSize,System.lineSeparator())); 985 986 String urlInfo = String.format("URL: %s%s", this.url, System.lineSeparator()); 987 String usernameInfo = String.format("User: %s%s", this.username, System.lineSeparator()); 988 String header = String.format("%sLine\tHashCode\tCreatedTime\tChangedTime\tStatus\tLastUsingBy%s", 989 System.lineSeparator(), System.lineSeparator()); 990 String lineFormat = "%s\t%s\t%s\t%s\t%s\t%s%s"; 991 StringBuffer sbf = new StringBuffer(); 992 sbf.append(String.format("ThreadName: %s%s", this.name, System.lineSeparator())); 993 sbf.append(String.format("Host: %s\tPID: %s\tSystemTime: %s%s", CommonTools.getHostname(), PID, 994 timeFormat.format(new Date()), System.lineSeparator())); 995 sbf.append(urlInfo); 996 sbf.append(usernameInfo); 997 sbf.append(driverInfo); 998 sbf.append(countInfoSb.toString()); 999 sbf.append(header); 1000 int lineNumber = 1; 1001 1002 Set<Integer> keys = CONNECTION_OPENED_AT_MARK_SYNC.keySet(); 1003 List<Object[]> list = new ArrayList<Object[]>(); 1004 for (Integer hashCode : keys) { 1005 String status = "-"; 1006 String thread = "-"; 1007 Date changedTime = null; 1008 Date createdTime = CONNECTION_OPENED_AT_MARK_SYNC.get(hashCode); 1009 1010 if (CONNECTION_THREAD_MARK_SYNC.containsKey(hashCode)) { 1011 thread = CONNECTION_THREAD_MARK_SYNC.get(hashCode); 1012 } 1013 1014 if (CONNECTION_USING_AT_MARK_SYNC.containsKey(hashCode)) { 1015 changedTime = CONNECTION_USING_AT_MARK_SYNC.get(hashCode); 1016 status = "Using"; 1017 } else if (CONNECTION_IDLE_BEGIN_MARK_SYNC.containsKey(hashCode)) { 1018 changedTime = CONNECTION_IDLE_BEGIN_MARK_SYNC.get(hashCode); 1019 status = "Idle"; 1020 } 1021 1022 Object[] objArr = new Object[] { hashCode, createdTime, changedTime, status, thread }; 1023 list.add(objArr); 1024 } 1025 1026 Comparator comp = new Comparator() { 1027 @Override 1028 public int compare(Object arg0, Object arg1) { 1029 Object[] objArr0 = (Object[]) arg0; 1030 Object[] objArr1 = (Object[]) arg1; 1031 Date date0 = (Date) objArr0[1]; 1032 Date date1 = (Date) objArr1[1]; 1033 return date0.getTime() < date1.getTime() ? -1 : 1; 1034 } 1035 }; 1036 1037 Collections.sort(list, comp); 1038 1039 for (Object[] objArr : list) { 1040 Integer hashCode = (Integer) objArr[0]; 1041 Date createdTime = (Date) objArr[1]; 1042 String createdTimeStr = createdTime == null ? "-" : timeFormat.format(createdTime); 1043 Date changedTime = (Date) objArr[2]; 1044 String changedTimeStr = changedTime == null ? "-" : timeFormat.format(changedTime); 1045 String status = objArr[3].toString(); 1046 String thread = objArr[4].toString(); 1047 String line = String.format(lineFormat, lineNumber, hashCode, createdTimeStr, changedTimeStr, status, 1048 thread, System.lineSeparator()); 1049 sbf.append(line); 1050 lineNumber++; 1051 } 1052 1053 try { 1054 String msg = sbf.toString(); 1055 FileTools.write(statusLogFile, msg, false); 1056 1057 if (isFull && enableFullStatusLog) { 1058 SimpleDateFormat timeFullFormat = new SimpleDateFormat("yyyyMMddHHmm"); 1059 File fullStatusLogFile = new File(String.format("%s_full_%s", statusLogFile.getAbsolutePath(), 1060 timeFullFormat.format(new Date()))); 1061 FileTools.write(fullStatusLogFile, msg, false); 1062 1063 } 1064 } catch (IOException e) { 1065 log.warn(e.getMessage(), e); 1066 1067 } 1068 } 1069 1070 /** 1071 * Manage connection pool 1072 * */ 1073 private synchronized void manageConnectionPool() { 1074 if (!inited) { 1075 inited = true; 1076 1077 String threadName = this.name; 1078 ExecutorService executor = Executors.newFixedThreadPool(2); 1079 executor.execute(new Runnable() { 1080 1081 @Override 1082 public void run() { 1083 Thread.currentThread().setName(String.format("%s-manageConnectionPool-1", threadName)); 1084 while (true) { 1085 if (closed || Thread.currentThread().isInterrupted()) 1086 break; 1087 1088 try { 1089 abortLifeCycleEndedConnection(); 1090 abortTimeoutConnection(); 1091 abortIdleTimeoutConnection(); 1092 createConnectionToPool(); 1093 countUsage(true); 1094 if (enableStatusLog && statusLogFile != null) { 1095 statusLogOutput(); 1096 archiveLog(); 1097 } 1098 if (dynamicReloadConfig) { 1099 boolean overTime = (System.currentTimeMillis() - dynamicReloadConfigTime) >= RELOAD_TIMER; 1100 if(overTime) { 1101 reloadConfig(); 1102 dynamicReloadConfigTime = System.currentTimeMillis(); 1103 } 1104 } 1105 } catch (Exception e) { 1106 log.warn(e.getMessage(), e); 1107 } 1108 1109 try { 1110 Thread.sleep(refreshPoolTimer); 1111 } catch (InterruptedException e) { 1112 log.warn(e.getMessage(), e); 1113 } 1114 } 1115 } 1116 1117 }); 1118 1119 executor.execute(new Runnable() { 1120 1121 @Override 1122 public void run() { 1123 Thread.currentThread().setName(String.format("%s-manageConnectionPool-2", threadName)); 1124 while (true) { 1125 if (closed || Thread.currentThread().isInterrupted()) 1126 break; 1127 1128 try { 1129 abortInvalidConnection(); 1130 } catch (Exception e) { 1131 log.warn(e.getMessage(), e); 1132 1133 } 1134 1135 try { 1136 Thread.sleep(refreshPoolTimer); 1137 } catch (InterruptedException e) { 1138 log.warn(e.getMessage(), e); 1139 } 1140 } 1141 } 1142 1143 }); 1144 } 1145 } 1146 1147 /** 1148 * Get connection 1149 * @exception SQLException 1150 * @return Connection 1151 * */ 1152 @Override 1153 public synchronized Connection getConnection() throws SQLException { 1154 try{ 1155 manageConnectionPool(); 1156 Date currentTime = new Date(); 1157 Connection conn = null; 1158 boolean allowedGetSingle = true; 1159 1160 if(connectionPool.size() == 0){ 1161 loadConnectionToPool(); 1162 } 1163 1164 conn = connectionPool.getConnection(); 1165 if (conn == null) { 1166 throwConnIsNull(); 1167 } 1168 countUsage(false); 1169 1170 if (conn == null) { 1171 throwConnIsNull(); 1172 } 1173 log.debug("Get Connection -> {}", conn.hashCode()); 1174 return new DriverConnection(this, conn); 1175 }catch(Exception e){ 1176 log.error(e.getMessage(),e); 1177 throwConnIsNull(); 1178 } 1179 return null; 1180 } 1181 1182 private void throwConnIsNull() throws SQLException { 1183 int errorCode = 99904; 1184 String errorMsg = "Connection is null."; 1185 log.warn(String.format("ERROR CODE: (%s) %s", errorCode, errorMsg)); 1186 throw new SQLException(errorMsg) { 1187 @Override 1188 public int getErrorCode() { 1189 return errorCode; 1190 } 1191 }; 1192 } 1193 1194 private synchronized void checkConnectionPoolSize() throws SQLException { 1195 int openConnectionSize = getOpenConnectionSize(); 1196 if (openConnectionSize >= maxPoolSize) { 1197 int errorCode = 99903; 1198 String errorMsg = String.format("Full connection pool,current size is %s.", openConnectionSize); 1199 log.warn(String.format("ERROR CODE: (%s) %s", errorCode, errorMsg)); 1200 throw new SQLException(errorMsg) { 1201 @Override 1202 public int getErrorCode() { 1203 return errorCode; 1204 } 1205 }; 1206 1207 } 1208 } 1209 1210 /** 1211 * Get connection properties 1212 * @return Properties 1213 * */ 1214 private Properties getConnectionProperties() { 1215 Properties props = new Properties(); 1216 props.setProperty("user", username); 1217 props.setProperty("password", password); 1218 1219 if (!CommonTools.isBlank(connProps)) { 1220 try { 1221 String[] ps = connProps.split(";"); 1222 int psSize = ps.length; 1223 for (int i = 0; i < psSize; i++) { 1224 String[] propKeyValue = ps[i].split("="); 1225 String pkey = propKeyValue[0].trim(); 1226 String pval = propKeyValue[1].trim(); 1227 props.setProperty(pkey, pval); 1228 } 1229 } catch (Exception e) { 1230 log.warn(e.getMessage(), e); 1231 1232 } 1233 } 1234 return props; 1235 } 1236 1237 /** 1238 * Get connection from driver 1239 * @exception SQLException 1240 * @return Connection 1241 * */ 1242 private synchronized Connection getConnectionFromDriver() throws SQLException { 1243 Properties props = getConnectionProperties(); 1244 Connection conn = null; 1245 if (driverJar == null) { 1246 conn = tryGetConnection(null, props); 1247 } else { 1248 Driver driver = loadJdbcDriver(driverJar, driverClassName); 1249 log.debug("Success init drive -> {}", driverClassName); 1250 conn = tryGetConnection(driver, props); 1251 } 1252 1253 if (conn == null) { 1254 throwConnIsNull(); 1255 } 1256 return conn; 1257 } 1258 1259 /** 1260 * Load connection to pool 1261 * @exception SQLException 1262 * */ 1263 private synchronized void loadConnectionToPool() throws SQLException { 1264 checkConnectionPoolSize(); 1265 log.debug("Opening(Pool)"); 1266 final Connection conn = getConnectionFromDriver(); 1267 final Integer hashCode = conn.hashCode(); 1268 log.debug("Created Connection -> {}", hashCode); 1269 connectionPool.add(conn); 1270 } 1271 1272 /** 1273 * Close connection 1274 * @exception SQLException 1275 * @param conn - Connection object 1276 * */ 1277 protected void closeConnection(Connection conn) throws SQLException { 1278 if (conn != null) 1279 closeConnection(conn.hashCode()); 1280 } 1281 1282 /** 1283 * Close connection 1284 * @exception SQLException 1285 * @param hashCode - Connection hash code 1286 * */ 1287 protected void closeConnection(Integer hashCode) throws SQLException { 1288 Connection conn = connectionPool.getConnectionMark().get(hashCode); 1289 if (conn != null) { 1290 try { 1291 synchronized (this) { 1292 connectionPool.removeConnectionUsingAtMark(hashCode); 1293 } 1294 if (lifeCycle <= 0) { 1295 conn.close(); 1296 connectionPool.removeAll(hashCode); 1297 countClosed++; 1298 log.debug("Closed Connection -> {}", hashCode); 1299 } else { 1300 if(closed){ 1301 conn.close(); 1302 connectionPool.removeAll(hashCode); 1303 countClosed++; 1304 log.debug("Closed Connection -> {}", hashCode); 1305 }else{ 1306 conn.setAutoCommit(true); 1307 connectionPool.freed(hashCode); 1308 log.debug("Freed Connection -> {}", hashCode); 1309 } 1310 } 1311 } catch (SQLException e) { 1312 addCountAbortedCloseFailed(+1); 1313 abort(hashCode); 1314 1315 throw e; 1316 } 1317 } 1318 } 1319 1320 /** 1321 * Add count abort close failed for write status log 1322 * @param value - Change value 1323 * */ 1324 private synchronized void addCountAbortedCloseFailed(int value) { 1325 countAbortedCloseFailed += value; 1326 } 1327 1328 /** 1329 * Close all connections 1330 * */ 1331 public synchronized void close() { 1332 closed = true; 1333 log.mark("Try Abort(On SystemExit,CLOSE-{}) connections.",name); 1334 Map<Integer,Connection> connectionMark = connectionPool.getConnectionMark(); 1335 Iterator<Integer> itr = connectionMark.keySet().iterator(); 1336 while (itr.hasNext()) { 1337 Integer key = itr.next(); 1338 Connection conn = connectionMark.get(key); 1339 if (conn != null) { 1340 log.mark("Abort(On SystemExit) connection '{}.{}'.",name,conn.hashCode()); 1341 abort(conn.hashCode()); 1342 } 1343 } 1344 List<Integer> connList = connectionPool.list(); 1345 for(Integer hashCode : connList){ 1346 log.mark("Abort(On SystemExit) connection '{}.{}'.",name,hashCode); 1347 abort(hashCode); 1348 } 1349 log.mark("Aborted all connections '{}'.",name); 1350 } 1351 1352 /** 1353 * Loop close all connections 1354 * */ 1355 public synchronized void closeAll() { 1356 closed = true; 1357 log.mark("Try Abort(On SystemExit,CLOSE_ALL-{}) connections.",name); 1358 List<Integer> connList = connectionPool.list(); 1359 for(Integer hashCode : connList){ 1360 log.mark("Abort(On SystemExit) connection '{}.{}'.",name,hashCode); 1361 abort(hashCode); 1362 } 1363 double currentUsage = getUsage(); 1364 log.mark("Checking usage for close all , the '{}' usage is '{}'.",name,currentUsage); 1365 if(currentUsage > 0){ 1366 try{ 1367 Thread.sleep(1000L); 1368 }catch(InterruptedException e){ 1369 log.error(e.getMessage(),e); 1370 } 1371 closeAll(); 1372 } 1373 log.mark("Aborted all connections '{}'.",name); 1374 } 1375 1376 /** 1377 * Deregister JDBC Driver 1378 * For system exit 1379 * */ 1380 public synchronized static void deregisterDriver(){ 1381 LoggerFactory.getLogger(DriverDataSource.class).mark("DeregisterDriver processing."); 1382 Enumeration<Driver> drivers = DriverManager.getDrivers(); 1383 if(drivers != null){ 1384 while (drivers.hasMoreElements()) { 1385 Driver driver = drivers.nextElement(); 1386 try{ 1387 LoggerFactory.getLogger(DriverDataSource.class).mark("Driver: '{}'",driver.hashCode()); 1388 DriverManager.deregisterDriver(driver); 1389 }catch(Exception e){ 1390 LoggerFactory.getLogger(DriverDataSource.class).error(e.getMessage(),e); 1391 } 1392 } 1393 } 1394 LoggerFactory.getLogger(DriverDataSource.class).mark("DeregisterDriver processed."); 1395 } 1396 1397 /** 1398 * Try get connection 1399 * Support multi url 1400 * @param driver - Driver 1401 * @param props - Connection properties ,var name is 'connProps' 1402 * */ 1403 private synchronized Connection tryGetConnection(Driver driver, Properties props) { 1404 Connection conn = null; 1405 int urlLastIndex = urlList.size() - 1; 1406 1407 do { 1408 if (closed) 1409 break; 1410 1411 if (connectionPool.size() >= maxPoolSize) 1412 break; 1413 1414 if (!CONN_ERROR_TIME.containsKey(urlIndex)) { 1415 CONN_ERROR_TIME.put(urlIndex, 0L); 1416 } 1417 1418 Long connErrorTime = CONN_ERROR_TIME.get(urlIndex); 1419 if (connErrorTime > 0) { 1420 ConfigProperties cp = getConfigProperties(); 1421 long connCheckMs = 10000L; 1422 if (cp != null) { 1423 connCheckMs = cp.getLong("TryGetConnectionCheckTime", 10000L); 1424 } 1425 boolean isSkipConn = connErrorTime > 0 && (System.currentTimeMillis() - connErrorTime) <= connCheckMs; 1426 if (isSkipConn) { 1427 try { 1428 Thread.sleep(100L); 1429 } catch (InterruptedException e) { 1430 log.debug(e.getMessage(), e); 1431 } 1432 continue; 1433 } 1434 } 1435 1436 url = urlList.get(urlIndex); 1437 try { 1438 if (driver == null) { 1439 Class.forName(driverClassName); 1440 conn = DriverManager.getConnection(url, props); 1441 } else { 1442 conn = driver.connect(url, props); 1443 } 1444 conn.setAutoCommit(true); 1445 1446 countCreatedConnection++; 1447 1448 CONN_ERROR_TIME.put(urlIndex, 0L); 1449 1450 break; 1451 } catch (Exception e) { 1452 1453 CONN_ERROR_TIME.put(urlIndex, System.currentTimeMillis()); 1454 1455 urlIndex += 1; 1456 1457 Logger.systemError(DriverDataSource.class,"Failed connect to '{}'.", url); 1458 Logger.systemError(DriverDataSource.class,e.getMessage(), e); 1459 1460 } 1461 } while (urlIndex <= urlLastIndex); 1462 1463 if (conn == null) { 1464 urlIndex = 0; 1465 url = urlList.get(urlIndex); 1466 } 1467 1468 return conn; 1469 } 1470 1471 /** 1472 * Abort connection by hash code 1473 * @param hashCode - Connection hash code 1474 * */ 1475 protected void abort(Integer hashCode) { 1476 log.debug("Abort Connection {}", hashCode); 1477 Connection conn = connectionPool.getConnectionMark().get(hashCode); 1478 if (conn != null) { 1479 try { 1480 callAbortConn(conn); 1481 log.debug("Aborted Connection -> {}", hashCode); 1482 connectionPool.removeAll(hashCode); 1483 log.debug("Abort(If Aborted,{}), UsedPoolSize={}.", hashCode, getOpenConnectionSize()); 1484 } catch (Exception e) { 1485 log.warn(e.getMessage(), e); 1486 1487 } 1488 } 1489 } 1490 1491 /** 1492 * Call abort connection 1493 * Please see 'abortMode' options (rollback,close,abort) 1494 * Default is 'abort' 1495 * @param conn - Connection 1496 * */ 1497 private void callAbortConn(Connection conn) { 1498 try { 1499 if (abortMode.equalsIgnoreCase("rollback")) { 1500 if (!conn.getAutoCommit()) 1501 conn.rollback(); 1502 1503 conn.close(); 1504 } else if (abortMode.equalsIgnoreCase("close")) { 1505 conn.close(); 1506 } else { 1507 conn.abort(Executors.newFixedThreadPool(1)); 1508 } 1509 } catch (SQLException e) { 1510 log.warn("EROR CODE: {}", e.getErrorCode()); 1511 log.warn(e.getMessage(), e); 1512 1513 } catch (Exception e) { 1514 log.warn(e.getMessage(), e); 1515 1516 } 1517 } 1518 1519 /** 1520 * Load jdbc driver jar 1521 * @param driverJar - It is jar file path or jar dir path 1522 * @param driver 1523 * @exception SQLException 1524 * @return Driver 1525 * */ 1526 private synchronized Driver loadJdbcDriver(String driverJar, String driver) throws SQLException { 1527 try { 1528 1529 if (driverJar == null) { 1530 int errorCode = 99901; 1531 String errorMsg = "Driver jar is null."; 1532 log.warn(String.format("ERROR CODE: (%s) %s", errorCode, errorMsg)); 1533 throw new SQLException(errorMsg) { 1534 @Override 1535 public int getErrorCode() { 1536 return errorCode; 1537 } 1538 }; 1539 } 1540 1541 Driver driverNewInstance = DRIVER_MARK.get(driverJar); 1542 1543 if (driverNewInstance == null) { 1544 URLClassLoader driverClassLoader = CommonTools.loadJars(driverJar); 1545 Class classToLoad = Class.forName(driver, true, driverClassLoader); 1546 Constructor driverConstructor = classToLoad.getConstructor(); 1547 driverConstructor.setAccessible(true); 1548 driverNewInstance = (Driver) driverConstructor.newInstance(); 1549 1550 DRIVER_MARK.put(driverJar, driverNewInstance); 1551 } 1552 1553 return driverNewInstance; 1554 } catch (Exception e) { 1555 int errorCode = 99902; 1556 String errorMsg = e.getMessage(); 1557 log.warn(String.format("ERROR CODE: (%s) %s", errorCode, errorMsg)); 1558 throw new SQLException(e) { 1559 1560 @Override 1561 public int getErrorCode() { 1562 return errorCode; 1563 } 1564 1565 }; 1566 } 1567 } 1568 1569 /** 1570 * Get owner thread name 1571 * For use sync DataSource 1572 * @return String 1573 * */ 1574 public String getOwner() { 1575 return owner; 1576 } 1577 1578 /** 1579 * Get config properties 1580 * @return ConfigProperties 1581 * */ 1582 public ConfigProperties getConfigProperties() { 1583 return configProperties; 1584 } 1585 1586 /** 1587 * Get sync data source list 1588 * @exception SQLException 1589 * @param owner - From master data source thread name 1590 * @return List<DriverDataSource> 1591 * */ 1592 protected final synchronized List<DriverDataSource> getSyncDataSourceList(String owner) throws SQLException { 1593 if (syncDriverDataSource == null) { 1594 syncDriverDataSource = new ArrayList<DriverDataSource>(); 1595 List<String> syncDsPropList = configProperties.getArray("SyncDataSource"); 1596 1597 if (syncDsPropList == null) 1598 return null; 1599 1600 for (String syncDsProp : syncDsPropList) { 1601 1602 if (CommonTools.isBlank(syncDsProp)) 1603 continue; 1604 1605 File _appDsFile = new File(syncDsProp); 1606 if (_appDsFile.exists()) { 1607 DriverDataSource dds = new DriverDataSource(_appDsFile); 1608 dds.owner = owner; 1609 syncDriverDataSource.add(dds); 1610 } else { 1611 int errorCode = 99905; 1612 String errorMsg = String.format("SyncDatasource is null - %s .", syncDsProp); 1613 throw new SQLException(errorMsg) { 1614 @Override 1615 public int getErrorCode() { 1616 return errorCode; 1617 } 1618 }; 1619 } 1620 1621 } 1622 } 1623 return syncDriverDataSource; 1624 } 1625 1626 /** 1627 * For checksum master and sync data source connection url 1628 * Pause sync where same connection url 1629 * @return String 1630 * */ 1631 public String getFingerprint() { 1632 return CommonTools.md5(String.format("%s-%s", this.url, this.username)); 1633 } 1634 1635 public synchronized double getAveUsage(){ 1636 int size = aveUsageSum.size(); 1637 double sum = 0.0D; 1638 for(int i = 0; i < size;i++){ 1639 sum += aveUsageSum.get(i); 1640 } 1641 1642 if(size <= 0) return 0.0D; 1643 1644 return sum/size; 1645 } 1646 1647 public void setMaxAveUsageSize(Integer maxAveUsageSize){ 1648 this.maxAveUsageSize = maxAveUsageSize == null ? 10000 : maxAveUsageSize; 1649 } 1650 1651 private synchronized void countUsage(boolean calAve) { 1652 double usage = getUsage(); 1653 if(calAve){ 1654 if(aveUsageSum.size() >= maxAveUsageSize){ 1655 aveUsageSum.remove(0); 1656 } 1657 aveUsageSum.add(usage); 1658 } 1659 if (usage > maxUsage) { 1660 maxUsage = usage; 1661 } 1662 } 1663 1664 /** 1665 * Get pool using 1666 * @return double 1667 * */ 1668 public synchronized double getUsage() { 1669 Map<Integer, Date> CONNECTION_USING_AT_MARK_SYNC = connectionPool.getConnectionUsingAtMark(); 1670 int usingSize = CONNECTION_USING_AT_MARK_SYNC.keySet().size(); 1671 1672 if (usingSize <= 0) 1673 return 0.0D; 1674 1675 if (maxPoolSize <= 0) 1676 return 0.0D; 1677 1678 return usingSize / (maxPoolSize * 1.0D); 1679 } 1680 1681 /** Abandon the following methods **/ 1682 1683 @Override 1684 public java.lang.Object unwrap(java.lang.Class arg0) throws java.sql.SQLException { 1685 return null; 1686 } 1687 1688 @Override 1689 public boolean isWrapperFor(java.lang.Class arg0) throws java.sql.SQLException { 1690 return false; 1691 } 1692 1693 @Override 1694 public java.util.logging.Logger getParentLogger() throws java.sql.SQLFeatureNotSupportedException { 1695 return null; 1696 } 1697 1698 @Override 1699 public void setLogWriter(PrintWriter arg0) throws SQLException { 1700 } 1701 1702 @Override 1703 public PrintWriter getLogWriter() throws SQLException { 1704 return null; 1705 } 1706 1707 @Override 1708 public int getLoginTimeout() throws SQLException { 1709 return 0; 1710 } 1711 1712 @Override 1713 public void setLoginTimeout(int arg0) throws SQLException { 1714 } 1715 1716 @Override 1717 public Connection getConnection(String arg0, String arg1) throws SQLException { 1718 return null; 1719 } 1720 1721 @Override 1722 public ConnectionBuilder createConnectionBuilder() throws SQLException { 1723 return null; 1724 } 1725 1726}