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 }