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 }