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 }