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 }