package railo.runtime.type.scope.storage; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import railo.commons.lang.RandomUtil; import railo.commons.lang.SizeOf; import railo.commons.lang.StringUtil; import railo.runtime.PageContext; import railo.runtime.config.Config; import railo.runtime.dump.DumpData; import railo.runtime.dump.DumpProperties; import railo.runtime.engine.ThreadLocalPageContext; import railo.runtime.exp.PageException; import railo.runtime.listener.ApplicationContext; import railo.runtime.op.Duplicator; import railo.runtime.type.Collection; import railo.runtime.type.Sizeable; import railo.runtime.type.Struct; import railo.runtime.type.StructImpl; import railo.runtime.type.dt.DateTime; import railo.runtime.type.dt.DateTimeImpl; import railo.runtime.type.util.CollectionUtil; import railo.runtime.type.util.KeyConstants; import railo.runtime.type.util.StructSupport; import railo.runtime.type.util.StructUtil; public abstract class StorageScopeImpl extends StructSupport implements StorageScope,Sizeable { public static Collection.Key CFID=KeyConstants._cfid; public static Collection.Key CFTOKEN=KeyConstants._cftoken; public static Collection.Key URLTOKEN=KeyConstants._urltoken; public static Collection.Key LASTVISIT=KeyConstants._lastvisit; public static Collection.Key HITCOUNT=KeyConstants._hitcount; public static Collection.Key TIMECREATED=KeyConstants._timecreated; public static Collection.Key SESSION_ID=KeyConstants._sessionid; private static int _id=0; private int id=0; private static final long serialVersionUID = 7874930250042576053L; private static Set<Collection.Key> FIX_KEYS=new HashSet<Collection.Key>(); static { FIX_KEYS.add(CFID); FIX_KEYS.add(CFTOKEN); FIX_KEYS.add(URLTOKEN); FIX_KEYS.add(LASTVISIT); FIX_KEYS.add(HITCOUNT); FIX_KEYS.add(TIMECREATED); } protected static Set<Collection.Key> ignoreSet=new HashSet<Collection.Key>(); static { ignoreSet.add(CFID); ignoreSet.add(CFTOKEN); ignoreSet.add(URLTOKEN); } protected boolean isinit=true; protected Struct sct; protected long lastvisit; protected DateTime _lastvisit; protected int hitcount=0; protected DateTime timecreated; private boolean hasChanges=false; private String strType; private int type; private long timeSpan=-1; private String storage; private Map<String, String> tokens; /** * Constructor of the class * @param sct * @param timecreated * @param _lastvisit * @param lastvisit * @param hitcount */ public StorageScopeImpl(Struct sct, DateTime timecreated, DateTime _lastvisit, long lastvisit, int hitcount,String strType,int type) { this.sct=sct; this.timecreated=timecreated; if(_lastvisit==null) this._lastvisit=timecreated; else this._lastvisit=_lastvisit; if(lastvisit==-1) this.lastvisit=this._lastvisit.getTime(); else this.lastvisit=lastvisit; this.hitcount=hitcount; this.strType=strType; this.type=type; id=++_id; } /** * Constructor of the class * @param other * @param deepCopy */ public StorageScopeImpl(StorageScopeImpl other, boolean deepCopy) { this.sct=(Struct)Duplicator.duplicate(other.sct,deepCopy); this.timecreated=other.timecreated; this._lastvisit=other._lastvisit; this.hitcount=other.hitcount; this.isinit=other.isinit; this.lastvisit=other.lastvisit; this.strType=other.strType; this.type=other.type; this.timeSpan=other.timeSpan; id=++_id; } @Override public void touchBeforeRequest(PageContext pc) { hasChanges=false; setTimeSpan(pc); //lastvisit=System.currentTimeMillis(); if(sct==null) sct=new StructImpl(); sct.setEL(KeyConstants._cfid, pc.getCFID()); sct.setEL(KeyConstants._cftoken, pc.getCFToken()); sct.setEL(URLTOKEN, pc.getURLToken()); sct.setEL(LASTVISIT, _lastvisit); _lastvisit=new DateTimeImpl(pc.getConfig()); lastvisit=System.currentTimeMillis(); if(type==SCOPE_CLIENT){ sct.setEL(HITCOUNT, new Double(hitcount++)); } else { sct.setEL(SESSION_ID, pc.getApplicationContext().getName()+"_"+pc.getCFID()+"_"+pc.getCFToken()); } sct.setEL(TIMECREATED, timecreated); } public void resetEnv(PageContext pc){ _lastvisit=new DateTimeImpl(pc.getConfig()); timecreated=new DateTimeImpl(pc.getConfig()); touchBeforeRequest(pc); } void setTimeSpan(PageContext pc) { ApplicationContext ac=pc.getApplicationContext(); this.timeSpan=getType()==SCOPE_SESSION? ac.getSessionTimeout().getMillis(): ac.getClientTimeout().getMillis(); } @Override public void setMaxInactiveInterval(int interval) { this.timeSpan=interval*1000L; } @Override public int getMaxInactiveInterval() { return (int)(this.timeSpan/1000L); } @Override public final boolean isInitalized() { return isinit; } @Override public final void initialize(PageContext pc) { // StorageScopes need only request initialisation no global init, they are not reused; } @Override public void touchAfterRequest(PageContext pc) { sct.setEL(LASTVISIT, _lastvisit); sct.setEL(TIMECREATED, timecreated); if(type==SCOPE_CLIENT){ sct.setEL(HITCOUNT, new Double(hitcount)); } } @Override public final void release() { clear(); isinit=false; } @Override public final void release(PageContext pc) { clear(); isinit=false; } /** * @return returns if the scope is empty or not, this method ignore the "constant" entries of the scope (cfid,cftoken,urltoken) */ public boolean hasContent() { if(sct.size()==(type==SCOPE_CLIENT?6:5) && sct.containsKey(URLTOKEN) && sct.containsKey(KeyConstants._cftoken) && sct.containsKey(KeyConstants._cfid)) { return false; } return true; } @Override public void clear() { sct.clear(); } @Override public boolean containsKey(Key key) { return sct.containsKey(key); } @Override public Object get(Key key) throws PageException { return sct.get(key); } @Override public Object get(Key key, Object defaultValue) { return sct.get(key, defaultValue); } @Override public Iterator<Collection.Key> keyIterator() { return sct.keyIterator(); } @Override public Iterator<String> keysAsStringIterator() { return sct.keysAsStringIterator(); } @Override public Iterator<Entry<Key, Object>> entryIterator() { return sct.entryIterator(); } @Override public Iterator<Object> valueIterator() { return sct.valueIterator(); } @Override public railo.runtime.type.Collection.Key[] keys() { return CollectionUtil.keys(this); } @Override public Object remove(Key key) throws PageException { hasChanges=true; return sct.remove(key); } @Override public Object removeEL(Key key) { hasChanges=true; return sct.removeEL(key); } @Override public Object set(Key key, Object value) throws PageException { hasChanges=true; return sct.set(key, value); } @Override public Object setEL(Key key, Object value) { hasChanges=true; return sct.setEL(key, value); } @Override public int size() { return sct.size(); } @Override public boolean castToBooleanValue() throws PageException { return sct.castToBooleanValue(); } @Override public Boolean castToBoolean(Boolean defaultValue) { return sct.castToBoolean(defaultValue); } @Override public DateTime castToDateTime() throws PageException { return sct.castToDateTime(); } @Override public DateTime castToDateTime(DateTime defaultValue) { return sct.castToDateTime(defaultValue); } @Override public double castToDoubleValue() throws PageException { return sct.castToDoubleValue(); } @Override public double castToDoubleValue(double defaultValue) { return sct.castToDoubleValue(defaultValue); } @Override public String castToString() throws PageException { return sct.castToString(); } @Override public String castToString(String defaultValue) { return sct.castToString(defaultValue); } @Override public int compareTo(boolean b) throws PageException { return sct.compareTo(b); } @Override public int compareTo(DateTime dt) throws PageException { return sct.compareTo(dt); } @Override public int compareTo(double d) throws PageException { return sct.compareTo(d); } @Override public int compareTo(String str) throws PageException { return sct.compareTo(str); } @Override public long lastVisit() { return lastvisit; } public Collection.Key[] pureKeys() { List<Collection.Key> keys=new ArrayList<Collection.Key>(); Iterator<Key> it = keyIterator(); Collection.Key key; while(it.hasNext()){ key=it.next(); if(!FIX_KEYS.contains(key))keys.add(key); } return keys.toArray(new Collection.Key[keys.size()]); } @Override public void store(Config config){ //do nothing } @Override public void unstore(Config config){ //do nothing } /** * @return the hasChanges */ public boolean hasChanges() { return hasChanges; } @Override public boolean containsValue(Object value) { return sct.containsValue(value); } @Override public java.util.Collection values() { return sct.values(); } @Override public long sizeOf() { return SizeOf.size(sct); } public final int getType() { return type; } public final String getTypeAsString() { return strType; } @Override public final DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) { return StructUtil.toDumpTable(this, StringUtil.ucFirst(getTypeAsString())+" Scope ("+getStorageType()+")", pageContext, maxlevel, dp); } public long getLastAccess() { return lastvisit;} public long getTimeSpan() { return timeSpan;} public void touch() { lastvisit=System.currentTimeMillis(); _lastvisit=new DateTimeImpl(ThreadLocalPageContext.getConfig()); } public boolean isExpired() { return (getLastAccess()+getTimeSpan())<System.currentTimeMillis(); } @Override public void setStorage(String storage) { this.storage=storage; } @Override public String getStorage() { return storage; } public static String encode(String input) { int len=input.length(); StringBuilder sb=new StringBuilder(); char c; for(int i=0;i<len;i++){ c=input.charAt(i); if((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_' || c=='-') sb.append(c); else { sb.append('$'); sb.append(Integer.toString((c),Character.MAX_RADIX)); sb.append('$'); } } return sb.toString(); } public static String decode(String input) { int len=input.length(); StringBuilder sb=new StringBuilder(); char c; int ni; for(int i=0;i<len;i++){ c=input.charAt(i); if(c=='$') { ni=input.indexOf('$',i+1); sb.append((char)Integer.parseInt(input.substring(i+1,ni),Character.MAX_RADIX)); i=ni; } else { sb.append(c); } } return sb.toString(); } public int _getId() { return id; } public long getCreated() { return timecreated==null?0:timecreated.getTime(); } @Override public synchronized String generateToken(String key, boolean forceNew) { if(tokens==null) tokens = new HashMap<String,String>(); // get existing String token; if(!forceNew) { token = tokens.get(key); if(token!=null) return token; } // create new one token = RandomUtil.createRandomStringLC(40); tokens.put(key, token); return token; } @Override public synchronized boolean verifyToken(String token, String key) { if(tokens==null) return false; String _token = tokens.get(key); return _token!=null && _token.equalsIgnoreCase(token); } }