1888b3ace6f697a71b02ee0b41024972cd185374
[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!=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 }