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