001 /* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014 package org.apache.commons.classscan.builtin; 015 016 import java.util.ArrayList; 017 import java.util.Collection; 018 import java.util.Collections; 019 import java.util.Iterator; 020 import java.util.LinkedHashMap; 021 import java.util.List; 022 import java.util.Map; 023 import java.util.MissingResourceException; 024 import java.util.NoSuchElementException; 025 026 import org.apache.commons.classscan.ClassPath; 027 import org.apache.commons.classscan.ClassPathElement; 028 import org.apache.commons.classscan.MetaClassLoader; 029 import org.apache.commons.classscan.MetaClassPathElement; 030 import org.apache.commons.classscan.model.MetaArray; 031 import org.apache.commons.classscan.model.MetaClass; 032 import org.apache.commons.classscan.model.MetaType; 033 import org.apache.commons.classscan.spi.model.SpiClassPathElement; 034 import org.apache.commons.classscan.spi.model.SpiMetaClassLoader; 035 import org.apache.commons.classscan.spi.model.SpiMetaClassPathElement; 036 import org.apache.commons.classscan.spi.model.SpiMetaRegistry; 037 import org.slf4j.Logger; 038 import org.slf4j.LoggerFactory; 039 040 public class UrlMetaClassLoader implements SpiMetaClassLoader { 041 042 private static final Logger logger = LoggerFactory.getLogger(UrlMetaClassLoader.class); 043 044 private final SpiMetaClassLoader parent; 045 private Map<String, SpiMetaClassPathElement> locations; 046 047 public UrlMetaClassLoader(SpiMetaRegistry registry, ClassLoader classLoader) { 048 parent= classLoader!= null ?(SpiMetaClassLoader)registry.getMetaClassLoader(classLoader.getParent()) :null; 049 // use LinkedHashMap to keep iteration over path elements in same order as definition 050 locations = new LinkedHashMap<String, SpiMetaClassPathElement>(); 051 addLocations(registry, classLoader); 052 resolve(); 053 locations = Collections.unmodifiableMap(locations); 054 } 055 056 void addLocations(SpiMetaRegistry registry, ClassLoader classLoader) { 057 ClassPath classPath = registry.getClassPath(classLoader); 058 for(ClassPathElement pathElement : classPath.getClassPathElements()) { 059 addLocation(registry, pathElement); 060 } 061 } 062 063 /** 064 * Add a location 065 * 066 * @param pathElement 067 * The URI of a jar or folder 068 */ 069 private void addLocation(SpiMetaRegistry registry, ClassPathElement pathElement) { 070 String baseLocation= pathElement.getLocation(); 071 if (!locations.containsKey(baseLocation)) { 072 SpiMetaClassPathElement mcl = registry.createMetaClassPathElement(pathElement); 073 if (mcl != null) { 074 locations.put(baseLocation, mcl); 075 } 076 Collection<SpiClassPathElement> additionalUrls= ((SpiClassPathElement)pathElement).getAdditionalLocations(registry); 077 if(additionalUrls!=null) { 078 for(SpiClassPathElement additionalUrl : additionalUrls) { 079 addLocation(registry, additionalUrl); 080 } 081 } 082 } 083 } 084 085 protected void addLocation(SpiMetaClassPathElement cpe) { 086 locations.put(cpe.getName(), cpe); 087 } 088 089 // resolve string names into model entities instead of using resolution upon demand 090 // memory used by multiple string copies weighs more heavily than time spent resolving meta-class 091 // demand resolution would require clients to filter out classes which have un-resolvable dependencies 092 private void resolve() { 093 Iterator<SpiMetaClassPathElement> metaClassPathElements= locations.values().iterator(); 094 while(metaClassPathElements.hasNext()) { 095 SpiMetaClassPathElement metaClassPathElement= metaClassPathElements.next(); 096 if(!metaClassPathElement.resolve(this)) { 097 metaClassPathElements.remove(); 098 } 099 } 100 } 101 102 @Override 103 public MetaClassLoader getParent() { 104 return parent; 105 } 106 107 @Override 108 public Collection<? extends MetaClassPathElement> getClassLocations() { 109 return locations.values(); 110 } 111 112 @Override 113 public MetaClassPathElement getClassLocation(String location) { 114 return locations.get(location); 115 } 116 117 @Override 118 public Iterator<? extends MetaClass> getMetaClasses() { 119 Collection<? extends MetaClassPathElement> classLocations = getClassLocations(); 120 if(classLocations.isEmpty()) { 121 List<? extends MetaClass> empty= Collections.emptyList(); 122 return empty.iterator(); 123 } 124 125 final Iterator<? extends MetaClassPathElement> locations= classLocations.iterator(); 126 127 return new Iterator<MetaClass>() { 128 { 129 nextInnerIterator(); 130 } 131 Iterator<? extends MetaClass> metaClasses; 132 133 private boolean nextInnerIterator() { 134 if(!locations.hasNext()) { 135 return false; 136 } 137 MetaClassPathElement mcpe= locations.next(); 138 metaClasses= mcpe.getMetaClasses().iterator(); 139 return true; 140 } 141 142 @Override 143 public boolean hasNext() { 144 while(!metaClasses.hasNext()) { 145 if(!nextInnerIterator()) { 146 return false; 147 } 148 } 149 return true; 150 } 151 152 @Override 153 public MetaClass next() { 154 while(!metaClasses.hasNext()) { 155 if(!locations.hasNext()) { 156 throw new NoSuchElementException(); 157 } 158 nextInnerIterator(); 159 } 160 return metaClasses.next(); 161 } 162 163 @Override 164 public void remove() { 165 throw new UnsupportedOperationException(); 166 } 167 }; 168 } 169 170 @Override 171 public MetaClass getMetaClass(String className) { 172 for(MetaClassPathElement location : locations.values()) { 173 MetaClass mc = location.getMetaClass(className); 174 if(mc != null) { 175 return mc; 176 } 177 } 178 return null; 179 } 180 181 @Override 182 public MetaClass findMetaClass(String className) { 183 MetaClass mc = parent.findMetaClass(className); 184 if (mc != null) { 185 return mc; 186 } 187 return getMetaClass(className); 188 } 189 190 @Override 191 public MetaClass resolveMetaClass(String className) { 192 MetaClass mc = parent.resolveMetaClass(className); 193 if (mc != null) { 194 return mc; 195 } 196 return resolveInPath(className); 197 } 198 199 MetaClass resolveInPath(String className) { 200 for(SpiMetaClassPathElement location : locations.values()) { 201 MetaClass mc = location.resolveMetaClass(this, className); 202 if(mc != null) { 203 return mc; 204 } 205 } 206 return null; 207 } 208 209 /** 210 * Get metadata representing a type from its byte code signature 211 * 212 * @param fieldDescriptor 213 * The field descriptor as defined in section 4.3.2 of The Java 214 * Virtual Machine Specification 215 * @return The type metadata 216 */ 217 /* 218 * FieldType: BaseType ObjectType ArrayType 219 * 220 * BaseType: // see PrimitiveClass 221 * 222 * ObjectType: L <classname> ; 223 * 224 * ArrayType: [ ComponentType 225 */ 226 @Override 227 public MetaType resolveTypeForDescriptor(String fieldDescriptor) { 228 MetaType mt; 229 char typeCode = fieldDescriptor.charAt(0); 230 switch (typeCode) { 231 case 'L': 232 mt = resolveObjectType(fieldDescriptor); 233 break; 234 case '[': 235 mt = resolveArrayType(fieldDescriptor.substring(1)); 236 break; 237 default: 238 mt = PrimitiveClass.valueOf(fieldDescriptor); 239 break; 240 } 241 if(mt == null) { 242 logger.info("Cannot resolve type "+fieldDescriptor); 243 } 244 return mt; 245 } 246 247 private MetaType resolveObjectType(String fieldDescriptor) { 248 String canonicalName = ClassNameHelper.internalToCanonicalName(fieldDescriptor); 249 return resolveMetaClass(canonicalName); 250 } 251 252 private MetaArray resolveArrayType(String fieldDescriptor) { 253 MetaType arrayType = resolveTypeForDescriptor(fieldDescriptor); 254 return new DefaultArrayType(arrayType); 255 } 256 257 @Override 258 public Collection<? extends MetaClass> findAllImplementors(String interfaceToFind) { 259 MetaClass mc = findMetaClass(interfaceToFind); 260 261 Collection<MetaClass> implementations = new ArrayList<MetaClass>(); 262 MetaClassLoader classLoader = this; 263 do { 264 Iterator<? extends MetaClass> metaClasses = classLoader.getMetaClasses(); 265 while(metaClasses.hasNext()) { 266 MetaClass potentialImplementation = metaClasses.next(); 267 try { 268 if (!potentialImplementation.equals(mc) && mc.isAssignableFrom(potentialImplementation)) { 269 implementations.add(potentialImplementation); 270 } 271 } 272 catch (MissingResourceException mre) { 273 continue; 274 } 275 } 276 classLoader = classLoader.getParent(); 277 } 278 while (classLoader != null); 279 return implementations; 280 } 281 }