AT&T 2.0.19 Code drop, stage 3
[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 import java.util.logging.Level;
35
36 import org.onap.aaf.misc.env.Env;
37 import org.onap.aaf.misc.env.Trans;
38
39 /**
40  * Create and maintain a Map of Maps used for Caching
41  * 
42  * @author Jonathan
43  *
44  * @param <TRANS>
45  * @param <DATA>
46  */
47 public class Cache<TRANS extends Trans, DATA> {
48         private static Clean clean;
49         private static Timer cleanseTimer;
50
51         public static final String CACHE_HIGH_COUNT = "CACHE_HIGH_COUNT";
52         public static final String CACHE_CLEAN_INTERVAL = "CACHE_CLEAN_INTERVAL";
53 //      public static final String CACHE_MIN_REFRESH_INTERVAL = "CACHE_MIN_REFRESH_INTERVAL";
54
55         private static final Map<String,Map<String,Dated>> cacheMap;
56
57         static {
58                 cacheMap = new HashMap<String,Map<String,Dated>>();
59         }
60
61         /**
62          * Dated Class - store any Data with timestamp
63          * 
64          * @author Jonathan
65          *
66          */
67         public final static class Dated { 
68                 public Date timestamp;
69                 public List<?> data;
70                 private long expireIn;
71                 
72                 public Dated(List<?> data, long expireIn) {
73                         timestamp = new Date(System.currentTimeMillis()+expireIn);
74                         this.data = data;
75                         this.expireIn = expireIn;
76                 }
77
78                 public <T> Dated(T t, long expireIn) {
79                         timestamp = new Date(System.currentTimeMillis()+expireIn);
80                         ArrayList<T> al = new ArrayList<T>(1);
81                         al.add(t);
82                         data = al;
83                         this.expireIn = expireIn;
84                 }
85
86                 public void touch() {
87                         timestamp = new Date(System.currentTimeMillis()+expireIn);
88                 }
89         }
90         
91         public static Map<String,Dated> obtain(String key) {
92                 Map<String, Dated> m = cacheMap.get(key);
93                 if(m==null) {
94                         m = new ConcurrentHashMap<String, Dated>();
95                         synchronized(cacheMap) {
96                                 cacheMap.put(key, m);
97                         }
98                 }
99                 return m;
100         }
101
102         /**
103          * Clean will examine resources, and remove those that have expired.
104          * 
105          * If "highs" have been exceeded, then we'll expire 10% more the next time.  This will adjust after each run
106          * without checking contents more than once, making a good average "high" in the minimum speed.
107          * 
108          * @author Jonathan
109          *
110          */
111         private final static class Clean extends TimerTask {
112                 private final Env env;
113                 private Set<String> set;
114                 
115                 // The idea here is to not be too restrictive on a high, but to Expire more items by 
116                 // shortening the time to expire.  This is done by judiciously incrementing "advance"
117                 // when the "highs" are exceeded.  This effectively reduces numbers of cached items quickly.
118                 private final int high;
119                 private long advance;
120                 private final long timeInterval;
121                 
122                 public Clean(Env env, long cleanInterval, int highCount) {
123                         this.env = env;
124                         high = highCount;
125                         timeInterval = cleanInterval;
126                         advance = 0;
127                         set = new HashSet<String>();
128                 }
129                 
130                 public synchronized void add(String key) {
131                         set.add(key);
132                 }
133
134                 public void run() {
135                         int count = 0;
136                         int total = 0;
137                         // look at now.  If we need to expire more by increasing "now" by "advance"
138                         Date now = new Date(System.currentTimeMillis() + advance);
139                         
140                         
141                         for(String name : set) {
142                                 Map<String,Dated> map = cacheMap.get(name);
143                                 if(map!=null) for(Map.Entry<String,Dated> me : map.entrySet()) {
144                                         ++total;
145                                         if(me.getValue().timestamp.before(now)) {
146                                                 map.remove(me.getKey());
147                                                 ++count;
148                                         }
149                                 }
150 //                              if(count>0) {
151 //                                      env.info().log(Level.INFO, "Cache removed",count,"expired",name,"Elements");
152 //                              }
153                         }
154                         
155                         if(count>0) {
156                                 env.info().log(Level.INFO, "Cache removed",count,"expired Cached Elements out of", total);
157                         }
158
159                         // If High (total) is reached during this period, increase the number of expired services removed for next time.
160                         // There's no point doing it again here, as there should have been cleaned items.
161                         if(total>high) {
162                                 // advance cleanup by 10%, without getting greater than timeInterval.
163                                 advance = Math.min(timeInterval, advance+(timeInterval/10));
164                         } else {
165                                 // reduce advance by 10%, without getting lower than 0.
166                                 advance = Math.max(0, advance-(timeInterval/10));
167                         }
168                 }
169         }
170
171         public static synchronized void startCleansing(Env env, String ... keys) {
172                 if(cleanseTimer==null) {
173                         cleanseTimer = new Timer("Cache Cleanup Timer");
174                         int cleanInterval = Integer.parseInt(env.getProperty(CACHE_CLEAN_INTERVAL,"60000")); // 1 minute clean cycles 
175                         int highCount = Integer.parseInt(env.getProperty(CACHE_HIGH_COUNT,"5000"));
176                         cleanseTimer.schedule(clean = new Clean(env, cleanInterval, highCount), cleanInterval, cleanInterval);
177                 }
178                 
179                 for(String key : keys) {
180                         clean.add(key);
181                 }
182         }
183
184         public static void stopTimer() {
185                 if(cleanseTimer!=null) {
186                         cleanseTimer.cancel();
187                         cleanseTimer = null;
188                 }
189         }
190
191         public static void addShutdownHook() {
192                 Runtime.getRuntime().addShutdownHook(new Thread() {
193                         @Override
194                         public void run() {
195                                 Cache.stopTimer();
196                         }
197                 }); 
198         }
199
200 }