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