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.io.IOException;
017 import java.io.InputStream;
018 import java.net.URL;
019 import java.util.Map;
020 import java.util.ServiceConfigurationError;
021
022 import org.apache.commons.classscan.ClassPathElement;
023 import org.apache.commons.classscan.MetaClassLoader;
024 import org.apache.commons.classscan.MetaClassPathElement;
025 import org.apache.commons.classscan.spi.ClassDigesterFactory;
026 import org.apache.commons.classscan.spi.ClassPathElementFactory;
027 import org.apache.commons.classscan.spi.ClassPathFactory;
028 import org.apache.commons.classscan.spi.model.SpiClassDigester;
029 import org.apache.commons.classscan.spi.model.SpiClassPath;
030 import org.apache.commons.classscan.spi.model.SpiClassPathElement;
031 import org.apache.commons.classscan.spi.model.SpiMetaClass;
032 import org.apache.commons.classscan.spi.model.SpiMetaClassPathElement;
033 import org.apache.commons.classscan.spi.model.SpiMetaRegistry;
034 import org.apache.commons.classscan.util.FactoryCache;
035 import org.apache.commons.classscan.util.ReapingHashMap;
036 import org.apache.commons.classscan.util.ServiceVisitor;
037 import org.slf4j.Logger;
038 import org.slf4j.LoggerFactory;
039
040 /**
041 * The factory for MetaClassLoaders. Wherever possible, an existing
042 * MetaClassLoader is returned for a ClassLoader. The implementation should not
043 * prevent the garbage collection of a ClassLoader.
044 */
045 public class DefaultMetaRegistry implements SpiMetaRegistry {
046
047 private static final Logger logger = LoggerFactory.getLogger(DefaultMetaRegistry.class);
048
049 private final Map<ClassLoader, MetaClassLoader> metaClassLoaders;
050 private final FactoryCache<ClassPathFactory> classPathFactories;
051 private final FactoryCache<ClassPathElementFactory> classPathElementFactories;
052 private SpiClassDigester classDigester;
053
054 public DefaultMetaRegistry() {
055 metaClassLoaders = new ReapingHashMap<ClassLoader, MetaClassLoader>();
056 ClassLoader classLoader = getClass().getClassLoader();
057 classPathFactories = new FactoryCache<ClassPathFactory>(ClassPathFactory.class, classLoader);
058 classPathElementFactories = new FactoryCache<ClassPathElementFactory>(ClassPathElementFactory.class, classLoader);
059
060 findClassDigester(classLoader);
061 }
062
063 private void findClassDigester(ClassLoader classLoader) {
064 new ServiceVisitor<ClassDigesterFactory>() {
065 @Override
066 protected boolean visit(ClassDigesterFactory factory) {
067 try {
068 classDigester= factory.createDigester(DefaultMetaRegistry.this);
069 return false;
070 }
071 catch(Throwable error) {
072 logger.warn("Ignoring configuration error", error);
073 return true;
074 }
075 }
076 }.visitProviders(ClassDigesterFactory.class, classLoader);
077
078 if(classDigester==null) {
079 throw new ServiceConfigurationError("Could not load a ClassDigester");
080 }
081 }
082
083 @Override
084 public MetaClassLoader getMetaClassLoader(ClassLoader classLoader) {
085 synchronized(metaClassLoaders) {
086 MetaClassLoader mcl = metaClassLoaders.get(classLoader);
087 if (mcl == null) {
088 mcl = createMetaClassLoader(classLoader);
089 metaClassLoaders.put(classLoader, mcl);
090 }
091 return mcl;
092 }
093 }
094
095 public MetaClassLoader createMetaClassLoader(ClassLoader classLoader) {
096 SpiClassPath cp= getClassPath(classLoader);
097 return cp.createMetaClassLoader(this, classLoader);
098 }
099
100 @Override
101 public SpiClassPath getClassPath(ClassLoader classLoader) {
102
103 for(ClassPathFactory factory : classPathFactories) {
104 SpiClassPath cp= factory.getClassPath(this, classLoader);
105 if(cp!=null) {
106 return cp;
107 }
108 }
109
110 throw new IllegalArgumentException("No ClassPathFactory found for ClassLoader "+classLoader);
111 }
112
113 @Override
114 public SpiClassPathElement createClassPathElement(URL url) throws IOException {
115
116 for(ClassPathElementFactory factory : classPathElementFactories) {
117 SpiClassPathElement cp= factory.getClassPathElement(this, url);
118 if(cp!=null) {
119 return cp;
120 }
121 }
122
123 return null;
124 }
125
126 @Override
127 public SpiMetaClassPathElement createMetaClassPathElement(ClassPathElement pathElement) {
128 return new DefaultMetaClassPathElement(this, pathElement);
129 }
130
131 @Override
132 public SpiMetaClass createMetaClass(MetaClassPathElement location, String className, InputStream byteStream) throws IOException {
133 try {
134 SpiMetaClass smc = classDigester.createMetaClass(location, className, byteStream);
135 if (!className.equals(smc.getName())) {
136 throw new IllegalArgumentException("Expecting classname " + className + " but found " + smc.getName());
137 }
138 return smc;
139 }
140 finally {
141 byteStream.close();
142 }
143 }
144 }