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