Sonar Fixes, Formatting
[aaf/authz.git] / auth / auth-core / src / main / java / org / onap / aaf / auth / cache / Cache.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.auth.cache;
23
24 import java.util.ArrayList;
25 import java.util.Date;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.Timer;
32 import java.util.TimerTask;
33 import java.util.concurrent.ConcurrentHashMap;
34
35 import org.onap.aaf.misc.env.Env;
36 import org.onap.aaf.misc.env.Trans;
37
38 /**
39  * Create and maintain a Map of Maps used for Caching
40  *
41  * @author Jonathan
42  *
43  * @param <TRANS>
44  * @param <DATA>
45  */
46 public class Cache<TRANS extends Trans, DATA> {
47     private static Clean clean;
48     private static Timer cleanseTimer;
49
50     public static final String CACHE_HIGH_COUNT = "CACHE_HIGH_COUNT";
51     public static final String CACHE_CLEAN_INTERVAL = "CACHE_CLEAN_INTERVAL";
52
53     private static final Map<String,Map<String,Dated>> cacheMap;
54
55     static {
56         cacheMap = new HashMap<>();
57     }
58
59     /**
60      * Dated Class - store any Data with timestamp
61      *
62      * @author Jonathan
63      *
64      */
65     public static final class Dated {
66         public Date timestamp;
67         public List<?> data;
68         private long expireIn;
69
70         public Dated(List<?> data, long expireIn) {
71             timestamp = new Date(System.currentTimeMillis()+expireIn);
72             this.data = data;
73             this.expireIn = expireIn;
74         }
75
76         public <T> Dated(T t, long expireIn) {
77             timestamp = new Date(System.currentTimeMillis()+expireIn);
78             ArrayList<T> al = new ArrayList<>(1);
79             al.add(t);
80             data = al;
81             this.expireIn = expireIn;
82         }
83
84         public void touch() {
85             timestamp = new Date(System.currentTimeMillis()+expireIn);
86         }
87     }
88
89     public static Map<String,Dated> obtain(String key) {
90         Map<String, Dated> m = cacheMap.get(key);
91         if (m==null) {
92             m = new ConcurrentHashMap<>();
93             synchronized(cacheMap) {
94                 cacheMap.put(key, m);
95             }
96         }
97         return m;
98     }
99
100     /**
101      * Clean will examine resources, and remove those that have expired.
102      *
103      * If "highs" have been exceeded, then we'll expire 10% more the next time.  This will adjust after each run
104      * without checking contents more than once, making a good average "high" in the minimum speed.
105      *
106      * @author Jonathan
107      *
108      */
109     private static final class Clean extends TimerTask {
110         private final Env env;
111         private Set<String> set;
112
113         // The idea here is to not be too restrictive on a high, but to Expire more items by
114         // shortening the time to expire.  This is done by judiciously incrementing "advance"
115         // when the "highs" are exceeded.  This effectively reduces numbers of cached items quickly.
116         private final int high;
117         private long advance;
118         private final long timeInterval;
119
120         public Clean(Env env, long cleanInterval, int highCount) {
121             this.env = env;
122             high = highCount;
123             timeInterval = cleanInterval;
124             advance = 0;
125             set = new HashSet<>();
126         }
127
128         public synchronized void add(String key) {
129             set.add(key);
130         }
131
132         public void run() {
133             int count = 0;
134             int total = 0;
135             // look at now.  If we need to expire more by increasing "now" by "advance"
136             Date now = new Date(System.currentTimeMillis() + advance);
137
138
139             for (String name : set) {
140                 Map<String,Dated> map = cacheMap.get(name);
141                 if (map==null) {
142                     continue;
143                 }
144
145                 for (Map.Entry<String,Dated> me : map.entrySet()) {
146                     ++total;
147                     if (me.getValue().timestamp.before(now)) {
148                         map.remove(me.getKey());
149                         ++count;
150                     }
151                 }
152             }
153
154             if (count>0) {
155                 env.debug().log("Cache removed",count,"expired Cached Elements out of", total);
156             }
157
158             // If High (total) is reached during this period, increase the number of expired services removed for next time.
159             // There's no point doing it again here, as there should have been cleaned items.
160             if (total>high) {
161                 // advance cleanup by 10%, without getting greater than timeInterval.
162                 advance = Math.min(timeInterval, advance+(timeInterval/10));
163             } else {
164                 // reduce advance by 10%, without getting lower than 0.
165                 advance = Math.max(0, advance-(timeInterval/10));
166             }
167         }
168     }
169
170     public static synchronized void startCleansing(Env env, String ... keys) {
171         if (cleanseTimer==null) {
172             cleanseTimer = new Timer("Cache Cleanup Timer");
173             int cleanInterval = Integer.parseInt(env.getProperty(CACHE_CLEAN_INTERVAL,"60000")); // 1 minute clean cycles
174             int highCount = Integer.parseInt(env.getProperty(CACHE_HIGH_COUNT,"5000"));
175             cleanseTimer.schedule(clean = new Clean(env, cleanInterval, highCount), cleanInterval, cleanInterval);
176         }
177
178         for (String key : keys) {
179             clean.add(key);
180         }
181     }
182
183     public static void stopTimer() {
184         if (cleanseTimer!=null) {
185             cleanseTimer.cancel();
186             cleanseTimer = null;
187         }
188     }
189
190     public static void addShutdownHook() {
191         Runtime.getRuntime().addShutdownHook(new Thread() {
192             @Override
193             public void run() {
194                 Cache.stopTimer();
195             }
196         });
197     }
198
199 }