package railo.runtime;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.commons.collections.map.ReferenceMap;
import railo.commons.io.FileUtil;
import railo.commons.io.res.Resource;
import railo.commons.io.res.filter.ExtensionResourceFilter;
import railo.commons.lang.ArchiveClassLoader;
import railo.commons.lang.PCLCollection;
import railo.commons.lang.StringUtil;
import railo.runtime.config.Config;
import railo.runtime.config.ConfigImpl;
import railo.runtime.config.ConfigWebImpl;
import railo.runtime.config.ConfigWebUtil;
import railo.runtime.dump.DumpData;
import railo.runtime.dump.DumpProperties;
import railo.runtime.dump.DumpTable;
import railo.runtime.dump.DumpUtil;
import railo.runtime.dump.SimpleDumpData;
import railo.runtime.listener.ApplicationListener;
import railo.runtime.op.Caster;
import railo.runtime.type.util.ArrayUtil;
/**
* Mapping class
*/
public final class MappingImpl implements Mapping {
private static final long serialVersionUID = 6431380676262041196L;
//private static final Object NULL = new Object();
private String virtual;
private String lcVirtual;
private boolean topLevel;
private short inspect;
private boolean physicalFirst;
private ArchiveClassLoader archiveClassLoader;
//private PhysicalClassLoader physicalClassLoader;
private PCLCollection pclCollection;
private Resource archive;
private boolean hasArchive;
private Config config;
private Resource classRootDirectory;
private PageSourcePool pageSourcePool=new PageSourcePool();
private boolean readonly=false;
private boolean hidden=false;
private String strArchive;
private String strPhysical;
private Resource physical;
//private boolean hasPhysical;
private String lcVirtualWithSlash;
//private Resource classRoot;
private Map<String,Object> customTagPath=new ReferenceMap(ReferenceMap.SOFT,ReferenceMap.SOFT);
//private final Map<String,Object> customTagPath=new HashMap<String, Object>();
private int classLoaderMaxElements=1000;
/**
* @return the classLoaderMaxElements
*/
public int getClassLoaderMaxElements() {
return classLoaderMaxElements;
}
private boolean appMapping;
private boolean ignoreVirtual;
private ApplicationListener appListener;
public MappingImpl(Config config, String virtual, String strPhysical,String strArchive, short inspect,
boolean physicalFirst, boolean hidden, boolean readonly,boolean topLevel, boolean appMapping,boolean ignoreVirtual,ApplicationListener appListener) {
this(config, virtual, strPhysical, strArchive, inspect, physicalFirst, hidden, readonly,topLevel,appMapping,ignoreVirtual,appListener,5000);
}
/**
* @param configServer
* @param config
* @param virtual
* @param strPhysical
* @param strArchive
* @param trusted
* @param physicalFirst
* @param hidden
* @param readonly
* @throws IOException
*/
public MappingImpl(Config config, String virtual, String strPhysical,String strArchive, short inspect,
boolean physicalFirst, boolean hidden, boolean readonly,boolean topLevel, boolean appMapping, boolean ignoreVirtual,ApplicationListener appListener, int classLoaderMaxElements) {
this.ignoreVirtual=ignoreVirtual;
this.config=config;
this.hidden=hidden;
this.readonly=readonly;
this.strPhysical=StringUtil.isEmpty(strPhysical)?null:strPhysical;
this.strArchive=StringUtil.isEmpty(strArchive)?null:strArchive;
this.inspect=inspect;
this.topLevel=topLevel;
this.appMapping=appMapping;
this.physicalFirst=physicalFirst;
this.appListener=appListener;
this.classLoaderMaxElements=classLoaderMaxElements;
// virtual
if(virtual.length()==0)virtual="/";
if(!virtual.equals("/") && virtual.endsWith("/"))this.virtual=virtual.substring(0,virtual.length()-1);
else this.virtual=virtual;
this.lcVirtual=this.virtual.toLowerCase();
this.lcVirtualWithSlash=lcVirtual.endsWith("/")?this.lcVirtual:this.lcVirtual+'/';
//if(!(config instanceof ConfigWebImpl)) return;
//ConfigWebImpl cw=(ConfigWebImpl) config;
ServletContext cs = (config instanceof ConfigWebImpl)?((ConfigWebImpl)config).getServletContext():null;
// Physical
physical=ConfigWebUtil.getExistingResource(cs,strPhysical,null,config.getConfigDir(),FileUtil.TYPE_DIR,
config);
// Archive
archive=ConfigWebUtil.getExistingResource(cs,strArchive,null,config.getConfigDir(),FileUtil.TYPE_FILE,
config);
if(archive!=null) {
try {
archiveClassLoader = new ArchiveClassLoader(archive,getClass().getClassLoader());
}
catch (Throwable t) {
archive=null;
}
}
hasArchive=archive!=null;
if(archive==null) this.physicalFirst=true;
else if(physical==null) this.physicalFirst=false;
else this.physicalFirst=physicalFirst;
//if(!hasArchive && !hasPhysical) throw new IOException("missing physical and archive path, one of them must be defined");
}
@Override
public ClassLoader getClassLoaderForArchive() {
return archiveClassLoader;
}
public PCLCollection touchPCLCollection() throws IOException {
if(pclCollection==null){
pclCollection=new PCLCollection(this,getClassRootDirectory(),getConfig().getClassLoader(),classLoaderMaxElements);
}
return pclCollection;
}
public PCLCollection getPCLCollection() {
return pclCollection;
}
/**
* remove all Page from Pool using this classloader
* @param cl
*/
public void clearPages(ClassLoader cl){
pageSourcePool.clearPages(cl);
}
@Override
public Resource getPhysical() {
return physical;
}
@Override
public String getVirtualLowerCase() {
return lcVirtual;
}
@Override
public String getVirtualLowerCaseWithSlash() {
return lcVirtualWithSlash;
}
@Override
public Resource getArchive() {
//initArchive();
return archive;
}
@Override
public boolean hasArchive() {
return hasArchive;
}
@Override
public boolean hasPhysical() {
return physical!=null;
}
@Override
public Resource getClassRootDirectory() {
if(classRootDirectory==null) {
String path=getPhysical()!=null?
getPhysical().getAbsolutePath():
getArchive().getAbsolutePath();
classRootDirectory=config.getDeployDirectory().getRealResource(
StringUtil.toIdentityVariableName(
path)
);
}
return classRootDirectory;
}
/**
* clones a mapping and make it readOnly
* @param config
* @return cloned mapping
* @throws IOException
*/
public MappingImpl cloneReadOnly(ConfigImpl config) {
return new MappingImpl(config,virtual,strPhysical,strArchive,inspect,physicalFirst,hidden,true,topLevel,appMapping,ignoreVirtual,appListener,classLoaderMaxElements);
}
@Override
public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
maxlevel--;
DumpTable htmlBox = new DumpTable("mapping","#ff6600","#ffcc99","#000000");
htmlBox.setTitle("Mapping");
htmlBox.appendRow(1,new SimpleDumpData("virtual"),new SimpleDumpData(virtual));
htmlBox.appendRow(1,new SimpleDumpData("physical"),DumpUtil.toDumpData(strPhysical,pageContext,maxlevel,dp));
htmlBox.appendRow(1,new SimpleDumpData("archive"),DumpUtil.toDumpData(strArchive,pageContext,maxlevel,dp));
htmlBox.appendRow(1,new SimpleDumpData("inspect"),new SimpleDumpData(ConfigWebUtil.inspectTemplate(getInspectTemplateRaw(),"")));
htmlBox.appendRow(1,new SimpleDumpData("physicalFirst"),new SimpleDumpData(Caster.toString(physicalFirst)));
htmlBox.appendRow(1,new SimpleDumpData("readonly"),new SimpleDumpData(Caster.toString(readonly)));
htmlBox.appendRow(1,new SimpleDumpData("hidden"),new SimpleDumpData(Caster.toString(hidden)));
htmlBox.appendRow(1,new SimpleDumpData("appmapping"),new SimpleDumpData(Caster.toBoolean(appMapping)));
htmlBox.appendRow(1,new SimpleDumpData("toplevel"),new SimpleDumpData(Caster.toString(topLevel)));
htmlBox.appendRow(1,new SimpleDumpData("ClassLoaderMaxElements"),new SimpleDumpData(Caster.toString(classLoaderMaxElements)));
return htmlBox;
}
/**
* inspect template setting (Config.INSPECT_*), if not defined with the mapping the config setting is returned
* @return
*/
public short getInspectTemplate() {
if(inspect==ConfigImpl.INSPECT_UNDEFINED) return config.getInspectTemplate();
return inspect;
}
/**
* inspect template setting (Config.INSPECT_*), if not defined with the mapping, Config.INSPECT_UNDEFINED is returned
* @return
*/
public short getInspectTemplateRaw() {
return inspect;
}
@Override
public PageSource getPageSource(String realPath) {
boolean isOutSide = false;
realPath=realPath.replace('\\','/');
if(realPath.indexOf('/')!=0) {
if(realPath.startsWith("../")) {
isOutSide=true;
}
else if(realPath.startsWith("./")) {
realPath=realPath.substring(1);
}
else {
realPath="/"+realPath;
}
}
return getPageSource(realPath,isOutSide);
}
@Override
public PageSource getPageSource(String path, boolean isOut) {
PageSource source=pageSourcePool.getPageSource(path,true);
if(source!=null) return source;
PageSourceImpl newSource = new PageSourceImpl(this,path,isOut);
pageSourcePool.setPage(path,newSource);
return newSource;//new PageSource(this,path);
}
/**
* @return Returns the pageSourcePool.
*/
public PageSourcePool getPageSourcePool() {
return pageSourcePool;
}
@Override
public void check() {
//if(config instanceof ConfigServer) return;
//ConfigWebImpl cw=(ConfigWebImpl) config;
ServletContext cs = (config instanceof ConfigWebImpl)?((ConfigWebImpl)config).getServletContext():null;
// Physical
if(getPhysical()==null && strPhysical!=null && strPhysical.length()>0) {
physical=ConfigWebUtil.getExistingResource(cs,strPhysical,null,config.getConfigDir(),FileUtil.TYPE_DIR,config);
}
// Archive
if(getArchive()==null && strArchive!=null && strArchive.length()>0) {
try {
archive=ConfigWebUtil.getExistingResource(cs,strArchive,null,config.getConfigDir(),FileUtil.TYPE_FILE,
config);
if(archive!=null) {
try {
archiveClassLoader = new ArchiveClassLoader(archive,getClass().getClassLoader());
}
catch (MalformedURLException e) {
archive=null;
}
}
hasArchive=archive!=null;
}
catch (IOException e) {}
}
}
@Override
public Config getConfig() {
return config;
}
@Override
public boolean isHidden() {
return hidden;
}
@Override
public boolean isPhysicalFirst() {
return physicalFirst;
}
@Override
public boolean isReadonly() {
return readonly;
}
@Override
public String getStrArchive() {
return strArchive;
}
@Override
public String getStrPhysical() {
return strPhysical;
}
@Override
@Deprecated
public boolean isTrusted() {
return getInspectTemplate()==ConfigImpl.INSPECT_NEVER;
}
@Override
public String getVirtual() {
return virtual;
}
public boolean isAppMapping() {
return appMapping;
}
public boolean isTopLevel() {
return topLevel;
}
/*public PageSource getCustomTagPath(String name, boolean doCustomTagDeepSearch) {
String lcName=name.toLowerCase().trim();
Object o = customTagPath.get(lcName);
if(o==null){
PageSource ps=searchFor(name, lcName, doCustomTagDeepSearch);
if(ps!=null){
customTagPath.put(lcName,ps);
return ps;
}
customTagPath.put(lcName,NULL);
return null;
}
else if(o==NULL) return null;
return (PageSource) o;
}*/
public PageSource getCustomTagPath(String name, boolean doCustomTagDeepSearch) {
return searchFor(name, name.toLowerCase().trim(), doCustomTagDeepSearch);
}
public boolean ignoreVirtual(){
return ignoreVirtual;
}
private PageSource searchFor(String filename, String lcName, boolean doCustomTagDeepSearch) {
if(!hasPhysical()) return null;
PageSource source=getPageSource(filename);
if(isOK(source)) {
return source;
}
customTagPath.remove(lcName);
if(doCustomTagDeepSearch){
String path = _getRecursive(getPhysical(),null, filename);
if(path!=null ) {
source=getPageSource(path);
if(isOK(source)) {
return source;
}
customTagPath.remove(lcName);
}
}
return null;
}
public static boolean isOK(PageSource ps) {
return (ps.getMapping().isTrusted() && ((PageSourceImpl)ps).isLoad()) || ps.exists();
}
public static PageSource isOK(PageSource[] arr) {
if(ArrayUtil.isEmpty(arr)) return null;
for(int i=0;i<arr.length;i++) {
if(isOK(arr[i])) return arr[i];
}
return null;
}
private static String _getRecursive(Resource res, String path, String filename) {
if(res.isDirectory()) {
Resource[] children = res.listResources(new ExtensionResourceFilter(new String[]{".cfm",".cfc"},true,true));
if(path!=null)path+=res.getName()+"/";
else path="";
String tmp;
for(int i=0;i<children.length;i++){
tmp= _getRecursive(children[i], path, filename);
if(tmp!=null) return tmp;
}
}
else if(res.isFile()) {
if(res.getName().equalsIgnoreCase(filename)) return path+res.getName();
}
return null;
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public String toString() {
return "StrPhysical:"+getStrPhysical()+";"+
"StrArchive:"+getStrArchive()+";"+
"Virtual:"+getVirtual()+";"+
"Archive:"+getArchive()+";"+
"Physical:"+getPhysical()+";"+
"topLevel:"+topLevel+";"+
"inspect:"+ConfigWebUtil.inspectTemplate(getInspectTemplateRaw(),"")+";"+
"physicalFirst:"+physicalFirst+";"+
"readonly:"+readonly+";"+
"hidden:"+hidden+";";
}
public ApplicationListener getApplicationListener() {
if(appListener!=null) return appListener;
return config.getApplicationListener();
}
}