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);
}
}