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.cache;
025
026import com.killcoding.cache.AbsCacheApi;
027import com.killcoding.tool.CommonTools;
028import java.util.Date;
029import java.util.Map;
030import java.util.concurrent.ConcurrentHashMap;
031import java.nio.file.Files;
032import java.io.File;
033import java.nio.file.Path;
034import java.util.stream.Stream;
035import java.util.Comparator;
036import java.nio.file.Paths;
037import com.killcoding.log.Logger;
038import com.killcoding.log.LoggerFactory;
039import com.killcoding.tool.FileTools;
040import java.util.Set;
041import java.util.Arrays;
042import java.util.List;
043import java.util.ArrayList;
044import java.util.concurrent.ExecutorService;
045import java.util.concurrent.Executors;
046import java.util.concurrent.Future;
047import java.text.SimpleDateFormat;
048
049/**
050 * This cache class is save to memory
051 * */
052public class MemoryCache extends AbsCacheApi {
053
054        private static final Logger log = LoggerFactory.getLogger(MemoryCache.class);
055
056        private final long PID = ProcessHandle.current().pid();
057
058        private final static Map<String, Object> CACHE_VALUE = new ConcurrentHashMap<String, Object>();
059        private final static Map<String, Date> CACHE_LIFE = new ConcurrentHashMap<String, Date>();
060
061        private static boolean isStartRemoveExpiredThread = false;
062        private static ExecutorService executor = null;
063        
064    /**
065    * New a MemoryCache object
066    * */
067        public MemoryCache() {
068                super();
069        }
070
071    /**
072    * New a MemoryCache object
073    * @param stored - 'true' is force write cache
074    * */
075        public MemoryCache(Boolean stored) {
076                super();
077                this.stored = stored;
078        }
079
080    /**
081     * Start remove expired thread
082     * */
083        private synchronized void startRemoveExpiredThread() {
084                isStartRemoveExpiredThread = true;
085                executor = Executors.newFixedThreadPool(1);
086                executor.execute(new Runnable() {
087                        @Override
088                        public void run() {
089                                Thread.currentThread().setName("MemoryCache");
090                                while (true) {
091
092                                        if (Thread.currentThread().isInterrupted()) {
093                                                break;
094                                        }
095
096                                        Set<String> names = CACHE_LIFE.keySet();
097                                        for (String name : names) {
098                                                Date current = new Date();
099                                                Date life = CACHE_LIFE.get(name);
100                                                if (life != null) {
101                                                if (current.getTime() > life.getTime()) {
102                                                        remove(name);
103                                                        log.debug("Removed(Expired) -> {}", name);
104                                                }
105                                                }
106                                        }
107                                        try {
108                                                Thread.sleep(TIMER);
109                                        } catch (InterruptedException e) {
110                                                log.warn(e);
111                                                Thread.currentThread().interrupted();
112                                        }
113                                }
114                        }
115
116                });
117        }
118
119        @Override
120        synchronized boolean included(String name) {
121                if (stored) {
122                        return CACHE_VALUE.containsKey(name);
123                }else{
124                        if (CACHE_LIFE.containsKey(name)) {
125                                Date current = new Date();
126                                Date life = CACHE_LIFE.get(name);
127                                if (life != null) {
128                                if (current.getTime() > life.getTime()) {
129                                        return false;
130                                } else {
131                                        return true;
132                                }
133                                }
134                                return false;
135                        } else {
136                                return false;
137                        }
138                }
139        }
140
141        @Override
142        synchronized List<String> getNames() {
143                Set<String> names = CACHE_VALUE.keySet();
144                if (names != null) {
145                        return new ArrayList<String>(names);
146                } else {
147                        return null;
148                }
149        }
150
151        @Override
152        synchronized void removeAll() {
153                Set<String> names = CACHE_VALUE.keySet();
154                for (String name : names) {
155                        remove(name);
156                }
157        }
158
159        @Override
160        synchronized void remove(String name) {
161                if (CACHE_VALUE.containsKey(name)) {
162                        CACHE_LIFE.remove(name);
163                        CACHE_VALUE.remove(name);
164                }
165        }
166
167        @Override
168        synchronized Object get(String name) {
169                try {
170                        if (included(name)) {
171                                Object cacheObjectValue = CACHE_VALUE.get(name);
172                                return cacheObjectValue;
173                        } else {
174                                return null;
175                        }
176                } catch (Exception e) {
177                        log.warn(e);
178                        return null;
179                }
180        }
181
182        @Override
183        synchronized void set(String name, Object value, int lifeSeconds) {
184            if(value != null) {
185            
186            if(lifeSeconds <= 0){
187                log.debug("Skip use cache, key '{}' life is '{}s'",name,lifeSeconds);
188                return;    
189            }
190            
191                try {
192                        CACHE_VALUE.put(name, value);
193                        if(!stored){
194                            Date life = new Date(new Date().getTime() + lifeSeconds * 1000);
195                            CACHE_LIFE.put(name, life);
196                    }
197                } catch (Exception e) {
198                        log.warn(e);
199                }
200    
201                if (!isStartRemoveExpiredThread)
202                        startRemoveExpiredThread();
203                        
204            }
205        }
206
207}