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