001    // Copyright 2007 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.hivemind.impl;
016    
017    import java.io.IOException;
018    import java.lang.reflect.InvocationTargetException;
019    import java.net.URL;
020    import java.util.ArrayList;
021    import java.util.Enumeration;
022    import java.util.List;
023    import java.util.StringTokenizer;
024    import java.util.jar.Attributes;
025    import java.util.jar.Manifest;
026    
027    import org.apache.commons.logging.Log;
028    import org.apache.commons.logging.LogFactory;
029    import org.apache.hivemind.ApplicationRuntimeException;
030    import org.apache.hivemind.ClassResolver;
031    import org.apache.hivemind.util.URLResource;
032    
033    /**
034     * Searches for {@link org.apache.hivemind.impl.RegistryProvider} implementations
035     * that are defined in manifest files. Creates an instance of each 
036     * implementation and returns them in the getProviders method.
037     * The manifest file must contain a global attribute <code>hivemind-providers</code>
038     * which contains a comma separated list of classes that implement 
039     * the {@link org.apache.hivemind.impl.RegistryProvider} interface. 
040     * 
041     * @author Achim Huegen
042     */
043    public class RegistryProviderAutoDetector
044    {
045        private static final Log LOG = LogFactory.getLog(RegistryProviderAutoDetector.class);
046        public static final String MANIFEST = "META-INF/MANIFEST.MF";
047        public static final String HIVEMIND_SECTION_NAME = "hivemind";
048        public static final String PROVIDER_ATTRIBUTE_NAME = "hivemind-providers";
049        
050        private List _providers = new ArrayList();
051        
052        public RegistryProviderAutoDetector(ClassResolver resolver)
053        {
054            processManifestFiles(resolver);
055        }
056        
057        public List getProviders()
058        {
059            return _providers;
060        }
061        
062        /**
063         * Process all manifest files found in the classpath
064         * @param resolver  the ClassResolver to use for the search
065         */
066        private void processManifestFiles(ClassResolver resolver)
067        {
068            if (LOG.isDebugEnabled())
069                LOG.debug("Processing manifest files visible to " + resolver);
070    
071            ClassLoader loader = resolver.getClassLoader();
072            Enumeration e = null;
073    
074            try
075            {
076                e = loader.getResources(MANIFEST);
077            }
078            catch (IOException ex)
079            {
080                throw new ApplicationRuntimeException(ImplMessages.unableToFindProviders(resolver, ex),
081                        ex);
082            }
083    
084            while (e.hasMoreElements())
085            {
086                URL descriptorURL = (URL) e.nextElement();
087    
088                processManifestFile(resolver, new URLResource(descriptorURL));
089            }
090    
091        }
092    
093    
094        /**
095         * Process a single manifest file.
096         * 
097         * @param resolver
098         * @param resource  pointer to the manifest file
099         */
100        private void processManifestFile(ClassResolver resolver, URLResource resource)
101        {
102            URL url = resource.getResourceURL();
103            Manifest manifest;
104            try
105            {
106                manifest = new Manifest(url.openStream());
107            }
108            catch (IOException e)
109            {
110                throw new ApplicationRuntimeException(ImplMessages.unableToReadManifest(url, e),
111                        e);
112            }
113            // Search for an entry that defines a provider class
114            Attributes attributes = manifest.getMainAttributes();
115            if (attributes != null) {
116                String providers = attributes.getValue(PROVIDER_ATTRIBUTE_NAME);
117                if (providers != null) {
118                    if (LOG.isDebugEnabled()) {
119                        LOG.debug("Found providers '" + providers + "' defined in manifest file '" + url.toString() + "'");
120                    }
121                    handleProviderAttribute(resolver, providers);
122                }
123            }
124        }
125    
126        /**
127         * Parse the provider list in an attribute and load all classes.
128         */
129        private void handleProviderAttribute(ClassResolver resolver, String providers)
130        {
131            StringTokenizer tokenizer = new StringTokenizer(providers, ",");
132            while (tokenizer.hasMoreTokens())
133            {   
134                String providerClassName = tokenizer.nextToken();
135                loadProvider(resolver, providerClassName);
136            }
137        }
138        
139        /**
140         * Load a provider class and create an instance.
141         * 
142         * @param resolver
143         * @param providerClassName
144         */
145        private void loadProvider(ClassResolver resolver, String providerClassName)
146        {
147            if (LOG.isDebugEnabled())
148                LOG.debug("Loading provider " + providerClassName);
149            Object provider = null;
150            try
151            {
152                Class providerClass = resolver.findClass(providerClassName);
153                provider = providerClass.newInstance();
154            }
155            catch (Exception e)
156            {
157                Exception cause = e;
158                if (e instanceof InvocationTargetException)
159                {
160                    cause = (InvocationTargetException) e;
161                }
162                throw new ApplicationRuntimeException(ImplMessages.unableToCreateProvider(providerClassName, e),
163                        cause);
164            }
165            // Check type of provider
166            if (!(provider instanceof RegistryProvider)) {
167                throw new ApplicationRuntimeException(ImplMessages.providerWrongType(providerClassName, RegistryProvider.class));
168            }
169            
170            _providers.add(provider);
171        }
172    
173    }