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    
018    import java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.Iterator;
021    import java.util.List;
022    import java.util.Locale;
023    import java.util.Map;
024    
025    import org.apache.hivemind.ErrorHandler;
026    import org.apache.hivemind.HiveMind;
027    import org.apache.hivemind.ShutdownCoordinator;
028    import org.apache.hivemind.definition.ConfigurationPointDefinition;
029    import org.apache.hivemind.definition.Contribution;
030    import org.apache.hivemind.definition.ContributionContext;
031    import org.apache.hivemind.definition.ImplementationConstructionContext;
032    import org.apache.hivemind.definition.ImplementationConstructor;
033    import org.apache.hivemind.definition.ModuleDefinition;
034    import org.apache.hivemind.definition.RegistryDefinition;
035    import org.apache.hivemind.definition.ServicePointDefinition;
036    import org.apache.hivemind.definition.impl.ModuleDefinitionHelper;
037    import org.apache.hivemind.definition.impl.ModuleDefinitionImpl;
038    import org.apache.hivemind.impl.servicemodel.PooledServiceModelFactory;
039    import org.apache.hivemind.impl.servicemodel.PrimitiveServiceModelFactory;
040    import org.apache.hivemind.impl.servicemodel.SingletonServiceModelFactory;
041    import org.apache.hivemind.impl.servicemodel.ThreadedServiceModelFactory;
042    import org.apache.hivemind.internal.AbstractServiceImplementationConstructor;
043    import org.apache.hivemind.internal.ServiceModel;
044    import org.apache.hivemind.service.Autowiring;
045    import org.apache.hivemind.service.AutowiringStrategy;
046    import org.apache.hivemind.service.ClassFactory;
047    import org.apache.hivemind.service.InterfaceSynthesizer;
048    import org.apache.hivemind.service.ThreadEventNotifier;
049    import org.apache.hivemind.service.ThreadLocalStorage;
050    import org.apache.hivemind.service.ThreadLocale;
051    import org.apache.hivemind.service.impl.AutowiringByTypeStrategy;
052    import org.apache.hivemind.service.impl.AutowiringImpl;
053    import org.apache.hivemind.service.impl.AutowiringStrategyContribution;
054    import org.apache.hivemind.service.impl.ClassFactoryImpl;
055    import org.apache.hivemind.service.impl.EagerLoader;
056    import org.apache.hivemind.service.impl.InterfaceSynthesizerImpl;
057    import org.apache.hivemind.service.impl.ThreadEventNotifierImpl;
058    import org.apache.hivemind.service.impl.ThreadLocalStorageImpl;
059    import org.apache.hivemind.service.impl.ThreadLocaleImpl;
060    
061    /**
062     * Loads the core HiveMind services into a registry definition.
063     * 
064     * @author Achim Huegen
065     */
066    public class CoreServicesProvider implements RegistryProvider
067    {
068        private ModuleDefinitionHelper helper;
069    
070        public void process(RegistryDefinition registryDefinition, ErrorHandler errorHandler)
071    
072        {
073            DefaultClassResolver resolver = new DefaultClassResolver();
074    
075            // For the sake of backward compatibility add the core
076            // to an existing module that may have been created by the XmlRegistryProvider
077            // This way the core services and the hivemodule.xml from the xml package share 
078            // the same module name "hivemind"
079    
080            ModuleDefinition md = registryDefinition.getModule("hivemind");
081            if (md == null)
082            {
083                md = new ModuleDefinitionImpl("hivemind", HiveMind.getClassLocation(getClass(), resolver),
084                        resolver, null);
085                registryDefinition.addModule(md);
086            }
087    
088            // The cast to ModuleDefinitionImpl is save, since we exactly now the origin
089            helper = new ModuleDefinitionHelper((ModuleDefinitionImpl) md);
090            
091            addClassFactory(md);
092    
093            addThreadEventNotifier(md);
094    
095            addThreadLocalStorage(md);
096    
097            addThreadLocale(md);
098            
099            addStartup(md);
100    
101            addEagerLoad(md);
102            
103            addShutdownCoordinator(md);
104    
105            addInterfaceSynthesizer(md);
106            
107            addServiceModelConfiguration();
108            
109            addAutowiring(md);
110            
111            addAutowiringStrategiesConfiguration();
112        }
113    
114        /**
115         * Wrapper around Javassist used to dynamically create classes such as service interceptors.
116         */
117        private void addClassFactory(ModuleDefinition md)
118        {
119            ServicePointDefinition spd = helper.addServicePoint("ClassFactory", ClassFactory.class.getName());
120            helper.addSimpleServiceImplementation(spd, ClassFactoryImpl.class.getName(), ServiceModel.PRIMITIVE);
121        }
122    
123        /**
124         * Service used by other services to be alerted when a thread is cleaned up (typically, at the
125         * end of a request or transaction).
126         */
127        private void addThreadEventNotifier(ModuleDefinition md)
128        {
129            ServicePointDefinition spd = helper.addServicePoint(
130                    "ThreadEventNotifier",
131                    ThreadEventNotifier.class.getName());
132            helper.addSimpleServiceImplementation(
133                    spd,
134                    ThreadEventNotifierImpl.class.getName(),
135                    ServiceModel.SINGLETON);
136        }
137    
138        /**
139         * Service which manages a thread-local map of data items. This can be used for temporary
140         * storage of information when local variables can't be used. All stored items are released when
141         * the thread is cleaned up. Note: this service should be considered deprecated; use the
142         * threaded service model instead.
143         */
144        private void addThreadLocalStorage(ModuleDefinition md)
145        {
146            ServicePointDefinition spd = helper.addServicePoint(
147                    "ThreadLocalStorage",
148                    ThreadLocalStorage.class.getName());
149            helper.addSimpleServiceImplementation(spd, ThreadLocalStorageImpl.class.getName(), ServiceModel.THREADED);
150        }
151    
152        /**
153         * Stores the locale for the current thread. The default is determined when the Registry is
154         * first constructed. This locale is used for any messages.
155         */
156        private void addThreadLocale(ModuleDefinition md)
157        {
158            ServicePointDefinition spd = helper.addServicePoint("ThreadLocale", ThreadLocale.class.getName());
159    
160            // Define inline implementation constructor 
161            ImplementationConstructor constructor = new AbstractServiceImplementationConstructor(md.getLocation())
162            {
163    
164                public Object constructCoreServiceImplementation(ImplementationConstructionContext context)
165                {
166                    // Get the Locale from the registry
167                    Locale defaultLocale = context.getDefiningModule().getLocale();
168                    return new ThreadLocaleImpl(defaultLocale);
169                }
170    
171            };
172    
173            helper.addServiceImplementation(spd, constructor, ServiceModel.THREADED);
174        }
175        
176        /**
177         * A source of event notifications for when the Registry is shutdown.
178         */
179        private void addShutdownCoordinator(ModuleDefinition md)
180        {
181            ServicePointDefinition spd = helper.addServicePoint("ShutdownCoordinator", ShutdownCoordinator.class.getName());
182            helper.addSimpleServiceImplementation(spd, ShutdownCoordinatorImpl.class.getName(), ServiceModel.SINGLETON);
183        }
184        
185        /**
186         * Service that performs eager loading of other services. This service is contributed into the hivemind.Startup configuration.
187         */
188        private void addEagerLoad(ModuleDefinition md)
189        {
190            ServicePointDefinition spd = helper.addServicePoint("EagerLoad", Runnable.class.getName());
191    
192            // Define inline implementation constructor, that wires the EagerLoad configuration
193            ImplementationConstructor constructor = new AbstractServiceImplementationConstructor(md.getLocation())
194            {
195                public Object constructCoreServiceImplementation(ImplementationConstructionContext context)
196                {
197                    EagerLoader result = new EagerLoader();
198                    result.setServicePoints((List) context.getConfiguration("EagerLoad"));
199                    return result;
200                }
201            };
202            helper.addServiceImplementation(spd, constructor, ServiceModel.PRIMITIVE);
203            
204            // Configuration to which services may be contributed. The corresponding services are instantiated eagerly, as the Registry is started. 
205            // The order in which services are instantiated is not specified.
206    
207            helper.addConfigurationPoint("EagerLoad", List.class.getName());
208        }
209    
210        /**
211         * A service which is used to bootstrap HiveMind; it obtains the hivemind.Startup configuration and runs each 
212         * Runnable object or service within as the last step of the Registry construction phase.
213         * Note that the execution order is arbitrary and the startup objects are NOT executed in separate threads.
214         */
215        private void addStartup(ModuleDefinition md)
216        {
217            ServicePointDefinition spd = helper.addServicePoint("Startup", Runnable.class.getName());
218    
219            // Define inline implementation constructor, that wires the Startup configuration
220            ImplementationConstructor constructor = new AbstractServiceImplementationConstructor(md.getLocation())
221            {
222                public Object constructCoreServiceImplementation(ImplementationConstructionContext context)
223                {
224                    StartupImpl result = new StartupImpl();
225                    result.setRunnables((List) context.getConfiguration("Startup"));
226                    return result;
227                }
228            };
229            helper.addServiceImplementation(spd, constructor, ServiceModel.PRIMITIVE);
230            
231            // A configuration to which startup objects may be contributed (as objects or services). 
232            // Startup objects must implement the java.lang.Runnable interface. Order of execution is expliclitly NOT defined.
233    
234            ConfigurationPointDefinition cpd = helper.addConfigurationPoint("Startup", List.class.getName());
235            
236            final List services = getDefaultStartupServices();
237            helper.addContributionDefinition(cpd, new Contribution() {
238    
239                public void contribute(ContributionContext context)
240                {
241                    List contribution = new ArrayList(); 
242                    for (Iterator iterServices = services.iterator(); iterServices.hasNext();)
243                    {
244                        String serviceName = (String) iterServices.next();
245                        contribution.add(context.getService(serviceName, Runnable.class));
246                    }
247                    context.mergeContribution(contribution);
248                }});
249        }
250        
251        /**
252         * Defines service models, providing a name and a class for each.
253         */
254        private void addServiceModelConfiguration()
255        {
256    
257            ConfigurationPointDefinition cpd = helper.addConfigurationPoint("ServiceModels", 
258                    Map.class.getName());
259            
260            final List serviceModels = getDefaultServiceModels();
261            helper.addContributionDefinition(cpd, new Contribution() {
262    
263                public void contribute(ContributionContext context)
264                {
265                    Map contribution = new HashMap(); 
266                    for (Iterator iterServiceModels = serviceModels.iterator(); iterServiceModels.hasNext();)
267                    {
268                        ServiceModelContribution contrib = (ServiceModelContribution) iterServiceModels.next();
269                        contribution.put(contrib.getName(), contrib);
270                    }
271                    context.mergeContribution(contribution);
272                }});
273        }
274        
275        /**
276         * Returns default startup services for addition to "Startup" configuration.
277         */
278        private List getDefaultStartupServices()
279        {
280            List result = new ArrayList();
281            result.add("hivemind.EagerLoad");
282            return result;
283        }
284    
285        /**
286         * Returns default service Models as instances of {@link ServiceModelContribution}.
287         */
288        private List getDefaultServiceModels()
289        {
290            List result = new ArrayList();
291            result.add(new ServiceModelContribution(ServiceModel.PRIMITIVE, new PrimitiveServiceModelFactory()));
292            result.add(new ServiceModelContribution(ServiceModel.SINGLETON, new SingletonServiceModelFactory()));
293            result.add(new ServiceModelContribution(ServiceModel.POOLED, new PooledServiceModelFactory()));
294            result.add(new ServiceModelContribution(ServiceModel.THREADED, new ThreadedServiceModelFactory()));
295            return result;
296        }
297    
298        /**
299         * Synthesizes a service interface from an ordinary JavaBean.
300         */
301        private void addInterfaceSynthesizer(ModuleDefinition md)
302        {
303            ServicePointDefinition spd = helper.addServicePoint("InterfaceSynthesizer", InterfaceSynthesizer.class.getName());
304    
305            // Define inline implementation constructor 
306            ImplementationConstructor constructor = new AbstractServiceImplementationConstructor(md.getLocation())
307            {
308                public Object constructCoreServiceImplementation(ImplementationConstructionContext context)
309                {
310                    InterfaceSynthesizerImpl result = new InterfaceSynthesizerImpl();
311                    // Manual wiring of the class factory
312                    result.setClassFactory((ClassFactory) context.getService(ClassFactory.class));
313                    return result;
314                }
315            };
316    
317            helper.addServiceImplementation(spd, constructor, ServiceModel.SINGLETON);
318        }
319        
320        /**
321          * Service that wires properties of object with services defined in the registry.  
322          */
323        private void addAutowiring(ModuleDefinition md)
324        {
325            ServicePointDefinition spd = helper.addServicePoint("Autowiring", Autowiring.class.getName());
326    
327            // Define inline implementation constructor, that wires the AutowiringStrategies configuration
328            ImplementationConstructor constructor = new AbstractServiceImplementationConstructor(md.getLocation())
329            {
330                public Object constructCoreServiceImplementation(ImplementationConstructionContext context)
331                {
332                    List strategies = (List) context.getConfiguration("AutowiringStrategies");
333                    Autowiring result = new AutowiringImpl(context.getRegistry(), strategies, context.getDefiningModule().getErrorHandler());
334                    return result;
335                }
336            };
337            helper.addServiceImplementation(spd, constructor, ServiceModel.PRIMITIVE);
338    
339        }
340        
341        /**
342         * Defines service models, providing a name and a class for each.
343         */
344        private void addAutowiringStrategiesConfiguration()
345        {
346    
347            ConfigurationPointDefinition cpd = helper.addConfigurationPoint("AutowiringStrategies", List.class.getName());
348            
349            final List serviceModels = getDefaultAutowiringStrategies();
350            helper.addContributionDefinition(cpd, new Contribution() {
351    
352                public void contribute(ContributionContext context)
353                {
354                    List contribution = new ArrayList(); 
355                    contribution.addAll(serviceModels);
356                    context.mergeContribution(contribution);
357                }});
358        }
359    
360        /**
361         * Returns default service Models as instances of {@link AutowiringStrategyContribution}.
362         */
363        private List getDefaultAutowiringStrategies()
364        {
365            List result = new ArrayList();
366            result.add(new AutowiringStrategyContribution(new AutowiringByTypeStrategy(), AutowiringStrategy.BY_TYPE, null, null));
367            return result;
368        }
369    
370     }