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; 020 021import java.util.Date; 022import java.util.Iterator; 023import java.util.Map; 024import java.util.Map.Entry; 025 026import javax.servlet.http.HttpSession; 027 028import lucee.print; 029import lucee.commons.collection.MapFactory; 030import lucee.commons.io.log.Log; 031import lucee.commons.lang.ExceptionUtil; 032import lucee.commons.lang.SizeOf; 033import lucee.commons.lang.StringUtil; 034import lucee.commons.lang.types.RefBoolean; 035import lucee.commons.lang.types.RefBooleanImpl; 036import lucee.runtime.CFMLFactoryImpl; 037import lucee.runtime.PageContext; 038import lucee.runtime.PageContextImpl; 039import lucee.runtime.cache.CacheConnection; 040import lucee.runtime.config.Config; 041import lucee.runtime.config.ConfigImpl; 042import lucee.runtime.db.DataSource; 043import lucee.runtime.exp.ApplicationException; 044import lucee.runtime.exp.ExceptionHandler; 045import lucee.runtime.exp.ExpressionException; 046import lucee.runtime.exp.PageException; 047import lucee.runtime.exp.PageRuntimeException; 048import lucee.runtime.functions.cache.Util; 049import lucee.runtime.interpreter.VariableInterpreter; 050import lucee.runtime.listener.ApplicationContext; 051import lucee.runtime.listener.ApplicationListener; 052import lucee.runtime.op.Caster; 053import lucee.runtime.type.KeyImpl; 054import lucee.runtime.type.Struct; 055import lucee.runtime.type.StructImpl; 056import lucee.runtime.type.scope.client.ClientCache; 057import lucee.runtime.type.scope.client.ClientCookie; 058import lucee.runtime.type.scope.client.ClientDatasource; 059import lucee.runtime.type.scope.client.ClientFile; 060import lucee.runtime.type.scope.client.ClientMemory; 061import lucee.runtime.type.scope.session.SessionCache; 062import lucee.runtime.type.scope.session.SessionCookie; 063import lucee.runtime.type.scope.session.SessionDatasource; 064import lucee.runtime.type.scope.session.SessionFile; 065import lucee.runtime.type.scope.session.SessionMemory; 066import lucee.runtime.type.scope.storage.MemoryScope; 067import lucee.runtime.type.scope.storage.StorageScope; 068import lucee.runtime.type.scope.storage.StorageScopeCleaner; 069import lucee.runtime.type.scope.storage.StorageScopeEngine; 070import lucee.runtime.type.scope.storage.clean.DatasourceStorageScopeCleaner; 071import lucee.runtime.type.scope.storage.clean.FileStorageScopeCleaner; 072import lucee.runtime.type.wrap.MapAsStruct; 073import lucee.runtime.util.PageContextUtil; 074 075import org.safehaus.uuid.UUIDGenerator; 076 077/** 078 * Scope Context handle Apllication and Session Scopes 079 */ 080public final class ScopeContext { 081 082 private static final int MINUTE = 60*1000; 083 private static final long CLIENT_MEMORY_TIMESPAN = 5*MINUTE; 084 private static final long SESSION_MEMORY_TIMESPAN = 5*MINUTE; 085 086 private static UUIDGenerator generator = UUIDGenerator.getInstance(); 087 private Map<String,Map<String,Scope>> cfSessionContextes=MapFactory.<String,Map<String,Scope>>getConcurrentMap(); 088 private Map<String,Map<String,Scope>> cfClientContextes=MapFactory.<String,Map<String,Scope>>getConcurrentMap(); 089 private Map<String,Application> applicationContextes=MapFactory.<String,Application>getConcurrentMap(); 090 091 private int maxSessionTimeout=0; 092 093 private static Cluster cluster; 094 private static Server server=null; 095 096 097 private StorageScopeEngine client; 098 private StorageScopeEngine session; 099 private CFMLFactoryImpl factory; 100 private Log log; 101 102 103 104 public ScopeContext(CFMLFactoryImpl factory) { 105 this.factory=factory; 106 } 107 108 /** 109 * @return the log 110 */ 111 private Log getLog() { 112 if(log==null) { 113 this.log=((ConfigImpl)factory.getConfig()).getLog("scope"); 114 115 } 116 return log; 117 } 118 119 public void info(String msg) {info(getLog(), msg);} 120 public void error(String msg) {error(getLog(),msg);} 121 public void error(Throwable t) { 122 ExceptionUtil.rethrowIfNecessary(t); 123 error(getLog(), t); 124 } 125 126 public static void info(Log log,String msg) { 127 if(log!=null)log.log(Log.LEVEL_INFO,"scope-context", msg); 128 } 129 130 131 public static void error(Log log,String msg) { 132 if(log!=null)log.log(Log.LEVEL_ERROR,"scope-context", msg); 133 } 134 135 public static void error(Log log,Throwable t) { 136 if(log!=null)log.log(Log.LEVEL_ERROR,"scope-context",ExceptionUtil.getStacktrace(t, true)); 137 } 138 139 140 /** 141 * return a map matching key from given map 142 * @param parent 143 * @param key key of the map 144 * @return matching map, if no map exist it willbe one created 145 */ 146 private Map<String,Scope> getSubMap(Map<String,Map<String,Scope>> parent, String key) { 147 148 Map<String,Scope> context=parent.get(key); 149 if(context!=null) return context; 150 151 context = MapFactory.<String,Scope>getConcurrentMap(); 152 parent.put(key,context); 153 return context; 154 155 } 156 157 /** 158 * return the server Scope for this context 159 * @param pc 160 * @return server scope 161 */ 162 public static Server getServerScope(PageContext pc) { 163 if(server==null) { 164 server=new ServerImpl(pc); 165 } 166 return server; 167 } 168 169 /* * 170 * Returns the current Cluster Scope, if there is no current Cluster Scope, this method returns null. 171 * @param pc 172 * @param create 173 * @return 174 * @throws SecurityException 175 * / 176 public static Cluster getClusterScope() { 177 return cluster; 178 }*/ 179 180 /** 181 * Returns the current Cluster Scope, if there is no current Cluster Scope and create is true, returns a new Cluster Scope. 182 * If create is false and the request has no valid Cluster Scope, this method returns null. 183 * @param pc 184 * @param create 185 * @return 186 * @throws PageException 187 */ 188 public static Cluster getClusterScope(Config config, boolean create) throws PageException { 189 if(cluster==null && create) { 190 cluster=((ConfigImpl)config).createClusterScope(); 191 192 } 193 return cluster; 194 } 195 196 public static void clearClusterScope() { 197 cluster=null; 198 } 199 200 201 public Client getClientScope(PageContext pc) throws PageException { 202 ApplicationContext appContext = pc.getApplicationContext(); 203 // get Context 204 Map<String, Scope> context = getSubMap(cfClientContextes,appContext.getName()); 205 206 // get Client 207 boolean isMemory=false; 208 String storage = appContext.getClientstorage(); 209 if(StringUtil.isEmpty(storage,true)){ 210 storage=ConfigImpl.DEFAULT_STORAGE_CLIENT; 211 } 212 else if("ram".equalsIgnoreCase(storage)) { 213 storage="memory"; 214 isMemory=true; 215 } 216 else if("registry".equalsIgnoreCase(storage)) { 217 storage="file"; 218 } 219 else { 220 storage=storage.toLowerCase(); 221 if("memory".equals(storage))isMemory=true; 222 } 223 224 //final boolean doMemory=isMemory || !appContext.getClientCluster(); 225 Client existing=(Client) context.get(pc.getCFID()); 226 Client client=appContext.getClientCluster()?null:existing; 227 //client=doMemory?(Client) context.get(pc.getCFID()):null; 228 229 230 if(client==null || client.isExpired() || !client.getStorage().equalsIgnoreCase(storage)) { 231 if("file".equals(storage)){ 232 client=ClientFile.getInstance(appContext.getName(),pc,getLog()); 233 } 234 else if("cookie".equals(storage)) 235 client=ClientCookie.getInstance(appContext.getName(),pc,getLog()); 236 else if("memory".equals(storage)){ 237 if(existing!=null) client=existing; 238 client=ClientMemory.getInstance(pc,getLog()); 239 } 240 else{ 241 DataSource ds = ((PageContextImpl)pc).getDataSource(storage,null); 242 if(ds!=null)client=ClientDatasource.getInstance(storage,pc,getLog()); 243 else client=ClientCache.getInstance(storage,appContext.getName(),pc,existing,getLog(),null); 244 245 if(client==null){ 246 // datasource not enabled for storage 247 if(ds!=null) 248 throw new ApplicationException("datasource ["+storage+"] is not enabled to be used as session/client storage, you have to enable it in the lucee administrator."); 249 250 CacheConnection cc = Util.getCacheConnection(pc.getConfig(),storage,null); 251 if(cc!=null) 252 throw new ApplicationException("cache ["+storage+"] is not enabled to be used as a session/client storage, you have to enable it in the lucee administrator."); 253 254 throw new ApplicationException("there is no cache or datasource with name ["+storage+"] defined."); 255 } 256 257 } 258 client.setStorage(storage); 259 context.put(pc.getCFID(),client); 260 } 261 else 262 getLog().log(Log.LEVEL_INFO,"scope-context", "use existing client scope for "+appContext.getName()+"/"+pc.getCFID()+" from storage "+storage); 263 264 client.touchBeforeRequest(pc); 265 return client; 266 } 267 268 public Client getClientScopeEL(PageContext pc) { 269 try { 270 return getClientScope(pc); 271 } catch (PageException pe) { 272 throw new PageRuntimeException(pe); 273 } 274 } 275 276 /*public ClientPlus getClientScopeEL(PageContext pc) { 277 ClientPlus client=null; 278 ApplicationContext appContext = pc.getApplicationContext(); 279 // get Context 280 Map context=getSubMap(cfClientContextes,appContext.getName()); 281 282 // get Client 283 String storage = appContext.getClientstorage(); 284 if(!StringUtil.isEmpty(storage))storage=storage.toLowerCase(); 285 else storage=""; 286 287 client=(ClientPlus) context.get(pc.getCFID()); 288 if(client==null || client.isExpired() || !client.getStorageType().equalsIgnoreCase(storage)) { 289 if(StringUtil.isEmpty(storage) || "file".equals(storage) || "registry".equals(storage)){ 290 storage="file"; 291 client=ClientFile.getInstance(appContext.getName(),pc,getLog()); 292 } 293 else if("cookie".equals(storage)) 294 client=ClientCookie.getInstance(appContext.getName(),pc,getLog()); 295 else if("memory".equals(storage) || "ram".equals(storage)){ 296 //storage="ram"; 297 client=ClientMemory.getInstance(pc,getLog()); 298 } 299 else{ 300 DataSource ds = ((ConfigImpl)pc.getConfig()).getDataSource(storage,null); 301 if(ds!=null)client=ClientDatasource.getInstanceEL(storage,pc,getLog()); 302 else client=ClientCache.getInstanceEL(storage,appContext.getName(),pc,getLog()); 303 304 } 305 client.setStorage(storage); 306 context.put(pc.getCFID(),client); 307 } 308 else 309 getLog().info("scope-context", "use existing client scope for "+appContext.getName()+"/"+pc.getCFID()+" from storage "+storage); 310 311 312 client.initialize(pc); 313 return client; 314 }*/ 315 316 317 318 /** 319 * return the session count of all application contextes 320 * @return 321 */ 322 public int getSessionCount(PageContext pc) { 323 if(pc.getSessionType()==Config.SESSION_TYPE_J2EE) return 0; 324 325 Iterator<Entry<String, Map<String, Scope>>> it = cfSessionContextes.entrySet().iterator(); 326 Entry<String, Map<String, Scope>> entry; 327 int count=0; 328 while(it.hasNext()) { 329 entry = it.next(); 330 count+=getSessionCount(entry.getValue()); 331 } 332 return count; 333 } 334 335 /** 336 * return the session count of this application context 337 * @return 338 */ 339 public int getAppContextSessionCount(PageContext pc) { 340 ApplicationContext appContext = pc.getApplicationContext(); 341 if(pc.getSessionType()==Config.SESSION_TYPE_J2EE) return 0; 342 343 Map<String, Scope> context = getSubMap(cfSessionContextes,appContext.getName()); 344 return getSessionCount(context); 345 } 346 347 private int getSessionCount(Map<String, Scope> context) { 348 Iterator<Entry<String, Scope>> it = context.entrySet().iterator(); 349 Entry<String, Scope> entry; 350 int count=0; 351 Session s; 352 while(it.hasNext()) { 353 entry = it.next(); 354 s=(Session)entry.getValue(); 355 if(!s.isExpired()) 356 count++; 357 } 358 return count; 359 } 360 361 362 363 /** 364 * return all session context of this application context 365 * @param pc 366 * @return 367 */ 368 public Struct getAllSessionScopes(PageContext pc) { 369 return getAllSessionScopes(pc.getApplicationContext().getName()); 370 } 371 372 public Struct getAllApplicationScopes() { 373 Struct trg=new StructImpl(); 374 StructImpl.copy(MapAsStruct.toStruct(applicationContextes, true), trg, false); 375 return trg; 376 } 377 378 public Struct getAllCFSessionScopes() { 379 Struct trg=new StructImpl(); 380 StructImpl.copy(MapAsStruct.toStruct(this.cfSessionContextes, true), trg, false); 381 return trg; 382 } 383 384 /** 385 * return the size in bytes of all session contextes 386 * @return size in bytes 387 * @throws ExpressionException 388 */ 389 public long getScopesSize(int scope) throws ExpressionException { 390 if(scope==Scope.SCOPE_APPLICATION)return SizeOf.size(applicationContextes); 391 if(scope==Scope.SCOPE_CLUSTER)return SizeOf.size(cluster); 392 if(scope==Scope.SCOPE_SERVER)return SizeOf.size(server); 393 if(scope==Scope.SCOPE_SESSION)return SizeOf.size(this.cfSessionContextes); 394 if(scope==Scope.SCOPE_CLIENT)return SizeOf.size(this.cfClientContextes); 395 396 throw new ExpressionException("can only return information of scope that are not request dependent"); 397 } 398 399 /** 400 * get all session contexts of given applicaton name 401 * @param pc 402 * @param appName 403 * @return 404 * @deprecated use instead getAllSessionScopes(String appName) 405 */ 406 public Struct getAllSessionScopes(PageContext pc, String appName) { 407 return getAllSessionScopes(appName); 408 } 409 410 /** 411 * get all session contexts of given applicaton name 412 * @param pc 413 * @param appName 414 * @return 415 */ 416 public Struct getAllSessionScopes(String appName) { 417 //if(pc.getSessionType()==Config.SESSION_TYPE_J2EE)return new StructImpl(); 418 return getAllSessionScopes(getSubMap(cfSessionContextes,appName),appName); 419 } 420 421 private Struct getAllSessionScopes(Map<String,Scope> context, String appName) { 422 Iterator<Entry<String, Scope>> it = context.entrySet().iterator(); 423 Entry<String, Scope> entry; 424 Struct sct=new StructImpl(); 425 Session s; 426 while(it.hasNext()) { 427 entry = it.next(); 428 s=(Session)entry.getValue(); 429 if(!s.isExpired()) 430 sct.setEL(KeyImpl.init(appName+"_"+entry.getKey()+"_0"), s); 431 } 432 return sct; 433 } 434 435 /** 436 * return the session Scope for this context (cfid,cftoken,contextname) 437 * @param pc PageContext 438 * @return session matching the context 439 * @throws PageException 440 */ 441 public Session getSessionScope(PageContext pc,RefBoolean isNew) throws PageException { 442 if(pc.getSessionType()==Config.SESSION_TYPE_CFML)return getCFSessionScope(pc,isNew); 443 return getJSessionScope(pc,isNew); 444 } 445 446 public boolean hasExistingSessionScope(PageContext pc) { 447 if(pc.getSessionType()==Config.SESSION_TYPE_CFML)return hasExistingCFSessionScope(pc); 448 return hasExistingJSessionScope(pc); 449 } 450 451 private boolean hasExistingJSessionScope(PageContext pc) { 452 HttpSession httpSession=pc.getSession(); 453 if(httpSession==null) return false; 454 455 Session session=(Session) httpSession.getAttribute(pc.getApplicationContext().getName()); 456 return session instanceof JSession; 457 } 458 459 460 private boolean hasExistingCFSessionScope(PageContext pc) { 461 462 ApplicationContext appContext = pc.getApplicationContext(); 463 // get Context 464 Map<String, Scope> context = getSubMap(cfSessionContextes,appContext.getName()); 465 466 // get Session 467 String storage = appContext.getSessionstorage(); 468 if(StringUtil.isEmpty(storage,true))storage="memory"; 469 else if("ram".equalsIgnoreCase(storage)) storage="memory"; 470 else if("registry".equalsIgnoreCase(storage)) storage="file"; 471 else storage=storage.toLowerCase(); 472 473 474 475 Session session=(Session) context.get(pc.getCFID()); 476 477 if(!(session instanceof StorageScope) || session.isExpired() || !((StorageScope)session).getStorage().equalsIgnoreCase(storage)) { 478 479 if("memory".equals(storage)) return false; 480 else if("file".equals(storage)) 481 return SessionFile.hasInstance(appContext.getName(),pc); 482 else if("cookie".equals(storage)) 483 return SessionCookie.hasInstance(appContext.getName(),pc); 484 else { 485 DataSource ds = ((ConfigImpl)pc.getConfig()).getDataSource(storage,null); 486 if(ds!=null && ds.isStorage()){ 487 if(SessionDatasource.hasInstance(storage,pc)) return true; 488 } 489 return SessionCache.hasInstance(storage,appContext.getName(),pc); 490 } 491 } 492 return true; 493 } 494 495 496 /** 497 * return cf session scope 498 * @param pc PageContext 499 * @param checkExpires 500 * @param listener 501 * @return cf session matching the context 502 * @throws PageException 503 */ 504 private Session getCFSessionScope(PageContext pc, RefBoolean isNew) throws PageException { 505 506 ApplicationContext appContext = pc.getApplicationContext(); 507 // get Context 508 Map<String, Scope> context = getSubMap(cfSessionContextes,appContext.getName()); 509 510 // get Session 511 boolean isMemory=false; 512 String storage = appContext.getSessionstorage(); 513 if(StringUtil.isEmpty(storage,true)){ 514 storage=ConfigImpl.DEFAULT_STORAGE_SESSION; 515 isMemory=true; 516 } 517 else if("ram".equalsIgnoreCase(storage)) { 518 storage="memory"; 519 isMemory=true; 520 } 521 else if("registry".equalsIgnoreCase(storage)) { 522 storage="file"; 523 } 524 else { 525 storage=storage.toLowerCase(); 526 if("memory".equals(storage))isMemory=true; 527 } 528 529 //final boolean doMemory=isMemory || !appContext.getSessionCluster(); 530 531 Session existing=(Session) context.get(pc.getCFID()); 532 Session session=appContext.getSessionCluster()?null:existing; 533 //Session session=doMemory?(appContext.getSessionCluster()?null:(Session) context.get(pc.getCFID())):null; 534 535 if(session==null || !(session instanceof StorageScope) || session.isExpired() || !((StorageScope)session).getStorage().equalsIgnoreCase(storage)) { 536 if(isMemory){ 537 if(existing!=null) session=existing; 538 else session=SessionMemory.getInstance(pc,isNew,getLog()); 539 } 540 else if("file".equals(storage)){ 541 session=SessionFile.getInstance(appContext.getName(),pc,getLog()); 542 } 543 else if("cookie".equals(storage)) 544 session=SessionCookie.getInstance(appContext.getName(),pc,getLog()); 545 else{ 546 DataSource ds = ((PageContextImpl)pc).getDataSource(storage,null); 547 if(ds!=null && ds.isStorage())session=SessionDatasource.getInstance(storage,pc,getLog(),null); 548 else { 549 session=SessionCache.getInstance(storage,appContext.getName(),pc,existing,getLog(),null); 550 } 551 552 if(session==null){ 553 // datasource not enabled for storage 554 if(ds!=null) 555 throw new ApplicationException( 556 "datasource ["+storage+"] is not enabled to be used as session/client storage, " + 557 "you have to enable it in the lucee administrator or define key \"storage=true\" for datasources defined in Application.cfc ."); 558 559 CacheConnection cc = Util.getCacheConnection(pc.getConfig(),storage,null); 560 if(cc!=null) 561 throw new ApplicationException("cache ["+storage+"] is not enabled to be used as a session/client storage, you have to enable it in the lucee administrator."); 562 563 throw new ApplicationException("there is no cache or datasource with name ["+storage+"] defined."); 564 } 565 } 566 ((StorageScope)session).setStorage(storage); 567 context.put(pc.getCFID(),session); 568 isNew.setValue(true); 569 } 570 else { 571 getLog().log(Log.LEVEL_INFO,"scope-context", "use existing session scope for "+appContext.getName()+"/"+pc.getCFID()+" from storage "+storage); 572 } 573 session.touchBeforeRequest(pc); 574 return session; 575 } 576 577 public void removeSessionScope(PageContext pc) throws PageException { 578 579 //CFSession 580 Session sess = getCFSessionScope(pc, new RefBooleanImpl()); 581 ApplicationContext appContext = pc.getApplicationContext(); 582 Map<String, Scope> context = getSubMap(cfSessionContextes,appContext.getName()); 583 if(context!=null) { 584 context.remove(pc.getCFID()); 585 if(sess instanceof StorageScope)((StorageScope)sess).unstore(pc.getConfig()); 586 } 587 588 // JSession 589 HttpSession httpSession=pc.getSession(); 590 if(httpSession!=null) { 591 httpSession.removeAttribute(appContext.getName()); 592 } 593 } 594 595 public void removeClientScope(PageContext pc) throws PageException { 596 Client cli = getClientScope(pc); 597 ApplicationContext appContext = pc.getApplicationContext(); 598 Map<String, Scope> context = getSubMap(cfClientContextes,appContext.getName()); 599 if(context!=null) { 600 context.remove(pc.getCFID()); 601 if(cli!=null)cli.unstore(pc.getConfig()); 602 } 603 } 604 605 606 public boolean remove(int type, String appName, String cfid) { 607 Map<String, Map<String, Scope>> contextes = type==Scope.SCOPE_CLIENT?cfClientContextes:cfSessionContextes; 608 Map<String, Scope> context = getSubMap(contextes,appName); 609 Object res = context.remove(cfid); 610 getLog().log(Log.LEVEL_INFO,"scope-context", "remove "+VariableInterpreter.scopeInt2String(type)+" scope "+appName+"/"+cfid+" from memory"); 611 612 return res!=null; 613 } 614 615 /** 616 * return j session scope 617 * @param pc PageContext 618 * @param listener 619 * @return j session matching the context 620 * @throws PageException 621 */ 622 private Session getJSessionScope(PageContext pc, RefBoolean isNew) throws PageException { 623 HttpSession httpSession=pc.getSession(); 624 ApplicationContext appContext = pc.getApplicationContext(); 625 Object session=null;// this is from type object, because it is possible that httpSession return object from prior restart 626 627 int s=(int) appContext.getSessionTimeout().getSeconds(); 628 if(maxSessionTimeout<s)maxSessionTimeout=s; 629 if(httpSession!=null) { 630 httpSession.setMaxInactiveInterval(maxSessionTimeout+60);// we let the http session run a minute longer so we are sure it exists when sessionTimeout is triggered. 631 session= httpSession.getAttribute(appContext.getName()); 632 } 633 else { 634 Map<String, Scope> context = getSubMap(cfSessionContextes,appContext.getName()); 635 session=context.get(pc.getCFID()); 636 } 637 638 JSession jSession=null; 639 if(session instanceof JSession) { 640 jSession=(JSession) session; 641 try { 642 if(jSession.isExpired()) { 643 jSession.touch(); 644 } 645 info(getLog(), "use existing JSession for "+appContext.getName()+"/"+pc.getCFID()); 646 647 } 648 catch(ClassCastException cce) { 649 error(getLog(), cce); 650 // if there is no HTTPSession 651 if(httpSession==null) return getCFSessionScope(pc, isNew); 652 653 jSession=new JSession(); 654 httpSession.setAttribute(appContext.getName(),jSession); 655 isNew.setValue(true); 656 } 657 } 658 else { 659 // if there is no HTTPSession 660 if(httpSession==null) return getCFSessionScope(pc, isNew); 661 662 info(getLog(), "create new JSession for "+appContext.getName()+"/"+pc.getCFID()); 663 jSession=new JSession(); 664 httpSession.setAttribute(appContext.getName(),jSession); 665 isNew.setValue(true); 666 Map<String, Scope> context = getSubMap(cfSessionContextes,appContext.getName()); 667 context.put(pc.getCFID(),jSession); 668 } 669 jSession.touchBeforeRequest(pc); 670 return jSession; 671 } 672 673 /** 674 * return the application Scope for this context (cfid,cftoken,contextname) 675 * @param pc PageContext 676 * @param listener 677 * @param isNew 678 * @return session matching the context 679 * @throws PageException 680 */ 681 public Application getApplicationScope(PageContext pc, RefBoolean isNew) { 682 ApplicationContext appContext = pc.getApplicationContext(); 683 // getApplication Scope from Context 684 ApplicationImpl application; 685 Object objApp=applicationContextes.get(appContext.getName()); 686 if(objApp!=null) { 687 application=(ApplicationImpl)objApp; 688 if(application.isExpired()) { 689 application.release(); 690 isNew.setValue(true); 691 } 692 } 693 else { 694 application=new ApplicationImpl(); 695 applicationContextes.put(appContext.getName(),application); 696 isNew.setValue(true); 697 } 698 application.touchBeforeRequest(pc); 699 //if(newApplication)listener.onApplicationStart(pc); 700 701 return application; 702 } 703 704 public void removeApplicationScope(PageContext pc) { 705 applicationContextes.remove(pc.getApplicationContext().getName()); 706 } 707 708 709 /** 710 * remove all unused scope objects 711 */ 712 public void clearUnused() { 713 714 Log log=getLog(); 715 try{ 716 // create cleaner engine for session/client scope 717 if(session==null)session=new StorageScopeEngine(factory,log,new StorageScopeCleaner[]{ 718 new FileStorageScopeCleaner(Scope.SCOPE_SESSION, null)//new SessionEndListener()) 719 ,new DatasourceStorageScopeCleaner(Scope.SCOPE_SESSION, null)//new SessionEndListener()) 720 //,new CacheStorageScopeCleaner(Scope.SCOPE_SESSION, new SessionEndListener()) 721 }); 722 if(client==null)client=new StorageScopeEngine(factory,log,new StorageScopeCleaner[]{ 723 new FileStorageScopeCleaner(Scope.SCOPE_CLIENT, null) 724 ,new DatasourceStorageScopeCleaner(Scope.SCOPE_CLIENT, null) 725 //,new CacheStorageScopeCleaner(Scope.SCOPE_CLIENT, null) //Cache storage need no control, if there is no listener 726 }); 727 728 729 730 // store session/client scope and remove from memory 731 storeUnusedStorageScope(factory, Scope.SCOPE_CLIENT); 732 storeUnusedStorageScope(factory, Scope.SCOPE_SESSION); 733 734 // remove unused memory based client/session scope (invoke onSessonEnd) 735 clearUnusedMemoryScope(factory, Scope.SCOPE_CLIENT); 736 clearUnusedMemoryScope(factory, Scope.SCOPE_SESSION); 737 738 739 // session must be executed first, because session creates a reference from client scope 740 session.clean(); 741 client.clean(); 742 743 // clean all unused application scopes 744 clearUnusedApplications(factory); 745 } 746 catch(Throwable t){ 747 ExceptionUtil.rethrowIfNecessary(t); 748 error(t); 749 } 750 } 751 752 /** 753 * remove all scope objects 754 */ 755 public void clear() { 756 try{ 757 Scope scope; 758 //Map.Entry entry,e; 759 //Map context; 760 761 // release all session scopes 762 Iterator<Entry<String, Map<String, Scope>>> sit = cfSessionContextes.entrySet().iterator(); 763 Entry<String, Map<String, Scope>> sentry; 764 Map<String, Scope> context; 765 Iterator<Entry<String, Scope>> itt; 766 Entry<String, Scope> e; 767 768 while(sit.hasNext()){ 769 sentry=sit.next(); 770 context = sentry.getValue(); 771 itt = context.entrySet().iterator(); 772 while(itt.hasNext()){ 773 e = itt.next(); 774 scope=e.getValue(); 775 scope.release(); 776 } 777 } 778 cfSessionContextes.clear(); 779 780 // release all application scopes 781 Iterator<Entry<String, Application>> ait = applicationContextes.entrySet().iterator(); 782 Entry<String, Application> aentry; 783 while(ait.hasNext()){ 784 aentry = ait.next(); 785 scope=aentry.getValue(); 786 scope.release(); 787 } 788 applicationContextes.clear(); 789 790 // release server scope 791 if(server!=null){ 792 server.release(); 793 server=null; 794 } 795 796 } 797 catch(Throwable t){ 798 ExceptionUtil.rethrowIfNecessary(t); 799 t.printStackTrace(); 800 } 801 } 802 803 804 805 private void storeUnusedStorageScope(CFMLFactoryImpl cfmlFactory, int type) { 806 Map<String, Map<String, Scope>> contextes = type==Scope.SCOPE_CLIENT?cfClientContextes:cfSessionContextes; 807 long timespan = type==Scope.SCOPE_CLIENT?CLIENT_MEMORY_TIMESPAN:SESSION_MEMORY_TIMESPAN; 808 String strType=VariableInterpreter.scopeInt2String(type); 809 810 if(contextes.size()==0)return; 811 long now = System.currentTimeMillis(); 812 Object[] arrContextes= contextes.keySet().toArray(); 813 Object applicationName,cfid,o; 814 Map<String, Scope> fhm; 815 for(int i=0;i<arrContextes.length;i++) { 816 817 applicationName=arrContextes[i]; 818 fhm = contextes.get(applicationName); 819 if(fhm.size()>0){ 820 Object[] arrClients= fhm.keySet().toArray(); 821 int count=arrClients.length; 822 for(int y=0;y<arrClients.length;y++) { 823 cfid=arrClients[y]; 824 o=fhm.get(cfid); 825 if(!(o instanceof StorageScope)) continue; 826 StorageScope scope=(StorageScope)o; 827 if(scope.lastVisit()+timespan<now && !(scope instanceof MemoryScope)) { 828 getLog().log(Log.LEVEL_INFO,"scope-context", "remove from memory "+strType+" scope for "+applicationName+"/"+cfid+" from storage "+scope.getStorage()); 829 830 //if(scope instanceof StorageScope)((StorageScope)scope).store(cfmlFactory.getConfig()); 831 fhm.remove(arrClients[y]); 832 count--; 833 } 834 } 835 if(count==0)contextes.remove(arrContextes[i]); 836 } 837 } 838 } 839 840 /** 841 * @param cfmlFactory 842 * 843 */ 844 private void clearUnusedMemoryScope(CFMLFactoryImpl cfmlFactory, int type) { 845 Map<String, Map<String, Scope>> contextes = type==Scope.SCOPE_CLIENT?cfClientContextes:cfSessionContextes; 846 if(contextes.size()==0)return; 847 848 849 850 Object[] arrContextes= contextes.keySet().toArray(); 851 ApplicationListener listener = cfmlFactory.getConfig().getApplicationListener(); 852 Object applicationName,cfid,o; 853 Map<String, Scope> fhm; 854 855 for(int i=0;i<arrContextes.length;i++) { 856 applicationName=arrContextes[i]; 857 fhm = contextes.get(applicationName); 858 859 if(fhm.size()>0){ 860 Object[] cfids= fhm.keySet().toArray(); 861 int count=cfids.length; 862 for(int y=0;y<cfids.length;y++) { 863 cfid=cfids[y]; 864 o=fhm.get(cfid); 865 if(!(o instanceof MemoryScope)) continue; 866 MemoryScope scope=(MemoryScope) o; 867 // close 868 if(scope.isExpired()) { 869 // TODO macht das sinn? ist das nicht kopierleiche? 870 ApplicationImpl application=(ApplicationImpl) applicationContextes.get(applicationName); 871 long appLastAccess=0; 872 if(application!=null){ 873 appLastAccess=application.getLastAccess(); 874 application.touch(); 875 } 876 scope.touch(); 877 878 try { 879 if(type==Scope.SCOPE_SESSION)listener.onSessionEnd(cfmlFactory,(String)applicationName,(String)cfid); 880 } 881 catch (Throwable t) { 882 ExceptionUtil.rethrowIfNecessary(t); 883 t.printStackTrace(); 884 ExceptionHandler.log(cfmlFactory.getConfig(),Caster.toPageException(t)); 885 } 886 finally { 887 if(application!=null)application.setLastAccess(appLastAccess); 888 fhm.remove(cfids[y]); 889 scope.release(); 890 getLog().log(Log.LEVEL_INFO,"scope-context", "remove memory based "+VariableInterpreter.scopeInt2String(type)+" scope for "+applicationName+"/"+cfid); 891 count--; 892 } 893 } 894 } 895 if(count==0)contextes.remove(arrContextes[i]); 896 } 897 } 898 } 899 900 private void clearUnusedApplications(CFMLFactoryImpl jspFactory) { 901 902 if(applicationContextes.size()==0)return; 903 904 long now=System.currentTimeMillis(); 905 Object[] arrContextes= applicationContextes.keySet().toArray(); 906 ApplicationListener listener = jspFactory.getConfig().getApplicationListener(); 907 for(int i=0;i<arrContextes.length;i++) { 908 Application application=applicationContextes.get(arrContextes[i]); 909 910 if(application.getLastAccess()+application.getTimeSpan()<now) { 911 //SystemOut .printDate(jspFactory.getConfigWebImpl().getOut(),"Clear application scope:"+arrContextes[i]+"-"+this); 912 application.touch(); 913 try { 914 listener.onApplicationEnd(jspFactory,(String)arrContextes[i]); 915 } 916 catch (Throwable t) { 917 ExceptionUtil.rethrowIfNecessary(t); 918 ExceptionHandler.log(jspFactory.getConfig(),Caster.toPageException(t)); 919 } 920 finally { 921 applicationContextes.remove(arrContextes[i]); 922 application.release(); 923 } 924 925 } 926 } 927 } 928 929 930 public void clearApplication(PageContext pc) throws PageException { 931 932 if(applicationContextes.size()==0) throw new ApplicationException("there is no application context defined"); 933 934 String name = pc.getApplicationContext().getName(); 935 CFMLFactoryImpl jspFactory = (CFMLFactoryImpl)pc.getCFMLFactory(); 936 937 Application application=applicationContextes.get(name); 938 if(application==null) throw new ApplicationException("there is no application context defined with name ["+name+"]"); 939 ApplicationListener listener = PageContextUtil.getApplicationListener(pc); 940 application.touch(); 941 try { 942 listener.onApplicationEnd(jspFactory,name); 943 } 944 finally { 945 applicationContextes.remove(name); 946 application.release(); 947 } 948 } 949 950 /** 951 * @return returns a new CFIs 952 */ 953 public static String getNewCFId() { 954 return generator.generateRandomBasedUUID().toString(); 955 } 956 957 /** 958 * @return returns a new CFToken 959 */ 960 public static String getNewCFToken() { 961 return "0"; 962 } 963 964 public void invalidateUserScope(PageContextImpl pc,boolean migrateSessionData,boolean migrateClientData) throws PageException { 965 ApplicationContext appContext = pc.getApplicationContext(); 966 967 // get in memory scopes 968 Map<String, Scope> clientContext = getSubMap(cfClientContextes,appContext.getName()); 969 UserScope clientScope = (UserScope) clientContext.get(pc.getCFID()); 970 Map<String, Scope> sessionContext = getSubMap(cfSessionContextes,appContext.getName()); 971 UserScope sessionScope = (UserScope) sessionContext.get(pc.getCFID()); 972 973 // remove Scopes completly 974 removeSessionScope(pc); 975 removeClientScope(pc); 976 977 pc.resetIdAndToken(); 978 979 _migrate(pc,clientContext,clientScope,migrateClientData); 980 _migrate(pc,sessionContext,sessionScope,migrateSessionData); 981 } 982 983 private static void _migrate(PageContextImpl pc, Map<String, Scope> context, UserScope scope, boolean migrate) { 984 if(scope==null) return; 985 if(!migrate) scope.clear(); 986 scope.resetEnv(pc); 987 context.put(pc.getCFID(), scope); 988 } 989 990 991}