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