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    }