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 }