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 }