* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/**
* Implement Fast lookup and Cache for Local User Info
- *
+ *
* Include ability to add and remove Users
- *
+ *
* Also includes a Timer Thread (when necessary) to invoke cleanup on expiring Credentials
- *
+ *
* @author Jonathan
*
*/
private final Map<String, User<PERM>> userMap;
private static final Map<String, Miss> missMap = new TreeMap<>();
private final Symm missEncrypt;
-
+
private Clean clean;
protected Access access;
-
+
protected AbsUserCache(Access access, long cleanInterval, int highCount, int usageCount) {
this.access = access;
Symm s;
s = Symm.base64noSplit;
}
missEncrypt = s;
-
+
userMap = new ConcurrentHashMap<>();
-
- if(cleanInterval>0) {
+
+ if (cleanInterval>0) {
cleanInterval = Math.max(MIN_INTERVAL, cleanInterval);
synchronized(AbsUserCache.class) { // Lazy instantiate.. in case there is no cleanup needed
- if(timer==null) {
+ if (timer==null) {
timer = new Timer("CADI Cleanup Timer",true);
}
-
+
timer.schedule(clean = new Clean(access, cleanInterval, highCount, usageCount), cleanInterval, cleanInterval);
access.log(Access.Level.INIT, "Cleaning Thread initialized with interval of",cleanInterval, "ms and max objects of", highCount);
}
}
}
-
+
@SuppressWarnings("unchecked")
public AbsUserCache(AbsUserCache<PERM> cache) {
this.access = cache.access;
userMap = cache.userMap;
missEncrypt = cache.missEncrypt;
-
+
synchronized(AbsUserCache.class) {
- if(cache.clean!=null && cache.clean.lur==null && this instanceof CachingLur) {
+ if (cache.clean!=null && cache.clean.lur==null && this instanceof CachingLur) {
cache.clean.lur=(CachingLur<PERM>)this;
}
}
}
protected void setLur(CachingLur<PERM> lur) {
- if(clean!=null)clean.lur = lur;
-
+ if (clean!=null)clean.lur = lur;
+
}
-
+
protected void addUser(User<PERM> user) {
Principal p = user.principal;
String key;
try {
- if(p instanceof GetCred) {
+ if (p instanceof GetCred) {
key = missKey(p.getName(), ((GetCred)p).getCred());
} else {
byte[] cred;
- if((cred=user.getCred())==null) {
+ if ((cred=user.getCred())==null) {
key = user.name + NO_CRED;
} else {
key = missKey(user.name,cred);
protected void addUser(String key, User<PERM> user) {
userMap.put(key, user);
}
-
+
/**
* Add miss to missMap. If Miss exists, or too many tries, returns false.
- *
+ *
* otherwise, returns true to allow another attempt.
- *
+ *
* @param key
* @param bs
* @return
- * @throws IOException
+ * @throws IOException
*/
protected synchronized boolean addMiss(String key, byte[] bs) {
String mkey;
return false;
}
Miss miss = missMap.get(mkey);
- if(miss==null) {
+ if (miss==null) {
missMap.put(mkey, new Miss(bs,clean==null?MIN_INTERVAL:clean.timeInterval,key));
return true;
}
- return miss.mayContinue();
+ return miss.mayContinue();
}
protected Miss missed(String key, byte[] bs) throws IOException {
protected User<PERM> getUser(Principal principal) {
String key;
- if(principal instanceof GetCred) {
+ if (principal instanceof GetCred) {
GetCred gc = (GetCred)principal;
try {
key = missKey(principal.getName(), gc.getCred());
key = principal.getName()+NO_CRED;
}
User<PERM> u = userMap.get(key);
- if(u!=null) {
+ if (u!=null) {
u.incCount();
}
return u;
}
-
+
protected User<PERM> getUser(CachedBasicPrincipal cbp) {
return getUser(cbp.getName(), cbp.getCred());
}
-
+
protected User<PERM> getUser(String user, byte[] cred) {
User<PERM> u;
String key=null;
return null;
}
u = userMap.get(key);
- if(u!=null) {
- if(u.permExpired()) {
+ if (u!=null) {
+ if (u.permExpired()) {
userMap.remove(key);
u=null;
} else {
}
return u;
}
-
+
/**
* Removes User from the Cache
* @param user
protected void remove(User<PERM> user) {
userMap.remove(user.principal.getName());
}
-
+
/**
* Removes user from the Cache
- *
+ *
* @param user
*/
public void remove(String user) {
Object o = userMap.remove(user);
- if(o!=null) {
+ if (o!=null) {
access.log(Level.INFO, user,"removed from Client Cache by Request");
}
}
-
+
/**
* Clear all Users from the Client Cache
*/
public void clearAll() {
userMap.clear();
}
-
+
public final List<DumpInfo> dumpInfo() {
List<DumpInfo> rv = new ArrayList<>();
- for(User<PERM> user : userMap.values()) {
+ for (User<PERM> user : userMap.values()) {
rv.add(new DumpInfo(user));
}
return rv;
public boolean handlesExclusively(Permission ... pond) {
return false;
}
-
+
/**
- * Container calls when cleaning up...
- *
+ * Container calls when cleaning up...
+ *
* If overloading in Derived class, be sure to call "super.destroy()"
*/
public void destroy() {
- if(timer!=null) {
+ if (timer!=null) {
timer.purge();
timer.cancel();
}
}
-
-
+
+
// Simple map of Group name to a set of User Names
// private Map<String, Set<String>> groupMap = new HashMap<>();
public final class DumpInfo {
public String user;
public List<String> perms;
-
+
public DumpInfo(User<PERM> user) {
this.user = user.principal.getName();
perms = new ArrayList<>(user.perms.keySet());
}
}
-
+
/**
* Clean will examine resources, and remove those that have expired.
- *
+ *
* If "highs" have been exceeded, then we'll expire 10% more the next time. This will adjust after each run
* without checking contents more than once, making a good average "high" in the minimum speed.
- *
+ *
* @author Jonathan
*
*/
private final class Clean extends TimerTask {
private final Access access;
private CachingLur<PERM> lur;
-
- // The idea here is to not be too restrictive on a high, but to Expire more items by
+
+ // The idea here is to not be too restrictive on a high, but to Expire more items by
// shortening the time to expire. This is done by judiciously incrementing "advance"
// when the "highs" are exceeded. This effectively reduces numbers of cached items quickly.
private final int high;
private long advance;
private final long timeInterval;
private final int usageTriggerCount;
-
+
public Clean(Access access, long cleanInterval, int highCount, int usageTriggerCount) {
this.access = access;
lur = null;
ArrayList<User<PERM>> al = new ArrayList<>(userMap.values().size());
al.addAll(0, userMap.values());
long now = System.currentTimeMillis() + advance;
- for(User<PERM> user : al) {
+ for (User<PERM> user : al) {
++total;
- if(user.count>usageTriggerCount) {
+ if (user.count>usageTriggerCount) {
boolean touched = false, removed=false;
- if(user.principal instanceof CachedPrincipal) {
+ if (user.principal instanceof CachedPrincipal) {
CachedPrincipal cp = (CachedPrincipal)user.principal;
- if(cp.expires() < now) {
+ if (cp.expires() < now) {
switch(cp.revalidate(null)) {
case INACCESSIBLE:
access.log(Level.AUDIT, "AAF Inaccessible. Keeping credentials");
}
}
}
-
- if(!removed && lur!=null && user.permExpires<= now ) {
- if(lur.reload(user).equals(Resp.REVALIDATED)) {
+
+ if (!removed && lur!=null && user.permExpires<= now ) {
+ if (lur.reload(user).equals(Resp.REVALIDATED)) {
user.renewPerm();
access.log(Level.DEBUG, "Reloaded Perms for",user);
touched = true;
}
}
user.resetCount();
- if(touched) {
+ if (touched) {
++renewed;
}
-
+
} else {
- if(user.permExpired()) {
+ if (user.permExpired()) {
remove(user);
++count;
}
}
}
-
+
// Clean out Misses
int missTotal = missMap.keySet().size();
int miss = 0;
- if(missTotal>0) {
+ if (missTotal>0) {
ArrayList<String> keys = new ArrayList<>(missTotal);
keys.addAll(missMap.keySet());
- for(String key : keys) {
+ for (String key : keys) {
Miss m = missMap.get(key);
- if(m!=null) {
+ if (m!=null) {
long timeLeft = m.timestamp - System.currentTimeMillis();
- if(timeLeft<0) {
+ if (timeLeft<0) {
synchronized(missMap) {
missMap.remove(key);
}
}
}
}
-
- if(count+renewed+miss>0) {
+
+ if (count+renewed+miss>0) {
access.log(Level.INFO, (lur==null?"Cache":lur.getClass().getSimpleName()), "removed",count,
"and renewed",renewed,"expired Permissions out of", total,"and removed", miss, "password misses out of",missTotal);
}
-
+
// If High (total) is reached during this period, increase the number of expired services removed for next time.
// There's no point doing it again here, as there should have been cleaned items.
- if(total>high) {
+ if (total>high) {
// advance cleanup by 10%, without getting greater than timeInterval.
advance = Math.min(timeInterval, advance+(timeInterval/10));
} else {
private long tries;
private final String name;
-
+
public Miss(final byte[] first, final long timeInterval, final String name) {
timestamp = System.currentTimeMillis() + timeInterval;
this.timetolive = timeInterval;
tries = 0L;
this.name = name;
}
-
-
+
+
public synchronized boolean mayContinue() {
- long ts = System.currentTimeMillis();
- if(ts>timestamp) {
+ long ts = System.currentTimeMillis();
+ if (ts>timestamp) {
tries = 0;
timestamp = ts + timetolive;
- } else if(MAX_TRIES <= ++tries) {
+ } else if (MAX_TRIES <= ++tries) {
return false;
}
return true;
}
-
+
}
-
+
/**
* Report on state
*/
public String toString() {
- return getClass().getSimpleName() +
+ return getClass().getSimpleName() +
" Cache:\n Users Cached: " +
userMap.size() +
"\n Misses Saved: " +
missMap.size() +
'\n';
-
+
}
public void clear(Principal p, StringBuilder sb) {