/** * Copyright (c) 2014, the Railo Company Ltd. * Copyright (c) 2015, 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.listener; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Appender; import org.apache.log4j.HTMLLayout; import org.apache.log4j.Level; import org.apache.log4j.PatternLayout; import org.apache.log4j.xml.XMLLayout; import org.osgi.framework.Version; import lucee.print; import lucee.commons.io.log.Log; import lucee.commons.io.log.LoggerAndSourceData; import lucee.commons.io.log.log4j.Log4jUtil; import lucee.commons.io.log.log4j.appender.ConsoleAppender; import lucee.commons.io.log.log4j.appender.DatasourceAppender; import lucee.commons.io.log.log4j.appender.RollingResourceAppender; import lucee.commons.io.log.log4j.layout.ClassicLayout; import lucee.commons.io.res.Resource; import lucee.commons.lang.Pair; import lucee.commons.lang.StringUtil; import lucee.runtime.Mapping; import lucee.runtime.PageContext; import lucee.runtime.cache.CacheConnection; import lucee.runtime.config.Config; import lucee.runtime.config.ConfigImpl; import lucee.runtime.config.ConfigWeb; import lucee.runtime.config.XMLConfigWebFactory; import lucee.runtime.db.ClassDefinition; import lucee.runtime.db.DataSource; import lucee.runtime.exp.ApplicationException; import lucee.runtime.functions.dynamicEvaluation.GetVariable; import lucee.runtime.op.Caster; import lucee.runtime.op.Duplicator; import lucee.runtime.osgi.OSGiUtil; import lucee.runtime.type.Collection; import lucee.runtime.type.Collection.Key; import lucee.runtime.type.KeyImpl; import lucee.runtime.type.Struct; import lucee.runtime.type.util.ArrayUtil; import lucee.transformer.library.ClassDefinitionImpl; import lucee.transformer.library.tag.TagLib; import lucee.transformer.library.tag.TagLibTag; import lucee.transformer.library.tag.TagLibTagAttr; public abstract class ApplicationContextSupport implements ApplicationContext { private static final long serialVersionUID = 1384678713928757744L; private static Map<Collection.Key,LoggerAndSourceData> _loggers=new ConcurrentHashMap<Collection.Key, LoggerAndSourceData>(); protected int idletimeout=1800; protected String cookiedomain; protected String applicationtoken; private Map<Collection.Key,Map<Collection.Key,Object>> tagDefaultAttributeValues=null; protected Object cachedWithinFunction; protected Object cachedWithinInclude; protected Object cachedWithinQuery; protected Object cachedWithinResource; protected Object cachedWithinHTTP; protected Object cachedWithinFile; protected Object cachedWithinWS; protected ConfigWeb config; public ApplicationContextSupport(ConfigWeb config) { this.config=config; tagDefaultAttributeValues=((ConfigImpl)config).getTagDefaultAttributeValues(); cachedWithinFunction=config.getCachedWithin(Config.CACHEDWITHIN_FUNCTION); cachedWithinInclude=config.getCachedWithin(Config.CACHEDWITHIN_INCLUDE); cachedWithinQuery=config.getCachedWithin(Config.CACHEDWITHIN_QUERY); cachedWithinResource=config.getCachedWithin(Config.CACHEDWITHIN_RESOURCE); cachedWithinHTTP=config.getCachedWithin(Config.CACHEDWITHIN_HTTP); cachedWithinFile=config.getCachedWithin(Config.CACHEDWITHIN_FILE); cachedWithinWS=config.getCachedWithin(Config.CACHEDWITHIN_WEBSERVICE); } protected void _duplicate(ApplicationContextSupport other) { idletimeout=other.idletimeout; cookiedomain=other.cookiedomain; applicationtoken=other.applicationtoken; if(other.tagDefaultAttributeValues!=null) { tagDefaultAttributeValues=new HashMap<Collection.Key, Map<Collection.Key,Object>>(); Iterator<Entry<Collection.Key, Map<Collection.Key, Object>>> it = other.tagDefaultAttributeValues.entrySet().iterator(); Entry<Collection.Key, Map<Collection.Key, Object>> e; Iterator<Entry<Collection.Key, Object>> iit; Entry<Collection.Key, Object> ee; Map<Collection.Key, Object> map; while(it.hasNext()){ e = it.next(); iit=e.getValue().entrySet().iterator(); map=new HashMap<Collection.Key, Object>(); while(iit.hasNext()){ ee = iit.next(); map.put(ee.getKey(), ee.getValue()); } tagDefaultAttributeValues.put(e.getKey(), map); } } other.cachedWithinFile=Duplicator.duplicate(cachedWithinFile, true); other.cachedWithinFunction=Duplicator.duplicate(cachedWithinFunction, true); other.cachedWithinHTTP=Duplicator.duplicate(cachedWithinHTTP, true); other.cachedWithinInclude=Duplicator.duplicate(cachedWithinInclude, true); other.cachedWithinQuery=Duplicator.duplicate(cachedWithinQuery, true); other.cachedWithinResource=Duplicator.duplicate(cachedWithinResource, true); other.cachedWithinWS=Duplicator.duplicate(cachedWithinWS, true); } @Override public void setSecuritySettings(String applicationtoken, String cookiedomain, int idletimeout) { this.applicationtoken=applicationtoken; this.cookiedomain=cookiedomain; this.idletimeout=idletimeout; } @Override public String getSecurityApplicationToken() { if(StringUtil.isEmpty(applicationtoken,true)) return getName(); return applicationtoken; } @Override public String getSecurityCookieDomain() { if(StringUtil.isEmpty(applicationtoken,true)) return null; return cookiedomain; } @Override public int getSecurityIdleTimeout() { if(idletimeout<1) return 1800; return idletimeout; } @Override public DataSource getDataSource(String dataSourceName, DataSource defaultValue) { if(dataSourceName==null) return defaultValue; dataSourceName=dataSourceName.trim(); DataSource[] sources = getDataSources(); if(!ArrayUtil.isEmpty(sources)) { for(int i=0;i<sources.length;i++){ if(sources[i].getName().equalsIgnoreCase(dataSourceName)) return sources[i]; } } return defaultValue; } @Override public DataSource getDataSource(String dataSourceName) throws ApplicationException { DataSource source = getDataSource(dataSourceName,null); if(source==null) throw new ApplicationException("there is no datasource with name ["+dataSourceName+"]"); return source; } @Override public Map<Collection.Key, Map<Collection.Key, Object>> getTagAttributeDefaultValues(PageContext pc) { return tagDefaultAttributeValues; } @Override public Map<Collection.Key, Object> getTagAttributeDefaultValues(PageContext pc,String fullname) { if(tagDefaultAttributeValues==null) return null; return tagDefaultAttributeValues.get(KeyImpl.init(fullname)); } @Override public void setTagAttributeDefaultValues(PageContext pc,Struct sct) { if(tagDefaultAttributeValues==null) tagDefaultAttributeValues=new HashMap<Collection.Key, Map<Collection.Key,Object>>(); initTagDefaultAttributeValues(config, tagDefaultAttributeValues, sct, pc.getCurrentTemplateDialect()); } public static void initTagDefaultAttributeValues(Config config,Map<Collection.Key, Map<Collection.Key, Object>> tagDefaultAttributeValues, Struct sct, int dialect) { if(sct.size()==0) return; ConfigImpl ci = ((ConfigImpl)config); // first check the core lib without namespace TagLib lib = ci.getCoreTagLib(dialect); _initTagDefaultAttributeValues(config, lib, tagDefaultAttributeValues, sct,false); if(sct.size()==0) return; // then all the other libs including the namespace TagLib[] tlds = ci.getTLDs(dialect); for(int i=0;i<tlds.length;i++){ _initTagDefaultAttributeValues(config, tlds[i], tagDefaultAttributeValues, sct,true); if(sct.size()==0) return; } } private static void _initTagDefaultAttributeValues(Config config,TagLib lib, Map<Collection.Key, Map<Collection.Key, Object>> tagDefaultAttributeValues, Struct sct, boolean checkNameSpace) { if(sct==null) return; Iterator<Entry<Key, Object>> it = sct.entryIterator(); // loop tags Struct attrs; TagLibTag tag; Iterator<Entry<Key, Object>> iit; Entry<Key, Object> e; Map<Collection.Key,Object> map; TagLibTagAttr attr; String name; while(it.hasNext()){ e = it.next(); attrs=Caster.toStruct(e.getValue(),null); if(attrs!=null){ tag=null; if(checkNameSpace) { name=e.getKey().getLowerString(); if(StringUtil.startsWithIgnoreCase(name, lib.getNameSpaceAndSeparator())) { name=name.substring(lib.getNameSpaceAndSeparator().length()); tag = lib.getTag(name); } } else tag = lib.getTag(e.getKey().getLowerString()); if(tag!=null) { sct.removeEL(e.getKey()); map=new HashMap<Collection.Key, Object>(); iit = attrs.entryIterator(); while(iit.hasNext()){ e = iit.next(); map.put(KeyImpl.init(e.getKey().getLowerString()),e.getValue()); } tagDefaultAttributeValues.put(KeyImpl.init(tag.getFullName()), map); } } } } @Override public final void setCachedWithin(int type, Object value) { if(StringUtil.isEmpty(value)) return; switch(type) { case Config.CACHEDWITHIN_FUNCTION: cachedWithinFunction=value; break; case Config.CACHEDWITHIN_INCLUDE: cachedWithinInclude=value; break; case Config.CACHEDWITHIN_QUERY: cachedWithinQuery=value; break; case Config.CACHEDWITHIN_RESOURCE: cachedWithinResource=value; break; case Config.CACHEDWITHIN_HTTP: cachedWithinHTTP=value; break; case Config.CACHEDWITHIN_FILE: cachedWithinFile=value; break; case Config.CACHEDWITHIN_WEBSERVICE: cachedWithinWS=value; break; } } @Override public Object getCachedWithin(int type) { switch(type) { case Config.CACHEDWITHIN_FUNCTION: return cachedWithinFunction; case Config.CACHEDWITHIN_INCLUDE: return cachedWithinInclude; case Config.CACHEDWITHIN_QUERY: return cachedWithinQuery; case Config.CACHEDWITHIN_RESOURCE: return cachedWithinResource; case Config.CACHEDWITHIN_HTTP: return cachedWithinHTTP; case Config.CACHEDWITHIN_FILE: return cachedWithinFile; case Config.CACHEDWITHIN_WEBSERVICE: return cachedWithinWS; } return null; } public static Map<Collection.Key,Pair<Log,Struct>> initLog(Struct sct) { Map<Collection.Key,Pair<Log,Struct>> rtn=new ConcurrentHashMap<Collection.Key,Pair<Log,Struct>>(); if(sct==null) return rtn; Iterator<Entry<Key, Object>> it = sct.entryIterator(); Entry<Key, Object> e; Struct v; int k; Collection.Key name; LoggerAndSourceData las; while(it.hasNext()){ e = it.next(); name=e.getKey(); v=Caster.toStruct(e.getValue(),null); if(v==null) continue; // raw way Struct sctApp=Caster.toStruct(v.get("appender",null),null); ClassDefinition cdApp=toClassDefinition(sctApp,null,true,false); Struct sctLay=Caster.toStruct(v.get("layout",null),null); ClassDefinition cdLay=toClassDefinition(sctLay,null,false,true); if(cdApp!=null && cdApp.hasClass()) { // level String strLevel=Caster.toString(v.get("level",null),null); if(StringUtil.isEmpty(strLevel,true))Caster.toString(v.get("loglevel",null),null); Level level=Log4jUtil.toLevel(StringUtil.trim(strLevel,""),Level.ERROR); Struct sctAppArgs=Caster.toStruct(sctApp.get("arguments",null),null); Struct sctLayArgs=Caster.toStruct(sctLay.get("arguments",null),null); boolean readOnly = Caster.toBooleanValue(v.get("readonly",null),false); // ignore when no appender/name is defined if(!StringUtil.isEmpty(name)) { Map<String, String> appArgs = toMap(sctAppArgs); if(cdLay!=null && cdLay.hasClass()) { Map<String, String> layArgs = toMap(sctLayArgs); las=addLogger(name,level,cdApp,appArgs,cdLay,layArgs,readOnly); } else las=addLogger(name,level,cdApp,appArgs,null,null,readOnly); rtn.put(name, new Pair<Log,Struct>(las.getLog(),v)); } } } return rtn; } private static Map<String, String> toMap(Struct sct) { Iterator<Entry<Key, Object>> it = sct.entryIterator(); Map<String, String> map=new HashMap<String, String>(); Entry<Key, Object> e; while(it.hasNext()) { e = it.next(); map.put(e.getKey().getLowerString(), Caster.toString(e.getValue(),null)); } return map; } private static LoggerAndSourceData addLogger(Collection.Key name, Level level, ClassDefinition appender, Map<String, String> appenderArgs, ClassDefinition layout, Map<String, String> layoutArgs, boolean readOnly) { LoggerAndSourceData existing = _loggers.get(name); String id=LoggerAndSourceData.id(name.getLowerString(), appender,appenderArgs,layout,layoutArgs,level,readOnly); if(existing!=null) { if(existing.id().equals(id)) { return existing; } existing.close(); } LoggerAndSourceData las = new LoggerAndSourceData(null,id,name.getLowerString(), appender,appenderArgs,layout,layoutArgs,level,readOnly,true); _loggers.put(name,las); return las; } public static ClassDefinition toClassDefinition(Struct sct, ClassDefinition defaultValue, boolean isAppender, boolean isLayout) { if(sct==null) return defaultValue; // class String className=Caster.toString(sct.get("class",null),null); if(StringUtil.isEmpty(className)) return defaultValue; if(isAppender) { if("console".equalsIgnoreCase(className)) return new ClassDefinitionImpl( ConsoleAppender.class); if("resource".equalsIgnoreCase(className)) return new ClassDefinitionImpl( RollingResourceAppender.class); if("datasource".equalsIgnoreCase(className))return new ClassDefinitionImpl( DatasourceAppender.class); } else if(isLayout) { if("classic".equalsIgnoreCase(className))return new ClassDefinitionImpl( ClassicLayout.class); if("html".equalsIgnoreCase(className))return new ClassDefinitionImpl( HTMLLayout.class); if("xml".equalsIgnoreCase(className))return new ClassDefinitionImpl( XMLLayout.class); if("pattern".equalsIgnoreCase(className))return new ClassDefinitionImpl( PatternLayout.class); } // name String name=Caster.toString(sct.get("bundlename",null),null); if(StringUtil.isEmpty(name)) name=Caster.toString(sct.get("name",null),null); // version Version version=OSGiUtil.toVersion(Caster.toString(sct.get("bundleversion",null),null),null); if(version==null) version=OSGiUtil.toVersion(Caster.toString(sct.get("version",null),null),null); if(StringUtil.isEmpty(name)) return new ClassDefinitionImpl(className); return new ClassDefinitionImpl(null,className, name, version); } // FUTURE add to interface public abstract Resource getAntiSamyPolicyResource(); public abstract void setAntiSamyPolicyResource(Resource res); public abstract CacheConnection getCacheConnection(String cacheName, CacheConnection defaultValue); public abstract Key[] getCacheConnectionNames(); public abstract void setCacheConnection(String cacheName, CacheConnection value); public abstract SessionCookieData getSessionCookie(); public abstract void setSessionCookie(SessionCookieData data); public abstract AuthCookieData getAuthCookie(); public abstract void setAuthCookie(AuthCookieData data); public abstract lucee.runtime.net.mail.Server[] getMailServers(); public abstract void setMailServers(lucee.runtime.net.mail.Server[] servers); public abstract void setLoggers(Map<Key, Pair<Log,Struct>> logs); public abstract java.util.Collection<Collection.Key> getLogNames(); public abstract Log getLog(String name); public abstract Struct getLogMetaData(String string); }