/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * 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.cache; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Iterator; import lucee.commons.io.cache.Cache; import lucee.commons.io.cache.CacheEntry; import lucee.commons.io.cache.CacheEntryFilter; import lucee.commons.io.cache.CacheFilter; import lucee.commons.io.cache.exp.CacheException; import lucee.commons.lang.ExceptionUtil; import lucee.commons.lang.StringUtil; import lucee.loader.engine.CFMLEngine; import lucee.runtime.PageContext; import lucee.runtime.PageContextImpl; import lucee.runtime.config.Config; import lucee.runtime.config.ConfigImpl; import lucee.runtime.config.ConfigWeb; import lucee.runtime.config.Constants; import lucee.runtime.config.Password; import lucee.runtime.config.PasswordImpl; import lucee.runtime.engine.ThreadLocalPageContext; import lucee.runtime.exp.PageException; import lucee.runtime.listener.ApplicationContext; import lucee.runtime.listener.ModernApplicationContext; import lucee.runtime.op.Caster; import lucee.runtime.type.Struct; import lucee.runtime.type.StructImpl; import lucee.runtime.type.dt.TimeSpan; import lucee.runtime.type.dt.TimeSpanImpl; import lucee.runtime.type.util.KeyConstants; public class CacheUtil { /** * get the default cache for a certain type, also check definitions in application context (application . cfc/cfapplication) * @param pc current PageContext * @param type default type -> Config.CACHE_DEFAULT_... * @param defaultValue value returned when there is no default cache for this type * @return matching cache */ public static Cache getDefault(PageContext pc, int type,Cache defaultValue) { // get default from application conetx String name=null; if(pc!=null && pc.getApplicationContext()!=null) name=pc.getApplicationContext().getDefaultCacheName(type); Config config=ThreadLocalPageContext.getConfig(pc); if(!StringUtil.isEmpty(name)){ Cache cc = getCache(pc, name, null); if(cc!=null) return cc; } // get default from config CacheConnection cc= ((ConfigImpl)config).getCacheDefaultConnection(type); if(cc==null) return defaultValue; try { return cc.getInstance(config); } catch(Throwable t) { ExceptionUtil.rethrowIfNecessary(t); return defaultValue; } } /** * get the default cache for a certain type, also check definitions in application context (application . cfc/cfapplication) * @param pc current PageContext * @param type default type -> Config.CACHE_DEFAULT_... * @return matching cache * @throws IOException */ public static Cache getDefault(PageContext pc, int type) throws IOException { // get default from application conetxt String name=pc!=null?pc.getApplicationContext().getDefaultCacheName(type):null; if(!StringUtil.isEmpty(name)){ Cache cc = getCache(pc, name, null); if(cc!=null) return cc; } // get default from config Config config = ThreadLocalPageContext.getConfig(pc); CacheConnection cc= ((ConfigImpl)config).getCacheDefaultConnection(type); if(cc==null) throw new CacheException("there is no default "+toStringType(type,"")+" cache defined, you need to define this default cache in the Lucee Administrator"); return cc.getInstance(config); } public static Cache getCache(PageContext pc,String cacheName, int type) throws IOException { if(StringUtil.isEmpty(cacheName)) return getDefault(pc, type); return getCache(pc, cacheName); } public static Cache getCache(PageContext pc,String cacheName, int type, Cache defaultValue) { if(StringUtil.isEmpty(cacheName)) return getDefault(pc, type,defaultValue); return getCache(pc, cacheName, defaultValue); } public static Cache getCache(PageContext pc, String cacheName) throws IOException { CacheConnection cc = getCacheConnection(pc, cacheName); return cc.getInstance(ThreadLocalPageContext.getConfig(pc)); } public static Cache getCache(PageContext pc, String cacheName, Cache defaultValue) { CacheConnection cc = getCacheConnection(pc, cacheName,null); if(cc==null) return defaultValue; Config config = ThreadLocalPageContext.getConfig(pc); try { return cc.getInstance(config); } catch(Throwable t) { ExceptionUtil.rethrowIfNecessary(t); return defaultValue; } } public static CacheConnection getCacheConnection(PageContext pc,String cacheName) throws IOException { pc=ThreadLocalPageContext.get(pc); if(pc!=null) return ((PageContextImpl)pc).getCacheConnection(cacheName); Config config = ThreadLocalPageContext.getConfig(pc); CacheConnection cc = config.getCacheConnections().get(cacheName.toLowerCase().trim()); if(cc==null) throw noCache(config,cacheName); return cc; } public static CacheConnection getCacheConnection(PageContext pc,String cacheName, CacheConnection defaultValue) { pc=ThreadLocalPageContext.get(pc); if(pc!=null) return ((PageContextImpl)pc).getCacheConnection(cacheName,null); Config config = ThreadLocalPageContext.getConfig(pc); CacheConnection cc= config.getCacheConnections().get(cacheName.toLowerCase().trim()); if(cc==null) return defaultValue; return cc; } public static CacheException noCache(Config config, String cacheName) { StringBuilder sb=new StringBuilder("there is no cache defined with name [").append(cacheName).append("], available caches are ["); Iterator<String> it = ((ConfigImpl)config).getCacheConnections().keySet().iterator(); if(it.hasNext()){ sb.append(it.next()); } while(it.hasNext()){ sb.append(", ").append(it.next()); } sb.append("]"); return new CacheException(sb.toString()); } public static Struct getInfo(CacheEntry ce) { return getInfo(new StructImpl(), ce); } public static Struct getInfo(Struct info,CacheEntry ce) { if(info==null) info=new StructImpl(); info.setEL("key", ce.getKey()); info.setEL("created", ce.created()); info.setEL("last_hit", ce.lastHit()); info.setEL("last_modified", ce.lastModified()); info.setEL("hit_count", new Double(ce.hitCount())); info.setEL("size", new Double(ce.size())); info.setEL("idle_time_span", toTimespan(ce.idleTimeSpan())); info.setEL("live_time_span", toTimespan(ce.liveTimeSpan())); return info; } public static Struct getInfo(Cache c) { return getInfo(new StructImpl(),c); } public static Struct getInfo(Struct info, Cache c) { if(info==null) info=new StructImpl(); try{ long value = c.hitCount(); if(value>=0)info.setEL("hit_count", new Double(value)); } catch(IOException ioe){ // simply ignore } try{ long value = c.missCount(); if(value>=0)info.setEL("miss_count", new Double(value)); } catch(IOException ioe){ // simply ignore } return info; } public static Object toTimespan(long timespan) { if(timespan==0)return ""; TimeSpan ts = TimeSpanImpl.fromMillis(timespan); if(ts==null)return ""; return ts; } public static String toString(CacheEntry ce) { return "created: "+ce.created() +"\nlast-hit: "+ce.lastHit() +"\nlast-modified: "+ce.lastModified() +"\nidle-time: "+ce.idleTimeSpan() +"\nlive-time :"+ce.liveTimeSpan() +"\nhit-count: "+ce.hitCount() +"\nsize: "+ce.size(); } public static boolean allowAll(CacheFilter filter) { if(filter==null)return true; String p = StringUtil.trim(filter.toPattern(),""); return p.equals("*") || p.equals(""); } /** * in difference to the getInstance method of the CacheConnection this method produces a wrapper Cache (if necessary) that creates Entry objects to make sure the Cache has meta data. * @param cc * @param config * @return * @throws IOException */ public static Cache getInstance(CacheConnection cc, Config config) throws IOException { return cc.getInstance(config); /*Cache c = cc.getInstance(config); if("org.lucee.extension.io.cache.memcache.MemCacheRaw".equals(c.getClass().getName())) { return new CacheComplex(cc,c); } return c;*/ } public static boolean removeEL(ConfigWeb config, CacheConnection cc) { try { remove(config,cc); return true; } catch (Throwable e) { ExceptionUtil.rethrowIfNecessary(e); return false; } } public static void remove(ConfigWeb config, CacheConnection cc) throws Throwable { Cache c = cc.getInstance(config); // FUTURE no reflection needed Method remove=null; try{ remove = c.getClass().getMethod("remove", new Class[0]); } catch(Exception ioe){ c.remove((CacheEntryFilter)null); return; } try { remove.invoke(c, new Object[0]); } catch (InvocationTargetException e) { throw e.getTargetException(); } } public static void releaseEL(CacheConnection cc) { try { release(cc); } catch (IOException e) {} } public static void release(CacheConnection cc) throws IOException { Cache c = ((CacheConnectionPlus)cc).getLoadedInstance(); if(c==null) return; // FUTURE no reflection needed Method release=null; try{ release = c.getClass().getMethod("release", new Class[]{}); } catch(Exception e){ return; } try { if(release!=null)release.invoke(c, new Object[]{}); } catch (Exception e) { throw ExceptionUtil.toIOException(e); } } public static void releaseAll(Config config) { // Config for(CacheConnection cc:config.getCacheConnections().values()) { releaseEL(cc); } } public static void releaseAllApplication() { // application defined caches (modern and classic) ModernApplicationContext.releaseInitCacheConnections(); } private static String toStringType(int type, String defaultValue) { if(type==Config.CACHE_TYPE_OBJECT) return "object"; if(type==Config.CACHE_TYPE_TEMPLATE) return "template"; if(type==Config.CACHE_TYPE_QUERY) return "query"; if(type==Config.CACHE_TYPE_RESOURCE) return "resource"; if(type==Config.CACHE_TYPE_FUNCTION) return "function"; if(type==Config.CACHE_TYPE_INCLUDE) return "include"; if(type==Config.CACHE_TYPE_HTTP) return "http"; if(type==Config.CACHE_TYPE_FILE) return "file"; if(type==Config.CACHE_TYPE_WEBSERVICE) return "webservice"; return defaultValue; } public static String key(String key) { return key.toUpperCase().trim(); } public static int toType(String type, int defaultValue) { type=type.trim().toLowerCase(); if("object".equals(type)) return Config.CACHE_TYPE_OBJECT; if("query".equals(type)) return Config.CACHE_TYPE_QUERY; if("resource".equals(type)) return Config.CACHE_TYPE_RESOURCE; if("template".equals(type)) return Config.CACHE_TYPE_TEMPLATE; if("function".equals(type)) return Config.CACHE_TYPE_FUNCTION; if("include".equals(type)) return Config.CACHE_TYPE_INCLUDE; if("http".equals(type)) return Config.CACHE_TYPE_HTTP; if("file".equals(type)) return Config.CACHE_TYPE_FILE; if("webservice".equals(type)) return Config.CACHE_TYPE_WEBSERVICE; return defaultValue; } public static String toType(int type, String defaultValue) { if(Config.CACHE_TYPE_OBJECT==type) return "object"; if(Config.CACHE_TYPE_QUERY==type) return "query"; if(Config.CACHE_TYPE_RESOURCE==type) return "resource"; if(Config.CACHE_TYPE_TEMPLATE==type) return "template"; if(Config.CACHE_TYPE_FUNCTION==type) return "function"; if(Config.CACHE_TYPE_INCLUDE==type) return "include"; if(Config.CACHE_TYPE_HTTP==type) return "http"; if(Config.CACHE_TYPE_FILE==type) return "file"; if(Config.CACHE_TYPE_WEBSERVICE==type) return "webservice"; return defaultValue; } /** * returns true if the webAdminPassword matches the passed password if one is passed, or a password defined * in Application . cfc as this.webAdminPassword if null or empty-string is passed for password * * @param pc * @param password * @return * @throws lucee.runtime.exp.SecurityException */ public static Password getPassword( PageContext pc, String password , boolean server) throws lucee.runtime.exp.SecurityException { // TODO: move this to a utility class in a more generic package? // no password passed if (StringUtil.isEmpty(password,true)) { ApplicationContext appContext = pc.getApplicationContext(); if ( appContext instanceof ModernApplicationContext) password = Caster.toString( ( (ModernApplicationContext)appContext ).getCustom( KeyConstants._webAdminPassword ), "" ); } else password=password.trim(); if (StringUtil.isEmpty(password, true)) throw new lucee.runtime.exp.SecurityException( "A Web Admin Password is required to manipulate Cache connections. " + "You can either pass the password as an argument to this function, or set it in " +(pc.getRequestDialect()==CFMLEngine.DIALECT_CFML?Constants.CFML_APPLICATION_EVENT_HANDLER:Constants.LUCEE_APPLICATION_EVENT_HANDLER) +" with the variable [this.webAdminPassword]." ); return PasswordImpl.passwordToCompare(pc.getConfig(), server, password); } }