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    }