001 // Copyright 2004, 2005 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.util.Iterator; 018 import java.util.List; 019 import java.util.Locale; 020 021 import org.apache.commons.logging.Log; 022 import org.apache.commons.logging.LogFactory; 023 import org.apache.hivemind.ErrorHandler; 024 import org.apache.hivemind.Registry; 025 import org.apache.hivemind.definition.ConfigurationPointDefinition; 026 import org.apache.hivemind.definition.DefinitionMessages; 027 import org.apache.hivemind.definition.ModuleDefinition; 028 import org.apache.hivemind.definition.Occurances; 029 import org.apache.hivemind.definition.RegistryDefinition; 030 import org.apache.hivemind.definition.RegistryDefinitionPostProcessor; 031 import org.apache.hivemind.definition.impl.RegistryDefinitionImpl; 032 import org.apache.hivemind.events.RegistryInitializationListener; 033 import org.apache.hivemind.internal.RegistryInfrastructure; 034 035 /** 036 * Class used to build a {@link org.apache.hivemind.Registry} from a {@link org.apache.hivemind.definition.RegistryDefinition}. 037 * 038 * A note about threadsafety: The assumption is that a single thread will access the RegistryBuilder 039 * at one time (typically, a startup class within some form of server or application). Code here and 040 * in many of the related classes is divided into construction-time logic and runtime logic. Runtime 041 * logic is synchronized and threadsafe. Construction-time logic is not threadsafe. Once the 042 * registry is fully constructed, it is not allowed to invoke those methods (though, at this time, 043 * no checks occur). 044 * <p> 045 * Runtime methods, such as {@link org.apache.hivemind.impl.ModuleImpl#getService(String, Class)} 046 * are fully threadsafe. 047 * 048 * @author Howard Lewis Ship 049 */ 050 public final class RegistryBuilder 051 { 052 private static final Log LOG = LogFactory.getLog(RegistryBuilder.class); 053 054 static 055 { 056 if (!LOG.isErrorEnabled()) 057 { 058 System.err 059 .println("********************************************************************************"); 060 System.err 061 .println("* L O G G I N G C O N F I G U R A T I O N E R R O R *"); 062 System.err 063 .println("* ---------------------------------------------------------------------------- *"); 064 System.err 065 .println("* Logging is not enabled for org.apache.hivemind.impl.RegistryBuilder. *"); 066 System.err 067 .println("* Errors during HiveMind module descriptor parsing and validation may not be *"); 068 System.err 069 .println("* logged. This may result in difficult-to-trace runtime exceptions, if there *"); 070 System.err 071 .println("* are errors in any of your module descriptors. You should enable error *"); 072 System.err 073 .println("* logging for the org.apache.hivemind and hivemind loggers. *"); 074 System.err 075 .println("********************************************************************************"); 076 } 077 } 078 079 /** 080 * Delegate used for handling errors. 081 */ 082 083 private ErrorHandler _errorHandler; 084 085 private RegistryDefinition _registryDefinition; 086 087 /** 088 * Constructs a new instance that starts with a empty {@link RegistryDefinition} which can be 089 * requested by {@link #getRegistryDefinition()}. 090 */ 091 public RegistryBuilder() 092 { 093 this(new RegistryDefinitionImpl(), new DefaultErrorHandler()); 094 } 095 096 /** 097 * Constructs a new instance that starts with the provided {@link RegistryDefinition}. 098 * The definition can still be altered afterwards. 099 */ 100 public RegistryBuilder(RegistryDefinition registryDefinition) 101 { 102 this(registryDefinition, new DefaultErrorHandler()); 103 } 104 105 public RegistryBuilder(ErrorHandler errorHandler) 106 { 107 this(new RegistryDefinitionImpl(), errorHandler); 108 } 109 110 public RegistryBuilder(RegistryDefinition registryDefinition, ErrorHandler errorHandler) 111 { 112 _registryDefinition = registryDefinition; 113 _errorHandler = errorHandler; 114 } 115 116 /** 117 * @return the contained registry definition 118 */ 119 public RegistryDefinition getRegistryDefinition() 120 { 121 return _registryDefinition; 122 } 123 124 /** 125 * Constructs the registry from its {@link RegistryDefinition}. Default locale is used. 126 * @see #constructRegistry(Locale) 127 */ 128 public Registry constructRegistry() 129 { 130 return constructRegistry(); 131 } 132 133 /** 134 * Constructs the registry from its {@link RegistryDefinition}. 135 * @param locale the locale used for translating resources 136 */ 137 public Registry constructRegistry(Locale locale) 138 { 139 // Add Core HiveMind services like ClassFactory 140 CoreServicesProvider coreServicesProvider = new CoreServicesProvider(); 141 coreServicesProvider.process(_registryDefinition, _errorHandler); 142 143 // Try to resolve all so far unresolved extensions 144 ExtensionResolver extensionResolver = new ExtensionResolver(_registryDefinition, _errorHandler); 145 extensionResolver.resolveExtensions(); 146 147 checkDependencies(_registryDefinition); 148 checkContributionCounts(_registryDefinition); 149 150 // Notify post processors 151 for (Iterator i = _registryDefinition.getPostProcessors().iterator(); i.hasNext();) 152 { 153 RegistryDefinitionPostProcessor processor = (RegistryDefinitionPostProcessor) i.next(); 154 155 processor.postprocess(_registryDefinition, _errorHandler); 156 } 157 158 RegistryInfrastructureConstructor constructor = new RegistryInfrastructureConstructor(_errorHandler, LOG, locale); 159 RegistryInfrastructure infrastructure = constructor 160 .constructRegistryInfrastructure(_registryDefinition); 161 162 // Notify initialization listeners 163 for (Iterator i = _registryDefinition.getRegistryInitializationListeners().iterator(); i.hasNext();) 164 { 165 RegistryInitializationListener listener = (RegistryInitializationListener) i.next(); 166 167 listener.registryInitialized(infrastructure); 168 } 169 170 infrastructure.startup(); 171 172 return new RegistryImpl(infrastructure); 173 } 174 175 /** 176 * Constructs the registry from a specified {@link RegistryDefinition}. 177 * @param definition the registry definition 178 * @param errorHandler errorHandler used for handling recoverable errors 179 * @param locale the locale used for translating resources 180 * @return the registry 181 */ 182 public static Registry constructRegistry(RegistryDefinition definition, ErrorHandler errorHandler, 183 Locale locale) 184 { 185 RegistryBuilder builder = new RegistryBuilder(definition, errorHandler); 186 return builder.constructRegistry(locale); 187 } 188 189 /** 190 * Checks if all dependencies of modules are present. 191 */ 192 private void checkDependencies(RegistryDefinition definition) 193 { 194 for (Iterator iterModules = definition.getModules().iterator(); iterModules.hasNext();) 195 { 196 ModuleDefinition module = (ModuleDefinition) iterModules.next(); 197 198 for (Iterator iterDependencies = module.getDependencies().iterator(); iterDependencies.hasNext();) 199 { 200 String requiredModuleId = (String) iterDependencies.next(); 201 checkModuleDependency(definition, module, requiredModuleId); 202 } 203 } 204 205 } 206 207 private void checkModuleDependency(RegistryDefinition definition, ModuleDefinition sourceModule, String requiredModuleId) 208 { 209 ModuleDefinition requiredModule = (ModuleDefinition) definition.getModule(requiredModuleId); 210 if (requiredModule == null) 211 { 212 // TODO: Include Location in Dependencies 213 _errorHandler.error( 214 LOG, 215 DefinitionMessages.dependencyOnUnknownModule(requiredModuleId), 216 null, 217 null); 218 return; 219 } 220 } 221 222 /** 223 * Checks that each configuration extension point has the right number of contributions. 224 */ 225 public void checkContributionCounts(RegistryDefinition definition) 226 { 227 for (Iterator iterModules = definition.getModules().iterator(); iterModules.hasNext();) 228 { 229 ModuleDefinition module = (ModuleDefinition) iterModules.next(); 230 231 for (Iterator iterConfigurations = module.getConfigurationPoints().iterator(); iterConfigurations.hasNext();) 232 { 233 ConfigurationPointDefinition cpd = (ConfigurationPointDefinition) iterConfigurations.next(); 234 checkContributionCounts(module, cpd); 235 } 236 } 237 } 238 239 private void checkContributionCounts(ModuleDefinition definingModule, ConfigurationPointDefinition configurationPoint) 240 { 241 Occurances expected = configurationPoint.getExpectedContributions(); 242 243 int actual = configurationPoint.getContributions().size(); 244 245 if (expected.inRange(actual)) 246 return; 247 248 _errorHandler.error(LOG, DefinitionMessages.wrongNumberOfContributions( 249 definingModule, configurationPoint, 250 actual, 251 expected), configurationPoint.getLocation(), null); 252 } 253 254 /** 255 * Automatically loads hivemind modules on the classpath which are 256 * provided by {@link RegistryProvider}s which are defined in Manifest-Files. 257 */ 258 public void autoDetectModules() 259 { 260 RegistryProviderAutoDetector detector = new RegistryProviderAutoDetector(new DefaultClassResolver()); 261 List providers = detector.getProviders(); 262 for (Iterator iterProviders = providers.iterator(); iterProviders.hasNext();) 263 { 264 RegistryProvider provider = (RegistryProvider) iterProviders.next(); 265 provider.process(getRegistryDefinition(), _errorHandler); 266 } 267 } 268 269 /** 270 * Constructs a default registry based on just the modules visible to the thread context class 271 * loader (this is sufficient is the majority of cases), and using the default locale. If you 272 * have different error handling needs, or wish to pick up HiveMind modules 273 * for non-standard locations, you must create a RegistryBuilder instance yourself. 274 */ 275 public static Registry constructDefaultRegistry() 276 { 277 RegistryBuilder builder = new RegistryBuilder(); 278 builder.autoDetectModules(); 279 return builder.constructRegistry(Locale.getDefault()); 280 } 281 282 public ErrorHandler getErrorHandler() 283 { 284 return _errorHandler; 285 } 286 287 }