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.util.List;
027import java.util.ArrayList;
028import java.util.Map;
029import java.util.concurrent.ConcurrentHashMap;
030import java.util.stream.Collectors;
031import com.killcoding.log.LoggerFactory;
032import com.killcoding.log.Logger;
033import java.util.Date;
034import java.sql.Connection;
035import java.util.HashMap;
036
037/**
038 * This class functions as a connection pool counter
039 * */
040public class ConnectionPool {
041
042        /**
043         * Record all connection hash code
044         * */
045        private final Map<Long, Integer> CONNECTION_POOL = new ConcurrentHashMap<Long, Integer>();
046        
047        private final Map<Integer, String> CONNECTION_THREAD_MARK = new ConcurrentHashMap<Integer, String>();
048        private final Map<Integer, Connection> CONNECTION_MARK = new ConcurrentHashMap<Integer, Connection>();
049        private final Map<Integer, Date> CONNECTION_OPENED_AT_MARK = new ConcurrentHashMap<Integer, Date>();
050        private final Map<Integer, Date> CONNECTION_USING_AT_MARK = new ConcurrentHashMap<Integer, Date>();
051        private final Map<Integer, Date> CONNECTION_IDLE_BEGIN_MARK = new ConcurrentHashMap<Integer, Date>();   
052
053        /**
054         * New a ConnectionPool object
055         * */
056        public ConnectionPool() {
057                super();
058        }
059
060        /**
061         * List all connection hash code
062         * @return Map - All connection hash code map
063         * */
064        public synchronized Map<Long,Integer> map() {
065           return new HashMap<Long,Integer>(CONNECTION_POOL);
066        }
067        
068        /**
069         * List all connection hash code
070         * @return List - All connection hash code
071         * */
072        public synchronized List<Integer> list() {
073           List<Integer> list = new ArrayList<Integer>();
074       List<Long> klist = CONNECTION_POOL.keySet()
075            .stream()
076            .sorted()
077            .collect(Collectors.toList());
078           for(Object k : klist){
079               list.add((Integer)CONNECTION_POOL.get(k));
080           }
081           return list;
082        }
083
084        /**
085         * Get connection pool size
086         * @return int - All connection size
087         * */
088        public synchronized int size() {
089                return CONNECTION_POOL.keySet().size();
090        }
091
092
093        protected synchronized boolean add(Connection conn) {
094            Integer connHashCode = conn.hashCode();
095            if(!contains(connHashCode)){
096            Long newKey = System.nanoTime();
097            while(CONNECTION_POOL.containsKey(newKey)){
098                newKey = System.nanoTime();
099            }
100            
101                final Date openedAt = new Date();
102                CONNECTION_IDLE_BEGIN_MARK.put(connHashCode, openedAt);
103                CONNECTION_OPENED_AT_MARK.put(connHashCode, openedAt);
104                CONNECTION_MARK.put(connHashCode, conn);                    
105            CONNECTION_POOL.put(newKey,connHashCode);
106            return true;
107            }
108            return false;
109        }
110
111        protected synchronized void freed(Integer connHashCode) {
112            if(!contains(connHashCode)){
113            Long newKey = System.nanoTime();
114            while(CONNECTION_POOL.containsKey(newKey)){
115                newKey = System.nanoTime();
116            }
117            CONNECTION_POOL.put(newKey,connHashCode);
118            }
119            CONNECTION_IDLE_BEGIN_MARK.put(connHashCode, new Date());
120        }
121
122        protected synchronized void removeAll(Integer connHashCode) {
123                CONNECTION_MARK.remove(connHashCode);
124                CONNECTION_OPENED_AT_MARK.remove(connHashCode);
125                CONNECTION_USING_AT_MARK.remove(connHashCode);
126                CONNECTION_IDLE_BEGIN_MARK.remove(connHashCode);
127                CONNECTION_THREAD_MARK.remove(connHashCode);        
128        remove(connHashCode);
129        }
130        
131        protected synchronized Boolean remove(Integer connHashCode) {
132        List klist = new ArrayList(CONNECTION_POOL.keySet());
133        Object rmKey = -1;
134        int size = klist.size();
135            for(int i = 0;i < size;i++){
136               Object key = klist.get(i);
137               Object hashCode = (Integer)CONNECTION_POOL.get(key);
138               if(hashCode.equals(connHashCode)){
139                   rmKey = key;
140                   break;
141               }
142            }
143            if(rmKey.equals(-1)){
144                return false;
145            }else{
146            Object rmHashCode = CONNECTION_POOL.remove(rmKey);
147            return rmHashCode != null;
148            }
149        }       
150
151        public synchronized boolean contains(Integer connHashCode) {
152                return list().contains(connHashCode);
153        }
154
155        private synchronized Integer get() {
156            try{
157            Integer hashCode =  list().get(0);
158            remove(hashCode);
159            return hashCode;
160            }catch(Exception e){
161                LoggerFactory.getLogger(ConnectionPool.class).error(e.getMessage(),e);
162                return null;
163            }
164        }
165        
166        public synchronized Integer getOpenConnectionSize() {
167                return CONNECTION_OPENED_AT_MARK.keySet().size();
168        }       
169        
170        public synchronized Map<Integer, Date> getConnectionOpenedAtMark(){
171            return new HashMap<Integer, Date>(CONNECTION_OPENED_AT_MARK);
172        }
173        
174        protected synchronized Object removeConnectionOpenedAtMark(Integer hashCode){
175             return CONNECTION_OPENED_AT_MARK.remove(hashCode);
176        }       
177        
178        protected synchronized Object setConnectionOpenedAtMark(Integer hashCode,Date date){
179             return CONNECTION_OPENED_AT_MARK.put(hashCode,date);
180        }       
181        
182        public synchronized Map<Integer, Date> getConnectionUsingAtMark(){
183            return new HashMap<Integer, Date>(CONNECTION_USING_AT_MARK);
184        }
185        
186        protected synchronized Object removeConnectionUsingAtMark(Integer hashCode){
187             return CONNECTION_USING_AT_MARK.remove(hashCode);
188        }
189        
190        protected synchronized Object setConnectionUsingAtMark(Integer hashCode,Date date){
191             return CONNECTION_USING_AT_MARK.put(hashCode,date);
192        }
193
194        protected synchronized Map<Integer, Connection> getConnectionMark(){
195            return CONNECTION_MARK;
196        }
197        
198        protected synchronized Connection getConnection(){
199        Thread ct = Thread.currentThread();
200        String tn = ct.getName();
201        long tid = ct.getId();      
202        Integer hashCode = get();
203        
204        if(hashCode == null) return null;
205        
206            CONNECTION_USING_AT_MARK.put(hashCode, new Date());
207        CONNECTION_IDLE_BEGIN_MARK.remove(hashCode);
208        CONNECTION_THREAD_MARK.put(hashCode, String.format("%s-%s", tn, tid));
209            return CONNECTION_MARK.get(hashCode);
210        }       
211        
212        protected synchronized Object removeConnectionMark(Integer hashCode){
213             return CONNECTION_MARK.remove(hashCode);
214        }
215        
216        protected synchronized Object setConnectionMark(Integer hashCode,Connection conn){
217             return CONNECTION_MARK.put(hashCode,conn);
218        }
219        
220        public synchronized Map<Integer, Date> getConnectionIdleBeginMark(){
221            return new HashMap<Integer,Date>(CONNECTION_IDLE_BEGIN_MARK);
222        }
223        
224        protected synchronized Object removeConnectionIdleBeginMark(Integer hashCode){
225             return CONNECTION_IDLE_BEGIN_MARK.remove(hashCode);
226        }
227        
228        protected synchronized Object setConnectionIdleBeginMark(Integer hashCode,Date date){
229             return CONNECTION_IDLE_BEGIN_MARK.put(hashCode,date);
230        }
231        
232        public synchronized Map<Integer, String> getConnectionThreadMark(){
233            return new HashMap<Integer, String>(CONNECTION_THREAD_MARK);
234        }
235
236}