001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.type.scope.storage;
020
021import lucee.commons.io.IOUtil;
022import lucee.commons.io.log.Log;
023import lucee.commons.io.res.Resource;
024import lucee.commons.io.res.util.ResourceUtil;
025import lucee.commons.lang.ExceptionUtil;
026import lucee.commons.lang.StringUtil;
027import lucee.runtime.PageContext;
028import lucee.runtime.config.Config;
029import lucee.runtime.config.ConfigImpl;
030import lucee.runtime.config.ConfigWeb;
031import lucee.runtime.converter.ScriptConverter;
032import lucee.runtime.interpreter.CFMLExpressionInterpreter;
033import lucee.runtime.op.Caster;
034import lucee.runtime.type.Struct;
035import lucee.runtime.type.StructImpl;
036import lucee.runtime.type.dt.DateTime;
037import lucee.runtime.type.dt.DateTimeImpl;
038import lucee.runtime.type.scope.ScopeContext;
039
040/**
041 * client scope that store it's data in a resource
042 */
043public abstract class StorageScopeFile extends StorageScopeImpl {
044
045        private static final long serialVersionUID = -7519591903822909934L;
046
047        public static final String STORAGE_TYPE = "File"; 
048        
049        private static ScriptConverter serializer=new ScriptConverter();
050        protected static CFMLExpressionInterpreter evaluator=new CFMLExpressionInterpreter(false);
051        
052        private Resource res;
053
054        /**
055         * Constructor of the class
056         * @param pc
057         * @param name
058         * @param sct
059         */
060        protected StorageScopeFile(PageContext pc,Resource res,String strType,int type,Struct sct) {
061                super(
062                                sct==null?(sct=new StructImpl()):sct,
063                                doNowIfNull(pc,Caster.toDate(sct.get(TIMECREATED,null),false,pc.getTimeZone(),null)),
064                                doNowIfNull(pc,Caster.toDate(sct.get(LASTVISIT,null),false,pc.getTimeZone(),null)),
065                                -1,
066                                type==SCOPE_CLIENT?Caster.toIntValue(sct.get(HITCOUNT,"1"),1):0,
067                                strType,type);
068                
069                this.res =res;// pc.getConfig().getClientScopeDir().getRealResource(name+"-"+pc.getCFID()+".script");
070                
071        }
072        private static DateTime doNowIfNull(PageContext pc,DateTime dt) {
073                if(dt==null)return new DateTimeImpl(pc.getConfig());
074                return dt;
075        }
076
077        /**
078         * Constructor of the class, clone existing
079         * @param other
080         */
081        protected StorageScopeFile(StorageScopeFile other,boolean deepCopy) {
082                super(other,deepCopy);
083                this.res=other.res;
084        }
085
086        @Override
087        public void touchBeforeRequest(PageContext pc) {
088                setTimeSpan(pc);
089                super.touchBeforeRequest(pc);
090        }
091
092        @Override
093        public void touchAfterRequest(PageContext pc) {
094                setTimeSpan(pc);
095                super.touchAfterRequest(pc);
096                store(pc.getConfig());
097        }
098        
099        
100        
101        @Override
102        public void store(Config config) {
103                //if(!super.hasContent()) return;
104                try {
105                        if(!res.exists())ResourceUtil.createFileEL(res, true);
106                        IOUtil.write(res, (getTimeSpan()+System.currentTimeMillis())+":"+serializer.serializeStruct(sct, ignoreSet), "UTF-8", false);
107                } 
108                catch (Throwable t) {
109                        ExceptionUtil.rethrowIfNecessary(t);
110                }
111        }
112        
113        protected static Struct _loadData(PageContext pc,Resource res, Log log) {
114                if(res.exists()) {
115                        try {
116                                String str=IOUtil.toString(res,"UTF-8");
117                                int index=str.indexOf(':');
118                                if(index!=-1){
119                                        long expires=Caster.toLongValue(str.substring(0,index),-1L);
120                                        // check is for backward compatibility, old files have no expires date inside. they do ot expire
121                                        if(expires!=-1) {
122                                                str=str.substring(index+1);
123                                                /*if(checkExpires && expires<System.currentTimeMillis()){
124                                                        print.o("expired("+new Date(expires)+"):"+res);
125                                                        return null;
126                                                }
127                                                else {
128                                                        str=str.substring(index+1);
129                                                        print.o("not expired("+new Date(expires)+"):"+res);
130                                                        print.o(str);
131                                                }*/
132                                        }
133                                }
134                                Struct s = (Struct) evaluator.interpret(pc,str);
135                                ScopeContext.info(log,"load existing file storage ["+res+"]");
136                                return s;
137                        } 
138                        catch (Throwable t) {
139                                ExceptionUtil.rethrowIfNecessary(t);
140                                ScopeContext.error(log, t);
141                        }
142                }
143                ScopeContext.info(log,"create new file storage ["+res+"]");
144                return null;
145        }
146        
147        
148        
149        public void unstore(Config config) {
150                try {
151                        if(!res.exists())return;
152                        res.remove(true);
153                } 
154                catch (Throwable t) {
155                        ExceptionUtil.rethrowIfNecessary(t);
156                }
157        }
158        
159        protected static Resource _loadResource(ConfigWeb config, int type,String name, String cfid) {
160                ConfigImpl ci = (ConfigImpl)config;
161                Resource dir= type==SCOPE_CLIENT?ci.getClientScopeDir():ci.getSessionScopeDir();
162                return   dir.getRealResource(getFolderName(name,cfid,true));
163        }
164        
165
166        
167        /**
168         * return a folder name that match given input
169         * @param name
170         * @param cfid
171         * @param addExtension
172         * @return
173         */
174        public static String getFolderName(String name, String cfid,boolean addExtension) {
175                if(addExtension) return getFolderName(name, cfid, false)+".scpt";
176                if(!StringUtil.isEmpty(name))
177                        name=encode(name);//StringUtil.toVariableName(StringUtil.toLowerCase(name));
178                else 
179                        name="__empty__";
180                return name+"/"+cfid.substring(0,2)+"/"+cfid.substring(2);
181        }
182        
183        
184        @Override
185        public String getStorageType() {
186                return STORAGE_TYPE;
187        }
188        
189        
190}