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    }