001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.rest; 020 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026 027import lucee.commons.io.FileUtil; 028import lucee.commons.io.res.Resource; 029import lucee.commons.io.res.filter.AndResourceFilter; 030import lucee.commons.io.res.filter.ExtensionResourceFilter; 031import lucee.commons.io.res.filter.ResourceFilter; 032import lucee.commons.lang.ExceptionUtil; 033import lucee.commons.lang.mimetype.MimeType; 034import lucee.runtime.Component; 035import lucee.runtime.PageContext; 036import lucee.runtime.PageSource; 037import lucee.runtime.component.ComponentLoader; 038import lucee.runtime.config.Config; 039import lucee.runtime.config.ConfigWebImpl; 040import lucee.runtime.config.ConfigWebUtil; 041import lucee.runtime.config.Constants; 042import lucee.runtime.exp.PageException; 043import lucee.runtime.op.Caster; 044import lucee.runtime.type.Struct; 045import lucee.runtime.type.StructImpl; 046import lucee.runtime.type.util.ArrayUtil; 047import lucee.runtime.type.util.KeyConstants; 048 049public class Mapping { 050 051 private static final ResourceFilter FILTER = new AndResourceFilter(new ResourceFilter[]{ 052 new ExtensionResourceFilter(Constants.CFC_EXTENSION), 053 new ResourceFilter() { 054 055 public boolean accept(Resource res) { 056 return !Constants.APP_CFC.equalsIgnoreCase(res.getName()); 057 } 058 } 059 }); 060 061 062 063 private String virtual; 064 private Resource physical; 065 private String strPhysical; 066 private boolean hidden; 067 private boolean readonly; 068 private boolean _default; 069 070 071 private List<Source> baseSources; 072 private Map<Resource,List<Source>> customSources=new HashMap<Resource, List<Source>>(); 073 074 public Mapping(Config config, String virtual, String physical, boolean hidden, boolean readonly, boolean _default) { 075 if(!virtual.startsWith("/"))this.virtual="/"+virtual; 076 if(virtual.endsWith("/"))this.virtual=virtual.substring(0,virtual.length()-1); 077 else this.virtual=virtual; 078 079 this.strPhysical=physical; 080 this.hidden=hidden; 081 this.readonly=readonly; 082 this._default=_default; 083 084 085 if(!(config instanceof ConfigWebImpl)) return; 086 ConfigWebImpl cw=(ConfigWebImpl) config; 087 088 this.physical=ConfigWebUtil.getExistingResource(cw.getServletContext(),physical,null,cw.getConfigDir(),FileUtil.TYPE_DIR,cw); 089 090 } 091 092 093 private List<Source> init(PageContext pc, boolean reset) throws PageException { 094 if(reset)release(); 095 096 Resource[] locations = pc.getApplicationContext().getRestCFCLocations(); 097 098 // base source 099 if(ArrayUtil.isEmpty(locations)) { 100 if(baseSources==null && this.physical!=null && this.physical.isDirectory()) { 101 baseSources=_init(pc,this, this.physical); 102 } 103 return baseSources; 104 } 105 106 // custom sources 107 List<Source> rtn = new ArrayList<Source>(); 108 List<Source> list; 109 for(int i=0;i<locations.length;i++){ 110 list = customSources.get(locations[i]); 111 if(list==null && locations[i].isDirectory()) { 112 list=_init(pc,this, locations[i]); 113 customSources.put(locations[i], list); 114 } 115 copy(list,rtn); 116 } 117 return rtn; 118 } 119 120 private void copy(List<Source> src, List<Source> trg) { 121 if(src==null) return; 122 Iterator<Source> it = src.iterator(); 123 while(it.hasNext()){ 124 trg.add(it.next()); 125 } 126 } 127 128 129 private static ArrayList<Source> _init(PageContext pc, Mapping mapping, Resource dir) throws PageException{ 130 Resource[] children = dir.listResources(FILTER); 131 132 RestSettings settings = pc.getApplicationContext().getRestSettings(); 133 ArrayList<Source> sources = new ArrayList<Source>(); 134 135 PageSource ps; 136 Component cfc; 137 Struct meta; 138 String path; 139 for(int i=0;i<children.length;i++){ 140 try{ 141 ps = pc.toPageSource(children[i],null); 142 cfc = ComponentLoader.loadComponent(pc, null, ps, children[i].getName(), true,true); 143 meta = cfc.getMetaData(pc); 144 if(Caster.toBooleanValue(meta.get(KeyConstants._rest,null),false)){ 145 path = Caster.toString(meta.get(KeyConstants._restPath,null),null); 146 sources.add(new Source(mapping, cfc.getPageSource(), path)); 147 } 148 } 149 catch(Throwable t){ 150 ExceptionUtil.rethrowIfNecessary(t); 151 if(!settings.getSkipCFCWithError()) throw Caster.toPageException(t); 152 } 153 } 154 return sources; 155 } 156 157 158 public lucee.runtime.rest.Mapping duplicate(Config config,Boolean readOnly) { 159 return new Mapping(config, virtual, strPhysical, hidden, readOnly==null?this.readonly:readOnly.booleanValue(),_default); 160 } 161 162 /** 163 * @return the physical 164 */ 165 public Resource getPhysical() { 166 return physical; 167 } 168 169 170 /** 171 * @return the virtual 172 */ 173 public String getVirtual() { 174 return virtual; 175 } 176 public String getVirtualWithSlash() { 177 return virtual+"/"; 178 } 179 180 181 /** 182 * @return the strPhysical 183 */ 184 public String getStrPhysical() { 185 return strPhysical; 186 } 187 188 189 /** 190 * @return the hidden 191 */ 192 public boolean isHidden() { 193 return hidden; 194 } 195 196 197 /** 198 * @return the readonly 199 */ 200 public boolean isReadonly() { 201 return readonly; 202 } 203 204 public boolean isDefault() { 205 return _default; 206 } 207 208 209 public Result getResult(PageContext pc,String path,Struct matrix,int format,boolean hasFormatExtension, List<MimeType> accept, MimeType contentType,Result defaultValue) throws PageException { 210 List<Source> sources = init(pc,false); 211 Iterator<Source> it = sources.iterator(); 212 Source src; 213 String[] arrPath,subPath; 214 int index; 215 while(it.hasNext()) { 216 src = it.next(); 217 Struct variables=new StructImpl(); 218 arrPath = RestUtil.splitPath(path); 219 index=RestUtil.matchPath(variables,src.getPath(),arrPath); 220 if(index!=-1){ 221 subPath=new String[(arrPath.length-1)-index]; 222 System.arraycopy(arrPath, index+1, subPath, 0, subPath.length); 223 return new Result(src,variables,subPath,matrix,format,hasFormatExtension,accept,contentType); 224 } 225 } 226 227 return defaultValue; 228 } 229 230 231 public void setDefault(boolean _default) { 232 this._default=_default; 233 } 234 235 236 public void reset(PageContext pc) throws PageException { 237 init(pc, true); 238 } 239 240 241 public synchronized void release() { 242 if(baseSources!=null) { 243 baseSources.clear(); 244 baseSources = null; 245 } 246 customSources.clear(); 247 } 248}