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