/**
*
* Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
* Copyright (c) 2016, Lucee Assosication Switzerland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
**/
package lucee.runtime.type.scope.storage;
import java.io.IOException;
import lucee.commons.io.cache.Cache;
import lucee.commons.io.log.Log;
import lucee.runtime.PageContext;
import lucee.runtime.cache.CacheConnection;
import lucee.runtime.cache.CacheUtil;
import lucee.runtime.config.Config;
import lucee.runtime.engine.ThreadLocalPageContext;
import lucee.runtime.exp.ApplicationException;
import lucee.runtime.exp.PageException;
import lucee.runtime.op.Caster;
import lucee.runtime.type.Struct;
import lucee.runtime.type.dt.DateTime;
import lucee.runtime.type.dt.DateTimeImpl;
import lucee.runtime.type.scope.ScopeContext;
import lucee.runtime.type.util.StructUtil;
/**
* client scope that store it's data in a datasource
*/
public abstract class StorageScopeCache extends StorageScopeImpl {
private static final long serialVersionUID = 6234854552927320080L;
public static final long SAVE_EXPIRES_OFFSET = 60*60*1000;
//private static ScriptConverter serializer=new ScriptConverter();
//boolean structOk;
private final String cacheName;
private final String appName;
private final String cfid;
private long lastModified;
/**
* Constructor of the class
* @param pc
* @param name
* @param sct
* @param b
*/
protected StorageScopeCache(PageContext pc,String cacheName, String appName,String strType,int type,Struct sct, long lastModified) {
// !!! do not store the pagecontext or config object, this object is Serializable !!!
super(
sct,
doNowIfNull(pc.getConfig(),Caster.toDate(sct.get(TIMECREATED,null),false,pc.getTimeZone(),null)),
doNowIfNull(pc.getConfig(),Caster.toDate(sct.get(LASTVISIT,null),false,pc.getTimeZone(),null)),
-1,
type==SCOPE_CLIENT?Caster.toIntValue(sct.get(HITCOUNT,"1"),1):1
,strType,type);
//this.isNew=isNew;
this.appName=appName;
this.cacheName=cacheName;
this.cfid=pc.getCFID();
this.lastModified=lastModified;
}
/**
* Constructor of the class, clone existing
* @param other
*/
protected StorageScopeCache(StorageScopeCache other,boolean deepCopy) {
super(other,deepCopy);
this.appName=other.appName;
this.cacheName=other.cacheName;
this.cfid=other.cfid;
this.lastModified=other.lastModified;
}
public long lastModified() {
return lastModified;
}
private static DateTime doNowIfNull(Config config,DateTime dt) {
if(dt==null)return new DateTimeImpl(config);
return dt;
}
@Override
public void touchAfterRequest(PageContext pc) {
setTimeSpan(pc);
super.touchAfterRequest(pc);
store(pc);
}
@Override
public String getStorageType() {
return "Cache";
}
@Override
public void touchBeforeRequest(PageContext pc) {
setTimeSpan(pc);
super.touchBeforeRequest(pc);
}
protected static StorageValue _loadData(PageContext pc, String cacheName, String appName, String strType, Log log) throws PageException {
Cache cache = getCache(pc,cacheName);
String key=getKey(pc.getCFID(),appName,strType);
Object val = cache.getValue(key,null);
if(val instanceof StorageValue) {
ScopeContext.info(log,"load existing data from cache ["+cacheName+"] to create "+strType+" scope for "+pc.getApplicationContext().getName()+"/"+pc.getCFID());
return (StorageValue)val;
}
else {
ScopeContext.info(log,"create new "+strType+" scope for "+pc.getApplicationContext().getName()+"/"+pc.getCFID()+" in cache ["+cacheName+"]");
}
return null;
}
@Override
public synchronized void store(PageContext pc) {
try {
Cache cache = getCache(ThreadLocalPageContext.get(pc), cacheName);
String key=getKey(cfid, appName, getTypeAsString());
Object existingVal = cache.getValue(key,null);
// cached data changed in meantime
if(existingVal instanceof StorageValue && ((StorageValue)existingVal).lastModified()>lastModified()) {
Struct trg=((Struct)((StorageValue)existingVal).getValue());
StructUtil.copy(sct, trg, true);
sct=trg;
}
cache.put(key, new StorageValue(sct),new Long(getTimeSpan()), null);
}
catch (Exception pe) {pe.printStackTrace();}
}
@Override
public synchronized void unstore(PageContext pc) {
try {
Cache cache = getCache(ThreadLocalPageContext.get(pc), cacheName);
String key=getKey(cfid, appName, getTypeAsString());
cache.remove(key);
}
catch (Exception pe) {}
}
private static Cache getCache(PageContext pc, String cacheName) throws PageException {
try {
CacheConnection cc = CacheUtil.getCacheConnection(pc,cacheName);
if(!cc.isStorage())
throw new ApplicationException("storage usage for this cache is disabled, you can enable this in the Lucee administrator.");
return CacheUtil.getInstance(cc,ThreadLocalPageContext.getConfig(pc)); //cc.getInstance(config);
} catch (IOException e) {
throw Caster.toPageException(e);
}
}
public static String getKey(String cfid, String appName, String type) {
return new StringBuilder("lucee-storage:").append(type).append(":").append(cfid).append(":").append(appName).toString().toUpperCase();
}
/*private void setTimeSpan(PageContext pc) {
ApplicationContext ac=(ApplicationContext) pc.getApplicationContext();
timespan = (getType()==SCOPE_CLIENT?ac.getClientTimeout().getMillis():ac.getSessionTimeout().getMillis())+(expiresControlFromOutside?SAVE_EXPIRES_OFFSET:0L);
}*/
}