AT&T 2.0.19 Code drop, stage 3
[aaf/authz.git] / auth / auth-cass / src / main / java / org / onap / aaf / auth / dao / Cached.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.dao;
23
24 import java.util.Date;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Timer;
28 import java.util.TimerTask;
29
30 import org.onap.aaf.auth.cache.Cache;
31 import org.onap.aaf.auth.dao.cass.Status;
32 import org.onap.aaf.auth.env.AuthzEnv;
33 import org.onap.aaf.auth.env.AuthzTrans;
34 import org.onap.aaf.auth.layer.Result;
35 import org.onap.aaf.misc.env.Env;
36 import org.onap.aaf.misc.env.Trans;
37
38 public class Cached<TRANS extends Trans, DATA extends Cacheable> extends Cache<TRANS,DATA> {
39         // Java does not allow creation of Arrays with Generics in them...
40         // private Map<String,Dated> cache[];
41         protected final CIDAO<TRANS> info;
42         
43         private static Timer infoTimer;
44         private Object cache[];
45         public final int segSize;
46
47         protected final String name;
48
49         private final long expireIn;
50         
51
52
53         // Taken from String Hash, but coded, to ensure consistent across Java versions.  Also covers negative case;
54         public int cacheIdx(String key) {
55                 int h = 0;
56                 for (int i = 0; i < key.length(); i++) {
57                     h = 31*h + key.charAt(i);
58                 }
59                 if(h<0)h*=-1;
60                 return h%segSize;
61         }
62         
63         public Cached(CIDAO<TRANS> info, String name, int segSize, long expireIn) {
64                 this.name =name;
65                 this.segSize = segSize;
66                 this.info = info;
67                 this.expireIn = expireIn;
68                 cache = new Object[segSize];
69                 // Create a new Map for each Segment, and store locally
70                 for(int i=0;i<segSize;++i) {
71                         cache[i]=obtain(name+i);
72                 }
73         }
74         
75         public void add(String key, List<DATA> data) {
76                 @SuppressWarnings("unchecked")
77                 Map<String,Dated> map = ((Map<String,Dated>)cache[cacheIdx(key)]);
78                 map.put(key, new Dated(data, expireIn));
79         }
80
81
82         public int invalidate(String key)  {
83                 int cacheIdx = cacheIdx(key);
84                 @SuppressWarnings("unchecked")
85                 Map<String,Dated> map = ((Map<String,Dated>)cache[cacheIdx]);
86 //              if(map.remove(key)!=null) // Not seeming to remove all the time
87                 if(map!=null)map.clear();
88 //                      System.err.println("Remove " + name + " " + key);
89                 return cacheIdx;
90         }
91
92         public Result<Void> invalidate(int segment)  {
93                 if(segment<0 || segment>=cache.length) return Result.err(Status.ERR_BadData,"Cache Segment %s is out of range",Integer.toString(segment));
94                 @SuppressWarnings("unchecked")
95                 Map<String,Dated> map = ((Map<String,Dated>)cache[segment]);
96                 if(map!=null) {
97                         map.clear();
98                 }
99                 return Result.ok();
100         }
101
102         protected interface Getter<D> {
103                 public abstract Result<List<D>> get();
104         };
105         
106         // TODO utilize Segmented Caches, and fold "get" into "reads"
107         @SuppressWarnings("unchecked")
108         public Result<List<DATA>> get(TRANS trans, String key, Getter<DATA> getter) {
109                 List<DATA> ld = null;
110                 Result<List<DATA>> rld = null;
111                 
112                 int cacheIdx = cacheIdx(key);
113                 Map<String, Dated> map = ((Map<String,Dated>)cache[cacheIdx]);
114                 
115                 // Check for saved element in cache
116                 Dated cached = map.get(key);
117                 // Note: These Segment Timestamps are kept up to date with DB
118                 Date dbStamp = info.get(trans, name,cacheIdx);
119                 
120                 // Check for cache Entry and whether it is still good (a good Cache Entry is same or after DBEntry, so we use "before" syntax)
121                 if(cached!=null && dbStamp.before(cached.timestamp)) {
122                         ld = (List<DATA>)cached.data;
123                         rld = Result.ok(ld);
124                 } else {
125                         rld = getter.get();
126                         if(rld.isOK()) { // only store valid lists
127                                 map.put(key, new Dated(rld.value,expireIn));  // successful item found gets put in cache
128 //                      } else if(rld.status == Result.ERR_Backend){
129 //                              map.remove(key);
130                         }
131                 }
132                 return rld;
133         }
134
135         /**
136          * Each Cached object has multiple Segments that need cleaning.  Derive each, and add to Cleansing Thread
137          * @param env
138          * @param dao
139          */
140         public static void startCleansing(AuthzEnv env, CachedDAO<?,?,?> ... dao) {
141                 for(CachedDAO<?,?,?> d : dao) {  
142                         for(int i=0;i<d.segSize;++i) {
143                                 startCleansing(env, d.table()+i);
144                         }
145                 }
146         }
147
148
149         public static<T extends Trans> void startRefresh(AuthzEnv env, CIDAO<AuthzTrans> cidao) {
150                 if(infoTimer==null) {
151                         infoTimer = new Timer("CachedDAO Info Refresh Timer");
152                         int minRefresh = 10*1000*60; // 10 mins Integer.parseInt(env.getProperty(CACHE_MIN_REFRESH_INTERVAL,"2000")); // 2 second minimum refresh 
153                         infoTimer.schedule(new Refresh(env,cidao, minRefresh), 1000, minRefresh); // note: Refresh from DB immediately
154                 }
155         }
156         
157         public static void stopTimer() {
158                 Cache.stopTimer();
159                 if(infoTimer!=null) {
160                         infoTimer.cancel();
161                         infoTimer = null;
162                 }
163         }
164         
165         private final static class Refresh extends TimerTask {
166                 private static final int maxRefresh = 2*60*10000; // 20 mins
167                 private AuthzEnv env;
168                 private CIDAO<AuthzTrans> cidao;
169                 private int minRefresh;
170                 private long lastRun;
171                 
172                 public Refresh(AuthzEnv env, CIDAO<AuthzTrans> cidao, int minRefresh) {
173                         this.env = env;
174                         this.cidao = cidao;
175                         this.minRefresh = minRefresh;
176                         lastRun = System.currentTimeMillis()-maxRefresh-1000;
177                 }
178                 
179                 @Override
180                 public void run() {
181                         // Evaluate whether to refresh based on transaction rate
182                         long now = System.currentTimeMillis();
183                         long interval = now-lastRun;
184
185                         if(interval < minRefresh || interval < Math.min(env.transRate(),maxRefresh)) return;
186                         lastRun = now;
187                         AuthzTrans trans = env.newTransNoAvg();
188                         Result<Void> rv = cidao.check(trans);
189                         if(rv.status!=Result.OK) {
190                                 env.error().log("Error in CacheInfo Refresh",rv.details);
191                         }
192                         if(env.debug().isLoggable()) {
193                                 StringBuilder sb = new StringBuilder("Cache Info Refresh: ");
194                                 trans.auditTrail(0, sb, Env.REMOTE);
195                                 env.debug().log(sb);
196                         }
197                 }
198         }
199 }