001 package railo.runtime.type.scope.storage; 002 003 import java.util.ArrayList; 004 import java.util.HashMap; 005 import java.util.HashSet; 006 import java.util.Iterator; 007 import java.util.List; 008 import java.util.Map; 009 import java.util.Set; 010 011 import railo.commons.lang.RandomUtil; 012 import railo.commons.lang.SizeOf; 013 import railo.commons.lang.StringUtil; 014 import railo.runtime.PageContext; 015 import railo.runtime.config.Config; 016 import railo.runtime.dump.DumpData; 017 import railo.runtime.dump.DumpProperties; 018 import railo.runtime.engine.ThreadLocalPageContext; 019 import railo.runtime.exp.PageException; 020 import railo.runtime.listener.ApplicationContext; 021 import railo.runtime.op.Duplicator; 022 import railo.runtime.type.Collection; 023 import railo.runtime.type.Sizeable; 024 import railo.runtime.type.Struct; 025 import railo.runtime.type.StructImpl; 026 import railo.runtime.type.dt.DateTime; 027 import railo.runtime.type.dt.DateTimeImpl; 028 import railo.runtime.type.util.CollectionUtil; 029 import railo.runtime.type.util.KeyConstants; 030 import railo.runtime.type.util.StructSupport; 031 import railo.runtime.type.util.StructUtil; 032 033 public abstract class StorageScopeImpl extends StructSupport implements StorageScope,Sizeable { 034 035 public static Collection.Key CFID=KeyConstants._cfid; 036 public static Collection.Key CFTOKEN=KeyConstants._cftoken; 037 public static Collection.Key URLTOKEN=KeyConstants._urltoken; 038 public static Collection.Key LASTVISIT=KeyConstants._lastvisit; 039 public static Collection.Key HITCOUNT=KeyConstants._hitcount; 040 public static Collection.Key TIMECREATED=KeyConstants._timecreated; 041 public static Collection.Key SESSION_ID=KeyConstants._sessionid; 042 043 044 private static int _id=0; 045 private int id=0; 046 047 private static final long serialVersionUID = 7874930250042576053L; 048 private static Set<Collection.Key> FIX_KEYS=new HashSet<Collection.Key>(); 049 static { 050 FIX_KEYS.add(CFID); 051 FIX_KEYS.add(CFTOKEN); 052 FIX_KEYS.add(URLTOKEN); 053 FIX_KEYS.add(LASTVISIT); 054 FIX_KEYS.add(HITCOUNT); 055 FIX_KEYS.add(TIMECREATED); 056 } 057 058 059 protected static Set<Collection.Key> ignoreSet=new HashSet<Collection.Key>(); 060 static { 061 ignoreSet.add(CFID); 062 ignoreSet.add(CFTOKEN); 063 ignoreSet.add(URLTOKEN); 064 } 065 066 067 protected boolean isinit=true; 068 protected Struct sct; 069 protected long lastvisit; 070 protected DateTime _lastvisit; 071 protected int hitcount=0; 072 protected DateTime timecreated; 073 private boolean hasChanges=false; 074 private String strType; 075 private int type; 076 private long timeSpan=-1; 077 private String storage; 078 private Map<String, String> tokens; 079 080 081 /** 082 * Constructor of the class 083 * @param sct 084 * @param timecreated 085 * @param _lastvisit 086 * @param lastvisit 087 * @param hitcount 088 */ 089 public StorageScopeImpl(Struct sct, DateTime timecreated, DateTime _lastvisit, long lastvisit, int hitcount,String strType,int type) { 090 this.sct=sct; 091 this.timecreated=timecreated; 092 if(_lastvisit==null) this._lastvisit=timecreated; 093 else this._lastvisit=_lastvisit; 094 095 if(lastvisit==-1) this.lastvisit=this._lastvisit.getTime(); 096 else this.lastvisit=lastvisit; 097 098 this.hitcount=hitcount; 099 this.strType=strType; 100 this.type=type; 101 id=++_id; 102 } 103 104 /** 105 * Constructor of the class 106 * @param other 107 * @param deepCopy 108 */ 109 public StorageScopeImpl(StorageScopeImpl other, boolean deepCopy) { 110 this.sct=(Struct)Duplicator.duplicate(other.sct,deepCopy); 111 this.timecreated=other.timecreated; 112 this._lastvisit=other._lastvisit; 113 this.hitcount=other.hitcount; 114 this.isinit=other.isinit; 115 this.lastvisit=other.lastvisit; 116 this.strType=other.strType; 117 this.type=other.type; 118 this.timeSpan=other.timeSpan; 119 id=++_id; 120 } 121 122 @Override 123 public void touchBeforeRequest(PageContext pc) { 124 125 hasChanges=false; 126 setTimeSpan(pc); 127 128 129 //lastvisit=System.currentTimeMillis(); 130 if(sct==null) sct=new StructImpl(); 131 sct.setEL(KeyConstants._cfid, pc.getCFID()); 132 sct.setEL(KeyConstants._cftoken, pc.getCFToken()); 133 sct.setEL(URLTOKEN, pc.getURLToken()); 134 sct.setEL(LASTVISIT, _lastvisit); 135 _lastvisit=new DateTimeImpl(pc.getConfig()); 136 lastvisit=System.currentTimeMillis(); 137 138 if(type==SCOPE_CLIENT){ 139 sct.setEL(HITCOUNT, new Double(hitcount++)); 140 } 141 else { 142 sct.setEL(SESSION_ID, pc.getApplicationContext().getName()+"_"+pc.getCFID()+"_"+pc.getCFToken()); 143 } 144 sct.setEL(TIMECREATED, timecreated); 145 } 146 147 public void resetEnv(PageContext pc){ 148 _lastvisit=new DateTimeImpl(pc.getConfig()); 149 timecreated=new DateTimeImpl(pc.getConfig()); 150 touchBeforeRequest(pc); 151 152 } 153 154 void setTimeSpan(PageContext pc) { 155 ApplicationContext ac=pc.getApplicationContext(); 156 this.timeSpan=getType()==SCOPE_SESSION? 157 ac.getSessionTimeout().getMillis(): 158 ac.getClientTimeout().getMillis(); 159 } 160 161 @Override 162 public void setMaxInactiveInterval(int interval) { 163 this.timeSpan=interval*1000L; 164 } 165 166 @Override 167 public int getMaxInactiveInterval() { 168 return (int)(this.timeSpan/1000L); 169 } 170 171 @Override 172 public final boolean isInitalized() { 173 return isinit; 174 } 175 176 @Override 177 public final void initialize(PageContext pc) { 178 // StorageScopes need only request initialisation no global init, they are not reused; 179 } 180 181 @Override 182 public void touchAfterRequest(PageContext pc) { 183 184 sct.setEL(LASTVISIT, _lastvisit); 185 sct.setEL(TIMECREATED, timecreated); 186 187 if(type==SCOPE_CLIENT){ 188 sct.setEL(HITCOUNT, new Double(hitcount)); 189 } 190 } 191 192 @Override 193 public final void release() { 194 clear(); 195 isinit=false; 196 } 197 198 @Override 199 public final void release(PageContext pc) { 200 clear(); 201 isinit=false; 202 } 203 204 205 /** 206 * @return returns if the scope is empty or not, this method ignore the "constant" entries of the scope (cfid,cftoken,urltoken) 207 */ 208 public boolean hasContent() { 209 if(sct.size()==(type==SCOPE_CLIENT?6:5) && sct.containsKey(URLTOKEN) && sct.containsKey(KeyConstants._cftoken) && sct.containsKey(KeyConstants._cfid)) { 210 return false; 211 } 212 return true; 213 } 214 215 @Override 216 public void clear() { 217 sct.clear(); 218 } 219 220 @Override 221 public boolean containsKey(Key key) { 222 return sct.containsKey(key); 223 } 224 225 @Override 226 public Object get(Key key) throws PageException { 227 return sct.get(key); 228 } 229 230 @Override 231 public Object get(Key key, Object defaultValue) { 232 return sct.get(key, defaultValue); 233 } 234 235 @Override 236 public Iterator<Collection.Key> keyIterator() { 237 return sct.keyIterator(); 238 } 239 240 @Override 241 public Iterator<String> keysAsStringIterator() { 242 return sct.keysAsStringIterator(); 243 } 244 245 @Override 246 public Iterator<Entry<Key, Object>> entryIterator() { 247 return sct.entryIterator(); 248 } 249 250 @Override 251 public Iterator<Object> valueIterator() { 252 return sct.valueIterator(); 253 } 254 255 @Override 256 public railo.runtime.type.Collection.Key[] keys() { 257 return CollectionUtil.keys(this); 258 } 259 260 261 @Override 262 public Object remove(Key key) throws PageException { 263 hasChanges=true; 264 return sct.remove(key); 265 } 266 267 @Override 268 public Object removeEL(Key key) { 269 hasChanges=true; 270 return sct.removeEL(key); 271 } 272 273 @Override 274 public Object set(Key key, Object value) throws PageException { 275 hasChanges=true; 276 return sct.set(key, value); 277 } 278 279 @Override 280 public Object setEL(Key key, Object value) { 281 hasChanges=true; 282 return sct.setEL(key, value); 283 } 284 285 @Override 286 public int size() { 287 return sct.size(); 288 } 289 290 291 @Override 292 public boolean castToBooleanValue() throws PageException { 293 return sct.castToBooleanValue(); 294 } 295 296 @Override 297 public Boolean castToBoolean(Boolean defaultValue) { 298 return sct.castToBoolean(defaultValue); 299 } 300 301 @Override 302 public DateTime castToDateTime() throws PageException { 303 return sct.castToDateTime(); 304 } 305 306 @Override 307 public DateTime castToDateTime(DateTime defaultValue) { 308 return sct.castToDateTime(defaultValue); 309 } 310 311 @Override 312 public double castToDoubleValue() throws PageException { 313 return sct.castToDoubleValue(); 314 } 315 316 @Override 317 public double castToDoubleValue(double defaultValue) { 318 return sct.castToDoubleValue(defaultValue); 319 } 320 321 @Override 322 public String castToString() throws PageException { 323 return sct.castToString(); 324 } 325 326 @Override 327 public String castToString(String defaultValue) { 328 return sct.castToString(defaultValue); 329 } 330 331 @Override 332 public int compareTo(boolean b) throws PageException { 333 return sct.compareTo(b); 334 } 335 336 @Override 337 public int compareTo(DateTime dt) throws PageException { 338 return sct.compareTo(dt); 339 } 340 341 @Override 342 public int compareTo(double d) throws PageException { 343 return sct.compareTo(d); 344 } 345 346 @Override 347 public int compareTo(String str) throws PageException { 348 return sct.compareTo(str); 349 } 350 351 @Override 352 public long lastVisit() { 353 return lastvisit; 354 } 355 356 public Collection.Key[] pureKeys() { 357 List<Collection.Key> keys=new ArrayList<Collection.Key>(); 358 Iterator<Key> it = keyIterator(); 359 Collection.Key key; 360 while(it.hasNext()){ 361 key=it.next(); 362 if(!FIX_KEYS.contains(key))keys.add(key); 363 } 364 return keys.toArray(new Collection.Key[keys.size()]); 365 } 366 367 @Override 368 public void store(Config config){ 369 //do nothing 370 } 371 372 @Override 373 public void unstore(Config config){ 374 //do nothing 375 } 376 377 /** 378 * @return the hasChanges 379 */ 380 public boolean hasChanges() { 381 return hasChanges; 382 } 383 384 385 @Override 386 public boolean containsValue(Object value) { 387 return sct.containsValue(value); 388 } 389 390 @Override 391 public java.util.Collection values() { 392 return sct.values(); 393 } 394 395 @Override 396 public long sizeOf() { 397 return SizeOf.size(sct); 398 } 399 400 public final int getType() { 401 return type; 402 } 403 404 public final String getTypeAsString() { 405 return strType; 406 } 407 408 409 410 @Override 411 public final DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) { 412 return StructUtil.toDumpTable(this, StringUtil.ucFirst(getTypeAsString())+" Scope ("+getStorageType()+")", pageContext, maxlevel, dp); 413 } 414 415 416 public long getLastAccess() { return lastvisit;} 417 public long getTimeSpan() { return timeSpan;} 418 419 420 public void touch() { 421 lastvisit=System.currentTimeMillis(); 422 _lastvisit=new DateTimeImpl(ThreadLocalPageContext.getConfig()); 423 } 424 425 public boolean isExpired() { 426 return (getLastAccess()+getTimeSpan())<System.currentTimeMillis(); 427 } 428 429 430 431 @Override 432 public void setStorage(String storage) { 433 this.storage=storage; 434 } 435 436 @Override 437 public String getStorage() { 438 return storage; 439 } 440 441 public static String encode(String input) { 442 int len=input.length(); 443 StringBuilder sb=new StringBuilder(); 444 char c; 445 for(int i=0;i<len;i++){ 446 c=input.charAt(i); 447 if((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_' || c=='-') 448 sb.append(c); 449 else { 450 sb.append('$'); 451 sb.append(Integer.toString((c),Character.MAX_RADIX)); 452 sb.append('$'); 453 } 454 } 455 456 return sb.toString(); 457 } 458 459 public static String decode(String input) { 460 int len=input.length(); 461 StringBuilder sb=new StringBuilder(); 462 char c; 463 int ni; 464 for(int i=0;i<len;i++){ 465 c=input.charAt(i); 466 if(c=='$') { 467 ni=input.indexOf('$',i+1); 468 sb.append((char)Integer.parseInt(input.substring(i+1,ni),Character.MAX_RADIX)); 469 i=ni; 470 } 471 472 else { 473 sb.append(c); 474 } 475 } 476 return sb.toString(); 477 } 478 479 public int _getId() { 480 return id; 481 } 482 483 484 485 public long getCreated() { 486 return timecreated==null?0:timecreated.getTime(); 487 } 488 489 @Override 490 public synchronized String generateToken(String key, boolean forceNew) { 491 if(tokens==null) 492 tokens = new HashMap<String,String>(); 493 494 // get existing 495 String token; 496 if(!forceNew) { 497 token = tokens.get(key); 498 if(token!=null) return token; 499 } 500 501 // create new one 502 token = RandomUtil.createRandomStringLC(40); 503 tokens.put(key, token); 504 return token; 505 } 506 507 @Override 508 public synchronized boolean verifyToken(String token, String key) { 509 if(tokens==null) return false; 510 String _token = tokens.get(key); 511 return _token!=null && _token.equalsIgnoreCase(token); 512 } 513 }