001 package railo.runtime.rest; 002 003 import java.util.ArrayList; 004 import java.util.HashMap; 005 import java.util.Iterator; 006 import java.util.List; 007 import java.util.Map; 008 009 import railo.commons.io.FileUtil; 010 import railo.commons.io.res.Resource; 011 import railo.commons.io.res.filter.AndResourceFilter; 012 import railo.commons.io.res.filter.ExtensionResourceFilter; 013 import railo.commons.io.res.filter.ResourceFilter; 014 import railo.commons.lang.mimetype.MimeType; 015 import railo.runtime.PageContext; 016 import railo.runtime.PageSource; 017 import railo.runtime.component.ComponentLoader; 018 import railo.runtime.config.Config; 019 import railo.runtime.config.ConfigWebImpl; 020 import railo.runtime.config.ConfigWebUtil; 021 import railo.runtime.config.Constants; 022 import railo.runtime.exp.PageException; 023 import railo.runtime.op.Caster; 024 import railo.runtime.type.Struct; 025 import railo.runtime.type.StructImpl; 026 import railo.runtime.type.cfc.ComponentAccess; 027 import railo.runtime.type.util.ArrayUtil; 028 import railo.runtime.type.util.KeyConstants; 029 030 public class Mapping { 031 032 private static final ResourceFilter FILTER = new AndResourceFilter(new ResourceFilter[]{ 033 new ExtensionResourceFilter(Constants.CFC_EXTENSION), 034 new ResourceFilter() { 035 036 public boolean accept(Resource res) { 037 return !Constants.APP_CFC.equalsIgnoreCase(res.getName()); 038 } 039 } 040 }); 041 042 043 044 private String virtual; 045 private Resource physical; 046 private String strPhysical; 047 private boolean hidden; 048 private boolean readonly; 049 private boolean _default; 050 051 052 private List<Source> baseSources; 053 private Map<Resource,List<Source>> customSources=new HashMap<Resource, List<Source>>(); 054 055 public Mapping(Config config, String virtual, String physical, boolean hidden, boolean readonly, boolean _default) { 056 if(!virtual.startsWith("/"))this.virtual="/"+virtual; 057 if(virtual.endsWith("/"))this.virtual=virtual.substring(0,virtual.length()-1); 058 else this.virtual=virtual; 059 060 this.strPhysical=physical; 061 this.hidden=hidden; 062 this.readonly=readonly; 063 this._default=_default; 064 065 066 if(!(config instanceof ConfigWebImpl)) return; 067 ConfigWebImpl cw=(ConfigWebImpl) config; 068 069 this.physical=ConfigWebUtil.getExistingResource(cw.getServletContext(),physical,null,cw.getConfigDir(),FileUtil.TYPE_DIR,cw); 070 071 } 072 073 074 private List<Source> init(PageContext pc, boolean reset) throws PageException { 075 if(reset)release(); 076 077 Resource[] locations = pc.getApplicationContext().getRestCFCLocations(); 078 079 // base source 080 if(ArrayUtil.isEmpty(locations)) { 081 if(baseSources==null && this.physical!=null && this.physical.isDirectory()) { 082 baseSources=_init(pc,this, this.physical); 083 } 084 return baseSources; 085 } 086 087 // custom sources 088 List<Source> rtn = new ArrayList<Source>(); 089 List<Source> list; 090 for(int i=0;i<locations.length;i++){ 091 list = customSources.get(locations[i]); 092 if(list==null && locations[i].isDirectory()) { 093 list=_init(pc,this, locations[i]); 094 customSources.put(locations[i], list); 095 } 096 copy(list,rtn); 097 } 098 return rtn; 099 } 100 101 private void copy(List<Source> src, List<Source> trg) { 102 if(src==null) return; 103 Iterator<Source> it = src.iterator(); 104 while(it.hasNext()){ 105 trg.add(it.next()); 106 } 107 } 108 109 110 private static ArrayList<Source> _init(PageContext pc, Mapping mapping, Resource dir) throws PageException{ 111 Resource[] children = dir.listResources(FILTER); 112 113 RestSettings settings = pc.getApplicationContext().getRestSettings(); 114 ArrayList<Source> sources = new ArrayList<Source>(); 115 116 PageSource ps; 117 ComponentAccess cfc; 118 Struct meta; 119 String path; 120 for(int i=0;i<children.length;i++){ 121 try{ 122 ps = pc.toPageSource(children[i],null); 123 cfc = ComponentLoader.loadComponent(pc, null, ps, children[i].getName(), true,true); 124 meta = cfc.getMetaData(pc); 125 if(Caster.toBooleanValue(meta.get(KeyConstants._rest,null),false)){ 126 path = Caster.toString(meta.get(KeyConstants._restPath,null),null); 127 sources.add(new Source(mapping, cfc.getPageSource(), path)); 128 } 129 } 130 catch(Throwable t){ 131 if(!settings.getSkipCFCWithError()) throw Caster.toPageException(t); 132 } 133 } 134 return sources; 135 } 136 137 138 public railo.runtime.rest.Mapping duplicate(Config config,Boolean readOnly) { 139 return new Mapping(config, virtual, strPhysical, hidden, readOnly==null?this.readonly:readOnly.booleanValue(),_default); 140 } 141 142 /** 143 * @return the physical 144 */ 145 public Resource getPhysical() { 146 return physical; 147 } 148 149 150 /** 151 * @return the virtual 152 */ 153 public String getVirtual() { 154 return virtual; 155 } 156 public String getVirtualWithSlash() { 157 return virtual+"/"; 158 } 159 160 161 /** 162 * @return the strPhysical 163 */ 164 public String getStrPhysical() { 165 return strPhysical; 166 } 167 168 169 /** 170 * @return the hidden 171 */ 172 public boolean isHidden() { 173 return hidden; 174 } 175 176 177 /** 178 * @return the readonly 179 */ 180 public boolean isReadonly() { 181 return readonly; 182 } 183 184 public boolean isDefault() { 185 return _default; 186 } 187 188 189 public Result getResult(PageContext pc,String path,Struct matrix,int format,boolean hasFormatExtension, List<MimeType> accept, MimeType contentType,Result defaultValue) throws PageException { 190 List<Source> sources = init(pc,false); 191 Iterator<Source> it = sources.iterator(); 192 Source src; 193 String[] arrPath,subPath; 194 int index; 195 while(it.hasNext()) { 196 src = it.next(); 197 Struct variables=new StructImpl(); 198 arrPath = RestUtil.splitPath(path); 199 index=RestUtil.matchPath(variables,src.getPath(),arrPath); 200 if(index!=-1){ 201 subPath=new String[(arrPath.length-1)-index]; 202 System.arraycopy(arrPath, index+1, subPath, 0, subPath.length); 203 return new Result(src,variables,subPath,matrix,format,hasFormatExtension,accept,contentType); 204 } 205 } 206 207 return defaultValue; 208 } 209 210 211 public void setDefault(boolean _default) { 212 this._default=_default; 213 } 214 215 216 public void reset(PageContext pc) throws PageException { 217 init(pc, true); 218 } 219 220 221 public synchronized void release() { 222 if(baseSources!=null) { 223 baseSources.clear(); 224 baseSources = null; 225 } 226 customSources.clear(); 227 } 228 }