001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 * Copyright (c) 2016, Lucee Assosication Switzerland
005 *
006 * This library is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either 
009 * version 2.1 of the License, or (at your option) any later version.
010 * 
011 * This library is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 * 
016 * You should have received a copy of the GNU Lesser General Public 
017 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
018 * 
019 **/
020package lucee.runtime.type.scope.storage;
021
022import java.io.IOException;
023
024import lucee.commons.io.cache.Cache;
025import lucee.commons.io.log.Log;
026import lucee.runtime.PageContext;
027import lucee.runtime.cache.CacheConnection;
028import lucee.runtime.cache.CacheUtil;
029import lucee.runtime.config.Config;
030import lucee.runtime.exp.ApplicationException;
031import lucee.runtime.exp.PageException;
032import lucee.runtime.functions.cache.Util;
033import lucee.runtime.op.Caster;
034import lucee.runtime.type.Struct;
035import lucee.runtime.type.dt.DateTime;
036import lucee.runtime.type.dt.DateTimeImpl;
037import lucee.runtime.type.scope.ScopeContext;
038import lucee.runtime.type.util.StructUtil;
039
040/**
041 * client scope that store it's data in a datasource
042 */
043public abstract class StorageScopeCache extends StorageScopeImpl {
044
045        private static final long serialVersionUID = 6234854552927320080L;
046        public static final long SAVE_EXPIRES_OFFSET = 60*60*1000;
047
048        private final String cacheName;
049        private final String appName;
050        private final String cfid;
051
052        private long lastModified;
053
054        /**
055         * Constructor of the class
056         * @param pc
057         * @param name
058         * @param sct
059         * @param b 
060         */
061        protected StorageScopeCache(PageContext pc,String cacheName, String appName,String strType,int type,Struct sct, long lastModified) { 
062                // !!! do not store the pagecontext or config object, this object is Serializable !!!
063                super(
064                                sct,
065                                doNowIfNull(pc.getConfig(),Caster.toDate(sct.get(TIMECREATED,null),false,pc.getTimeZone(),null)),
066                                doNowIfNull(pc.getConfig(),Caster.toDate(sct.get(LASTVISIT,null),false,pc.getTimeZone(),null)),
067                                -1, 
068                                type==SCOPE_CLIENT?Caster.toIntValue(sct.get(HITCOUNT,"1"),1):1
069                                ,strType,type);
070                
071                //this.isNew=isNew;
072                this.appName=appName;
073                this.cacheName=cacheName;
074                this.cfid=pc.getCFID();
075                this.lastModified=lastModified;
076        }
077        
078        public long lastModified() {
079                return lastModified;
080        }
081
082        /**
083         * Constructor of the class, clone existing
084         * @param other
085         */
086        protected StorageScopeCache(StorageScopeCache other,boolean deepCopy) {
087                super(other,deepCopy);
088                
089                this.appName=other.appName;
090                this.cacheName=other.cacheName;
091                this.cfid=other.cfid;
092                this.lastModified=other.lastModified;
093        }
094        
095        private static DateTime doNowIfNull(Config config,DateTime dt) {
096                if(dt==null)return new DateTimeImpl(config);
097                return dt;
098        }
099        
100        @Override
101        public void touchAfterRequest(PageContext pc) {
102                setTimeSpan(pc);
103                super.touchAfterRequest(pc);
104                //if(super.hasContent()) 
105                        store(pc.getConfig());
106        }
107
108        @Override
109        public String getStorageType() {
110                return "Cache";
111        }
112
113        @Override
114        public void touchBeforeRequest(PageContext pc) {
115                setTimeSpan(pc);
116                super.touchBeforeRequest(pc);
117        }
118        
119        protected static StorageValue _loadData(PageContext pc, String cacheName, String appName, String strType, Log log) throws PageException {
120                Cache cache = getCache(pc.getConfig(),cacheName);
121                String key=getKey(pc.getCFID(),appName,strType);
122                
123                Object val = cache.getValue(key,null);
124                
125                if(val instanceof StorageValue) {
126                        ScopeContext.info(log,"load existing data from  cache ["+cacheName+"] to create "+strType+" scope for "+pc.getApplicationContext().getName()+"/"+pc.getCFID());
127                        return (StorageValue)val;
128                }
129                ScopeContext.info(log,"create new "+strType+" scope for "+pc.getApplicationContext().getName()+"/"+pc.getCFID()+" in cache ["+cacheName+"]");
130                return null;
131        }
132
133        public synchronized void store(Config config) {
134                try {
135                        Cache cache = getCache(config, cacheName);
136                        String key=getKey(cfid, appName, getTypeAsString());
137                        
138                        /* / merge existing data if necessary ; MARK disabled merge
139                        Object existing = cache.getValue(key,null);
140                        // cached data changed in meantime
141                        if(existing instanceof StorageValue  && ((StorageValue)existing).lastModified()>lastModified()) {
142                                Struct trg=((StorageValue)existing).getValue();
143                                StructUtil.copy(sct, trg, true);
144                                sct=trg;
145                        }*/
146
147                        cache.put(key, new StorageValue(sct),null,new Long(getTimeSpan()));
148                } 
149                catch (Exception pe) {pe.printStackTrace();}
150        }
151        
152        
153        public synchronized void unstore(Config config) {
154                try {
155                        Cache cache = getCache(config, cacheName);
156                        String key=getKey(cfid, appName, getTypeAsString());
157                        cache.remove(key);
158                } 
159                catch (Exception pe) {}
160        }
161        
162
163        private static Cache getCache(Config config, String cacheName) throws PageException {
164                try {
165                        CacheConnection cc = Util.getCacheConnection(config,cacheName);
166                        if(!cc.isStorage()) 
167                                throw new ApplicationException("storage usage for this cache is disabled, you can enable this in the lucee administrator.");
168                        return CacheUtil.getInstance(cc,config); //cc.getInstance(config); 
169
170                } catch (IOException e) {
171                        throw Caster.toPageException(e);
172                }
173        }
174        
175
176        public static String getKey(String cfid, String appName, String type) {
177                return new StringBuilder("lucee-storage:").append(type).append(":").append(cfid).append(":").append(appName).toString().toUpperCase();
178        }
179}