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