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.functions.component; 020 021import java.io.File; 022import java.io.IOException; 023import java.nio.charset.Charset; 024import java.util.ArrayList; 025import java.util.HashSet; 026import java.util.Iterator; 027import java.util.Set; 028 029import lucee.commons.io.IOUtil; 030import lucee.commons.io.res.Resource; 031import lucee.commons.io.res.filter.ExtensionResourceFilter; 032import lucee.commons.io.res.util.ResourceUtil; 033import lucee.commons.lang.ExceptionUtil; 034import lucee.commons.lang.StringUtil; 035import lucee.runtime.Mapping; 036import lucee.runtime.PageContext; 037import lucee.runtime.PageContextImpl; 038import lucee.runtime.PageSource; 039import lucee.runtime.config.ConfigWebImpl; 040import lucee.runtime.exp.ApplicationException; 041import lucee.runtime.exp.PageException; 042import lucee.runtime.ext.function.Function; 043import lucee.runtime.op.Caster; 044import lucee.runtime.type.Array; 045import lucee.runtime.type.ArrayImpl; 046import lucee.runtime.type.util.ArrayUtil; 047import lucee.runtime.type.util.ListUtil; 048import lucee.transformer.bytecode.util.ASMUtil; 049 050public class ComponentListPackage implements Function { 051 052 private static final long serialVersionUID = 6502632300879457687L; 053 054 private static final ExtensionResourceFilter FILTER_CFC = new ExtensionResourceFilter(".cfc"); 055 private static final ExtensionResourceFilter FILTER_CLASS = new ExtensionResourceFilter(".class"); 056 private static final String[] EMPTY = new String[0]; 057 058 059 public static Array call(PageContext pc , String packageName) throws PageException { 060 Set<String> names; 061 try { 062 names = _call(pc, packageName); 063 } catch (IOException e) { 064 throw Caster.toPageException(e); 065 } 066 067 Array arr=new ArrayImpl(); 068 String name; 069 Iterator<String> it = names.iterator(); 070 while(it.hasNext()){ 071 name=it.next(); 072 if(StringUtil.endsWithIgnoreCase(name, ".cfc")) { 073 name=name.substring(0,name.length()-4); 074 } 075 arr.appendEL(name); 076 } 077 return arr; 078 } 079 080 private static Set<String> _call(PageContext pc , String packageName) throws IOException, ApplicationException { 081 PageContextImpl pci=(PageContextImpl) pc; 082 ConfigWebImpl config = (ConfigWebImpl) pc.getConfig(); 083 Set<String> rtn=null; 084 //var SEP=server.separator.file; 085 086 // get enviroment configuration 087 boolean searchLocal = config.getComponentLocalSearch(); 088 boolean searchRoot=config.getComponentRootSearch(); 089 090 String path=StringUtil.replace(packageName, ".", File.separator, false); 091 092 // search local 093 if(searchLocal) { 094 //Resource dir=pc.getCurrentTemplatePageSource().getResourceTranslated(pc).getParentResource(); 095 //dir=dir.getRealResource(path); 096 PageSource ps= pci.getRelativePageSourceExisting(path); 097 if(ps!=null){ 098 Mapping mapping = ps.getMapping(); 099 String _path=ps.getRealpath(); 100 _path=ListUtil.trim(_path,"\\/"); 101 String[] list = _listMapping(pc,mapping,_path); 102 if(!ArrayUtil.isEmpty(list)) rtn=add(rtn,list); 103 } 104 } 105 106 // check mappings (this includes the webroot) 107 if(searchRoot) { 108 String virtual="/"+StringUtil.replace(packageName, ".", "/", false); 109 Mapping[] mappings = config.getMappings(); 110 Mapping mapping; 111 String _path; 112 String[] list; 113 for(int i=0;i<mappings.length;i++){ 114 mapping=mappings[i]; 115 if(StringUtil.startsWithIgnoreCase(virtual, mapping.getVirtual())) { 116 _path=ListUtil.trim(virtual.substring(mapping.getVirtual().length()),"\\/").trim(); 117 _path=StringUtil.replace(_path, "/", File.separator, false); 118 list = _listMapping(pc,mapping,_path); 119 if(!ArrayUtil.isEmpty(list)) rtn=add(rtn,list); 120 } 121 } 122 } 123 124 // check component mappings 125 Mapping[] mappings = config.getComponentMappings(); 126 Mapping mapping; 127 String[] list; 128 for(int i=0;i<mappings.length;i++){ 129 mapping=mappings[i]; 130 list=_listMapping(pc,mapping,path); 131 if(!ArrayUtil.isEmpty(list)) rtn=add(rtn,list); 132 } 133 134 if(rtn==null)throw new ApplicationException("no package with name ["+packageName+"] found"); 135 return rtn; 136 } 137 138 139 private static Set<String> add(Set<String> set, String[] arr) { 140 if(set==null) set=new HashSet<String>(); 141 for(int i=0;i<arr.length;i++){ 142 set.add(arr[i]); 143 } 144 return set; 145 } 146 147 private static String[] _listMapping(PageContext pc,Mapping mapping, String path) throws IOException{ 148 if(mapping.isPhysicalFirst()) { 149 // check physical 150 String[] list = _listPhysical(path,mapping); 151 if(!ArrayUtil.isEmpty(list)) return list; 152 153 // check archive 154 list=_listArchive(pc,path,mapping); 155 if(!ArrayUtil.isEmpty(list)) return list; 156 } 157 else { 158 // check archive 159 String[] list = _listArchive(pc,path,mapping); 160 if(!ArrayUtil.isEmpty(list)) return list; 161 // check physical 162 list=_listPhysical(path,mapping); 163 if(!ArrayUtil.isEmpty(list)) return list; 164 } 165 return null; 166 } 167 168 private static String[] _listPhysical(String path, Mapping mapping){ 169 Resource physical = mapping.getPhysical(); 170 if(physical!=null) { 171 Resource dir = physical.getRealResource(path); 172 if(dir.isDirectory()) { 173 return dir.list(FILTER_CFC); 174 } 175 } 176 return EMPTY; 177 } 178 179 private static String[] _listArchive(PageContext pc,String path, Mapping mapping) throws IOException { 180 String packageName=StringUtil.replace(path, File.separator, ".", false); 181 Resource archive = mapping.getArchive(); 182 if(archive!=null) { 183 // TODO nor working with pathes with none ascci characters, eith none ascci characters, the java class path is renamed, so make sure you rename the path as well 184 String strDir="zip://"+archive+"!"+File.separator+path; 185 Resource dir = ResourceUtil.toResourceNotExisting(pc, strDir,true,false); 186 187 if(dir.isDirectory()) { 188 java.util.List<String> list=new ArrayList<String>(); 189 // we use the class files here to get the info, the source files are optional and perhaps not present. 190 Resource[] children = dir.listResources(FILTER_CLASS); 191 String className,c,sourceName=null; 192 for(int i=0;i<children.length;i++){ 193 className=children[i].getName(); 194 className=className.substring(0,className.length()-6); 195 className=packageName+"."+className; 196 197 try { 198 Class<?> clazz = mapping.getClassLoaderForArchive().loadClass(className); 199 sourceName=ASMUtil.getSourceInfo(pc.getConfig(),clazz,true).name; 200 } 201 catch (Throwable t) { 202 ExceptionUtil.rethrowIfNecessary(t); 203 } 204 205 if(StringUtil.isEmpty(sourceName)) { 206 c=IOUtil.toString(children[i],(Charset)null); 207 int loc = c.indexOf("<clinit>"); 208 if(loc !=-1) { 209 c=c.substring(0,loc); 210 c = ListUtil.last(c, "/\\",true).trim(); 211 if(StringUtil.endsWithIgnoreCase(c, ".cfc")) 212 list.add(c); 213 } 214 } 215 else list.add(sourceName); 216 217 } 218 if(list.size()>0) return list.toArray(new String[list.size()]); 219 } 220 } 221 return null; 222 } 223}