1 // Copyright 2006, 2007, 2008 The Apache Software Foundation
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package org.apache.tapestry5.ioc.internal;
16
17 import org.apache.tapestry5.ioc;
18 import org.apache.tapestry5.ioc.annotations.Local;
19 import org.apache.tapestry5.ioc.def.ContributionDef;
20 import org.apache.tapestry5.ioc.def.DecoratorDef;
21 import org.apache.tapestry5.ioc.def.ModuleDef;
22 import org.apache.tapestry5.ioc.def.ServiceDef;
23 import org.apache.tapestry5.ioc.internal.services.PerthreadManagerImpl;
24 import org.apache.tapestry5.ioc.internal.services.RegistryShutdownHubImpl;
25 import org.apache.tapestry5.ioc.internal.util;
26 import org.apache.tapestry5.ioc.services;
27 import org.slf4j.Logger;
28
29 import java.lang.reflect.Constructor;
30 import java.lang.reflect.InvocationTargetException;
31 import java.util;
32
33 public class RegistryImpl implements Registry, InternalRegistry, ServiceProxyProvider
34 {
35 private static final String SYMBOL_SOURCE_SERVICE_ID = "SymbolSource";
36
37 private static final String REGISTRY_SHUTDOWN_HUB_SERVICE_ID = "RegistryShutdownHub";
38
39 static final String PERTHREAD_MANAGER_SERVICE_ID = "PerthreadManager";
40
41 private static final String SERVICE_ACTIVITY_SCOREBOARD_SERVICE_ID = "ServiceActivityScoreboard";
42
43 /**
44 * The set of marker annotations for a builtin service.
45 */
46 private final static Set<Class> BUILTIN = CollectionFactory.newSet();
47
48 static
49 {
50 BUILTIN.add(Builtin.class);
51 }
52
53 /**
54 * Used to obtain the {@link org.apache.tapestry5.ioc.services.ClassFactory} service, which is crucial when creating
55 * runtime classes for proxies and the like.
56 */
57 static final String CLASS_FACTORY_SERVICE_ID = "ClassFactory";
58
59 static final String LOGGER_SOURCE_SERVICE_ID = "LoggerSource";
60
61 private final OneShotLock lock = new OneShotLock();
62
63 private final OneShotLock eagerLoadLock = new OneShotLock();
64
65 private final Map<String, Object> builtinServices = CollectionFactory.newCaseInsensitiveMap();
66
67 private final Map<String, Class> builtinTypes = CollectionFactory.newCaseInsensitiveMap();
68
69 private final RegistryShutdownHubImpl registryShutdownHub;
70
71 private final LoggerSource loggerSource;
72
73 /**
74 * Map from service id to the Module that contains the service.
75 */
76 private final Map<String, Module> serviceIdToModule = CollectionFactory.newCaseInsensitiveMap();
77
78 private final Map<String, ServiceLifecycle> lifecycles = CollectionFactory.newCaseInsensitiveMap();
79
80 private final PerthreadManager perthreadManager;
81
82 private final ClassFactory classFactory;
83
84 private final ServiceActivityTracker tracker;
85
86 private SymbolSource symbolSource;
87
88 private final Map<Module, Set<ServiceDef>> moduleToServiceDefs = CollectionFactory.newMap();
89
90 /**
91 * From marker type to a list of marked service instances.
92 */
93 private final Map<Class, List<ServiceDef>> markerToServiceDef = CollectionFactory.newMap();
94
95 private final Set<ServiceDef> allServiceDefs = CollectionFactory.newSet();
96
97 private final OperationTracker operationTracker;
98
99 public static final class OrderedConfigurationToOrdererAdaptor<T> implements OrderedConfiguration<T>
100 {
101 private final Orderer<T> orderer;
102
103 public OrderedConfigurationToOrdererAdaptor(Orderer<T> orderer)
104 {
105 this.orderer = orderer;
106 }
107
108 public void add(String id, T object, String... constraints)
109 {
110 orderer.add(id, object, constraints);
111 }
112 }
113
114 /**
115 * Constructs the registry from a set of module definitions and other resources.
116 *
117 * @param moduleDefs defines the modules (and builders, decorators, etc., within)
118 * @param classFactory TODO
119 * @param loggerSource used to obtain Logger instances
120 */
121 public RegistryImpl(Collection<ModuleDef> moduleDefs, ClassFactory classFactory, LoggerSource loggerSource)
122 {
123 this.loggerSource = loggerSource;
124
125 operationTracker = new PerThreadOperationTracker(loggerSource.getLogger(Registry.class));
126
127 final ServiceActivityTrackerImpl scoreboardAndTracker = new ServiceActivityTrackerImpl();
128
129 tracker = scoreboardAndTracker;
130
131 this.classFactory = classFactory;
132
133 Logger logger = loggerForBuiltinService(PERTHREAD_MANAGER_SERVICE_ID);
134
135 perthreadManager = new PerthreadManagerImpl(logger);
136
137
138 logger = loggerForBuiltinService(REGISTRY_SHUTDOWN_HUB_SERVICE_ID);
139
140 registryShutdownHub = new RegistryShutdownHubImpl(logger);
141
142
143 lifecycles.put("singleton", new SingletonServiceLifecycle());
144
145 registryShutdownHub.addRegistryShutdownListener(new RegistryShutdownListener()
146 {
147 public void registryDidShutdown()
148 {
149 scoreboardAndTracker.shutdown();
150 }
151 });
152
153 for (ModuleDef def : moduleDefs)
154 {
155 logger = this.loggerSource.getLogger(def.getLoggerName());
156
157 Module module = new ModuleImpl(this, tracker, def, classFactory, logger);
158
159 Set<ServiceDef> moduleServiceDefs = CollectionFactory.newSet();
160
161 for (String serviceId : def.getServiceIds())
162 {
163 ServiceDef serviceDef = module.getServiceDef(serviceId);
164
165 moduleServiceDefs.add(serviceDef);
166 allServiceDefs.add(serviceDef);
167
168 Module existing = serviceIdToModule.get(serviceId);
169
170 if (existing != null) throw new RuntimeException(IOCMessages.serviceIdConflict(serviceId, existing
171 .getServiceDef(serviceId), serviceDef));
172
173 serviceIdToModule.put(serviceId, module);
174
175 // The service is defined but will not have gone further than that.
176 tracker.define(serviceDef, Status.DEFINED);
177
178 for (Class marker : serviceDef.getMarkers())
179 InternalUtils.addToMapList(markerToServiceDef, marker, serviceDef);
180 }
181
182 moduleToServiceDefs.put(module, moduleServiceDefs);
183 }
184
185 addBuiltin(SERVICE_ACTIVITY_SCOREBOARD_SERVICE_ID, ServiceActivityScoreboard.class, scoreboardAndTracker);
186 addBuiltin(LOGGER_SOURCE_SERVICE_ID, LoggerSource.class, this.loggerSource);
187 addBuiltin(CLASS_FACTORY_SERVICE_ID, ClassFactory.class, this.classFactory);
188 addBuiltin(PERTHREAD_MANAGER_SERVICE_ID, PerthreadManager.class, perthreadManager);
189 addBuiltin(REGISTRY_SHUTDOWN_HUB_SERVICE_ID, RegistryShutdownHub.class, registryShutdownHub);
190
191 scoreboardAndTracker.startup();
192
193 SerializationSupport.setProvider(this);
194 }
195
196 /**
197 * It's not unreasonable for an eagerly-loaded service to decide to start a thread, at which point we raise issues
198 * about improper publishing of the Registry instance from the RegistryImpl constructor. Moving eager loading of
199 * services out to its own method should ensure thread safety.
200 */
201 public void performRegistryStartup()
202 {
203 eagerLoadLock.lock();
204
205 List<EagerLoadServiceProxy> proxies = CollectionFactory.newList();
206
207 for (Module m : moduleToServiceDefs.keySet())
208 m.collectEagerLoadServices(proxies);
209
210 // TAPESTRY-2267: Gather up all the proxies before instantiating any of them.
211
212 for (EagerLoadServiceProxy proxy : proxies)
213 proxy.eagerLoadService();
214
215 getService("RegistryStartup", Runnable.class).run();
216
217 cleanupThread();
218 }
219
220 public Logger getServiceLogger(String serviceId)
221 {
222 Module module = serviceIdToModule.get(serviceId);
223
224 assert module != null;
225
226 return loggerSource.getLogger(module.getLoggerName() + "." + serviceId);
227 }
228
229 private Logger loggerForBuiltinService(String serviceId)
230 {
231 return loggerSource.getLogger(TapestryIOCModule.class + "." + serviceId);
232 }
233
234 private <T> void addBuiltin(final String serviceId, final Class<T> serviceInterface, T service)
235 {
236 builtinTypes.put(serviceId, serviceInterface);
237 builtinServices.put(serviceId, service);
238
239 // Make sure each of the builtin services is also available via the Builtin annotation
240 // marker.
241
242 ServiceDef serviceDef = new ServiceDef()
243 {
244 public ObjectCreator createServiceCreator(ServiceBuilderResources resources)
245 {
246 return null;
247 }
248
249 public Set<Class> getMarkers()
250 {
251 return BUILTIN;
252 }
253
254 public String getServiceId()
255 {
256 return serviceId;
257 }
258
259 public Class getServiceInterface()
260 {
261 return serviceInterface;
262 }
263
264 public String getServiceScope()
265 {
266 return ScopeConstants.DEFAULT;
267 }
268
269 public boolean isEagerLoad()
270 {
271 return false;
272 }
273 };
274
275 for (Class marker : serviceDef.getMarkers())
276 {
277 InternalUtils.addToMapList(markerToServiceDef, marker, serviceDef);
278 allServiceDefs.add(serviceDef);
279 }
280
281 tracker.define(serviceDef, Status.BUILTIN);
282 }
283
284 public synchronized void shutdown()
285 {
286 lock.lock();
287
288 registryShutdownHub.fireRegistryDidShutdown();
289
290 SerializationSupport.clearProvider(this);
291 }
292
293 public <T> T getService(String serviceId, Class<T> serviceInterface)
294 {
295 lock.check();
296
297 T result = checkForBuiltinService(serviceId, serviceInterface);
298 if (result != null) return result;
299
300 // Checking serviceId and serviceInterface is overkill; they have been checked and rechecked
301 // all the way to here.
302
303 Module containingModule = locateModuleForService(serviceId);
304
305 return containingModule.getService(serviceId, serviceInterface);
306 }
307
308 private <T> T checkForBuiltinService(String serviceId, Class<T> serviceInterface)
309 {
310 Object service = builtinServices.get(serviceId);
311
312 if (service == null) return null;
313
314 try
315 {
316 return serviceInterface.cast(service);
317 }
318 catch (ClassCastException ex)
319 {
320 throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, builtinTypes
321 .get(serviceId), serviceInterface));
322 }
323 }
324
325 public void cleanupThread()
326 {
327 lock.check();
328
329 perthreadManager.cleanup();
330 }
331
332 private Module locateModuleForService(String serviceId)
333 {
334 Module module = serviceIdToModule.get(serviceId);
335
336 if (module == null) throw new RuntimeException(IOCMessages.noSuchService(serviceId, serviceIdToModule
337 .keySet()));
338
339 return module;
340 }
341
342 public <T> Collection<T> getUnorderedConfiguration(ServiceDef serviceDef, Class<T> objectType)
343 {
344 lock.check();
345
346 final Collection<T> result = CollectionFactory.newList();
347
348 Configuration<T> configuration = new Configuration<T>()
349 {
350 public void add(T object)
351 {
352 result.add(object);
353 }
354 };
355
356 for (Module m : moduleToServiceDefs.keySet())
357 addToUnorderedConfiguration(configuration, objectType, serviceDef, m);
358
359 return result;
360 }
361
362 @SuppressWarnings("unchecked")
363 public <T> List<T> getOrderedConfiguration(ServiceDef serviceDef, Class<T> objectType)
364 {
365 lock.check();
366
367 String serviceId = serviceDef.getServiceId();
368 Logger logger = getServiceLogger(serviceId);
369
370 final Orderer<T> orderer = new Orderer<T>(logger);
371
372 OrderedConfiguration<T> configuration = new OrderedConfigurationToOrdererAdaptor<T>(orderer);
373
374 for (Module m : moduleToServiceDefs.keySet())
375 addToOrderedConfiguration(configuration, objectType, serviceDef, m);
376
377 // An ugly hack ... perhaps we should introduce a new builtin service so that this can be
378 // accomplished in the normal way?
379
380 if (serviceId.equals("MasterObjectProvider"))
381 {
382 ObjectProvider contribution = new ObjectProvider()
383 {
384 public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
385 {
386 return findServiceByMarkerAndType(objectType, annotationProvider, null);
387 }
388 };
389
390 configuration.add("ServiceByMarker", (T) contribution);
391 }
392
393 return orderer.getOrdered();
394 }
395
396 public <K, V> Map<K, V> getMappedConfiguration(ServiceDef serviceDef, Class<K> keyType, Class<V> objectType)
397 {
398 lock.check();
399
400 // When the key type is String, then a case insensitive map is used for both cases.
401
402 final Map<K, V> result = newConfigurationMap(keyType);
403 Map<K, ContributionDef> keyToContribution = newConfigurationMap(keyType);
404
405 MappedConfiguration<K, V> configuration = new MappedConfiguration<K, V>()
406 {
407 public void add(K key, V value)
408 {
409 result.put(key, value);
410 }
411 };
412
413 for (Module m : moduleToServiceDefs.keySet())
414 addToMappedConfiguration(configuration, keyToContribution, keyType, objectType, serviceDef, m);
415
416 return result;
417 }
418
419 @SuppressWarnings("unchecked")
420 private <K, V> Map<K, V> newConfigurationMap(Class<K> keyType)
421 {
422 if (keyType.equals(String.class))
423 {
424 Map<String, K> result = CollectionFactory.newCaseInsensitiveMap();
425
426 return (Map<K, V>) result;
427 }
428
429 return CollectionFactory.newMap();
430 }
431
432 private <K, V> void addToMappedConfiguration(MappedConfiguration<K, V> configuration,
433 Map<K, ContributionDef> keyToContribution, Class<K> keyClass,
434 Class<V> valueType, ServiceDef serviceDef, final Module module)
435 {
436 String serviceId = serviceDef.getServiceId();
437 Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);
438
439 if (contributions.isEmpty()) return;
440
441 Logger logger = getServiceLogger(serviceId);
442
443 boolean debug = logger.isDebugEnabled();
444
445 final ServiceResources resources = new ServiceResourcesImpl(this, module, serviceDef, classFactory, logger);
446
447 for (final ContributionDef def : contributions)
448 {
449 final MappedConfiguration<K, V> validating =
450 new ValidatingMappedConfigurationWrapper<K, V>(serviceId, def,
451 logger,
452 keyClass,
453 valueType,
454 keyToContribution,
455 configuration);
456
457 String description = IOCMessages.invokingMethod(def);
458
459 if (debug)
460 logger.debug(description);
461
462 operationTracker.run(description, new Runnable()
463 {
464 public void run()
465 {
466 def.contribute(module, resources, validating);
467 }
468 });
469 }
470 }
471
472 private <T> void addToUnorderedConfiguration(Configuration<T> configuration, Class<T> valueType,
473 ServiceDef serviceDef, final Module module)
474 {
475 String serviceId = serviceDef.getServiceId();
476 Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);
477
478 if (contributions.isEmpty()) return;
479
480 Logger logger = getServiceLogger(serviceId);
481
482 boolean debug = logger.isDebugEnabled();
483
484 final ServiceResources resources = new ServiceResourcesImpl(this, module, serviceDef, classFactory, logger);
485
486 for (final ContributionDef def : contributions)
487 {
488 final Configuration<T> validating =
489 new ValidatingConfigurationWrapper<T>(serviceId, logger, valueType, def, configuration);
490
491 String description = IOCMessages.invokingMethod(def);
492
493 if (debug)
494 logger.debug(description);
495
496 operationTracker.run(description, new Runnable()
497 {
498 public void run()
499 {
500 def.contribute(module, resources, validating);
501 }
502 });
503 }
504 }
505
506 private <T> void addToOrderedConfiguration(OrderedConfiguration<T> configuration, Class<T> valueType,
507 ServiceDef serviceDef, final Module module)
508 {
509 String serviceId = serviceDef.getServiceId();
510 Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);
511
512 if (contributions.isEmpty()) return;
513
514 Logger logger = getServiceLogger(serviceId);
515 boolean debug = logger.isDebugEnabled();
516
517 final ServiceResources resources = new ServiceResourcesImpl(this, module, serviceDef, classFactory, logger);
518
519 for (final ContributionDef def : contributions)
520 {
521 final OrderedConfiguration<T> validating =
522 new ValidatingOrderedConfigurationWrapper<T>(serviceId, def, logger, valueType, configuration);
523
524 String description = IOCMessages.invokingMethod(def);
525
526 if (debug)
527 logger.debug(description);
528
529 operationTracker.run(description, new Runnable()
530 {
531 public void run()
532 {
533 def.contribute(module, resources, validating);
534 }
535 });
536 }
537 }
538
539 public <T> T getService(Class<T> serviceInterface)
540 {
541 lock.check();
542
543 List<String> serviceIds = findServiceIdsForInterface(serviceInterface);
544
545 if (serviceIds == null) serviceIds = Collections.emptyList();
546
547 switch (serviceIds.size())
548 {
549 case 0:
550
551 throw new RuntimeException(IOCMessages.noServiceMatchesType(serviceInterface));
552
553 case 1:
554
555 String serviceId = serviceIds.get(0);
556
557 return getService(serviceId, serviceInterface);
558
559 default:
560
561 Collections.sort(serviceIds);
562
563 throw new RuntimeException(IOCMessages.manyServiceMatches(serviceInterface, serviceIds));
564 }
565 }
566
567 private List<String> findServiceIdsForInterface(Class serviceInterface)
568 {
569 List<String> result = CollectionFactory.newList();
570
571 for (Module module : moduleToServiceDefs.keySet())
572 result.addAll(module.findServiceIdsForInterface(serviceInterface));
573
574 for (Map.Entry<String, Object> entry : builtinServices.entrySet())
575 {
576 if (serviceInterface.isInstance(entry.getValue())) result.add(entry.getKey());
577 }
578
579 Collections.sort(result);
580
581 return result;
582 }
583
584 public ServiceLifecycle getServiceLifecycle(String scope)
585 {
586 lock.check();
587
588 ServiceLifecycle result = lifecycles.get(scope);
589
590 if (result == null)
591 {
592 ServiceLifecycleSource source = getService("ServiceLifecycleSource", ServiceLifecycleSource.class);
593 result = source.get(scope);
594 }
595
596 if (result == null) throw new RuntimeException(IOCMessages.unknownScope(scope));
597
598 return result;
599 }
600
601 public List<ServiceDecorator> findDecoratorsForService(ServiceDef serviceDef)
602 {
603 lock.check();
604
605 assert serviceDef != null;
606
607 Logger logger = getServiceLogger(serviceDef.getServiceId());
608
609 Orderer<ServiceDecorator> orderer = new Orderer<ServiceDecorator>(logger);
610
611 for (Module module : moduleToServiceDefs.keySet())
612 {
613 Set<DecoratorDef> decorators = module.findMatchingDecoratorDefs(serviceDef);
614
615 if (decorators.isEmpty()) continue;
616
617 ServiceResources resources = new ServiceResourcesImpl(this, module, serviceDef, classFactory, logger);
618
619 for (DecoratorDef dd : decorators)
620 {
621 ServiceDecorator sd = dd.createDecorator(module, resources);
622
623 orderer.add(dd.getDecoratorId(), sd, dd.getConstraints());
624 }
625 }
626
627 return orderer.getOrdered();
628 }
629
630 public ClassFab newClass(Class serviceInterface)
631 {
632 lock.check();
633
634 return classFactory.newClass(serviceInterface);
635 }
636
637 private <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator,
638 Module localModule)
639 {
640 lock.check();
641
642 AnnotationProvider effectiveProvider = annotationProvider != null ? annotationProvider : new NullAnnotationProvider();
643
644 // We do a check here for known marker/type combinations, so that you can use a marker
645 // annotation
646 // to inject into a contribution method that contributes to MasterObjectProvider.
647 // We also force a contribution into MasterObjectProvider to accomplish the same thing.
648
649 T result = findServiceByMarkerAndType(objectType, annotationProvider, localModule);
650
651 if (result != null) return result;
652
653 MasterObjectProvider masterProvider = getService(IOCConstants.MASTER_OBJECT_PROVIDER_SERVICE_ID,
654 MasterObjectProvider.class);
655
656 return masterProvider.provide(objectType, effectiveProvider, locator, true);
657 }
658
659 private Collection<ServiceDef> filterByType(Class<?> objectType, Collection<ServiceDef> serviceDefs)
660 {
661 Collection<ServiceDef> result = CollectionFactory.newSet();
662
663 for (ServiceDef sd : serviceDefs)
664 {
665 if (objectType.isAssignableFrom(sd.getServiceInterface()))
666 {
667 result.add(sd);
668 }
669 }
670
671 return result;
672 }
673
674 @SuppressWarnings("unchecked")
675 private <T> T findServiceByMarkerAndType(Class<T> objectType, AnnotationProvider provider, Module localModule)
676 {
677 if (provider == null) return null;
678
679 boolean localOnly = localModule != null && provider.getAnnotation(Local.class) != null;
680
681
682 Set<ServiceDef> matches = CollectionFactory.newSet();
683
684 matches.addAll(filterByType(objectType, localOnly
685 ? moduleToServiceDefs.get(localModule)
686 : allServiceDefs
687 ));
688
689 List<Class> markers = CollectionFactory.newList();
690
691 if (localOnly) markers.add(Local.class);
692
693 for (Class marker : markerToServiceDef.keySet())
694 {
695 if (provider.getAnnotation(marker) == null) continue;
696
697 markers.add(marker);
698
699 matches = intersection(matches, markerToServiceDef.get(marker));
700 }
701
702 // If didn't see @Local or any recognized marker annotation, then don't try to filter that way.
703 // Continue on, eventually to the MasterObjectProvider service.
704
705 if (markers.isEmpty()) return null;
706
707 switch (matches.size())
708 {
709
710 case 1:
711
712 ServiceDef def = matches.iterator().next();
713
714 return getService(def.getServiceId(), objectType);
715
716 case 0:
717
718 // It's no accident that the user put the marker annotation at the injection
719 // point, since it matches a known marker annotation, it better be there for
720 // a reason. So if we don't get a match, we have to assume the user expected
721 // one, and that is an error.
722
723 // This doesn't help when the user places an annotation they *think* is a marker
724 // but isn't really a marker (because no service is marked by the annotation).
725
726 throw new RuntimeException(IOCMessages.noServicesMatchMarker(objectType, markers));
727
728 default:
729 throw new RuntimeException(IOCMessages.manyServicesMatchMarker(objectType, markers, matches));
730 }
731 }
732
733 /**
734 * Filters the set into a new set, containing only elements shared between the set and the filter collection.
735 *
736 * @param set to be filtered
737 * @param filter values to keep from the set
738 * @return a new set containing only the shared values
739 */
740 private static <T> Set<T> intersection(Set<T> set, Collection<T> filter)
741 {
742 if (set.isEmpty()) return Collections.emptySet();
743
744 Set<T> result = CollectionFactory.newSet();
745
746 for (T elem : filter)
747 {
748 if (set.contains(elem)) result.add(elem);
749 }
750
751 return result;
752 }
753
754
755 public <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider)
756 {
757 return getObject(objectType, annotationProvider, this, null);
758 }
759
760
761 public <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider, Module localModule)
762 {
763 return getObject(objectType, annotationProvider, this, localModule);
764 }
765
766 public void addRegistryShutdownListener(RegistryShutdownListener listener)
767 {
768 lock.check();
769
770 registryShutdownHub.addRegistryShutdownListener(listener);
771 }
772
773 public String expandSymbols(String input)
774 {
775 lock.check();
776
777 // Again, a bit of work to avoid instantiating the SymbolSource until absolutely necessary.
778
779 if (!InternalUtils.containsSymbols(input)) return input;
780
781 return getSymbolSource().expandSymbols(input);
782 }
783
784 /**
785 * Defers obtaining the symbol source until actually needed.
786 */
787 private synchronized SymbolSource getSymbolSource()
788 {
789 if (symbolSource == null) symbolSource = getService(SYMBOL_SOURCE_SERVICE_ID, SymbolSource.class);
790
791 return symbolSource;
792 }
793
794 public <T> T autobuild(final Class<T> clazz)
795 {
796 Defense.notNull(clazz, "clazz");
797
798 final Constructor constructor = InternalUtils.findAutobuildConstructor(clazz);
799
800 if (constructor == null) throw new RuntimeException(IOCMessages.noAutobuildConstructor(clazz));
801
802 final ObjectLocator locator = this;
803 final OperationTracker tracker = this;
804
805 final Invokable<T> operation = new Invokable<T>()
806 {
807 public T invoke()
808 {
809 Throwable failure;
810 // An empty map, because when performing autobuilding outside the context of building a
811 // service, we don't have defaults for Log, service id, etc.
812
813 Map<Class, Object> empty = Collections.emptyMap();
814
815 try
816 {
817 InternalUtils.validateConstructorForAutobuild(constructor);
818
819 Object[] parameters = InternalUtils.calculateParametersForConstructor(constructor,
820 locator,
821 empty,
822 tracker);
823
824 Object result = constructor.newInstance(parameters);
825
826 InternalUtils.injectIntoFields(result, locator, tracker);
827
828 return clazz.cast(result);
829 }
830 catch (InvocationTargetException ite)
831 {
832 failure = ite.getTargetException();
833 }
834 catch (Exception ex)
835 {
836 failure = ex;
837 }
838
839 String description = classFactory.getConstructorLocation(constructor).toString();
840
841 throw new RuntimeException(IOCMessages.autobuildConstructorError(description, failure),
842 failure);
843 }
844 };
845
846 return invoke("Autobuilding instance of class " + clazz.getName(),
847 operation);
848 }
849
850 public <T> T proxy(Class<T> interfaceClass, final Class<? extends T> implementationClass)
851 {
852 Defense.notNull(interfaceClass, "interfaceClass");
853 Defense.notNull(implementationClass, "implementationClass");
854
855 // TODO: Check really an interface
856 // TODO: Check impl class extends interfaceClass and is concrete
857
858 final ObjectCreator autobuildCreator = new ObjectCreator()
859 {
860 public Object createObject()
861 {
862 return autobuild(implementationClass);
863 }
864 };
865
866 ObjectCreator justInTime = new ObjectCreator()
867 {
868 private Object delegate;
869
870 public synchronized Object createObject()
871 {
872 if (delegate == null) delegate = autobuildCreator.createObject();
873
874 return delegate;
875 }
876 };
877
878 ClassFab cf = classFactory.newClass(interfaceClass);
879
880 String description = String.format("<Autobuild proxy %s(%s)>", implementationClass
881 .getName(), interfaceClass.getName());
882
883 return ClassFabUtils.createObjectCreatorProxy(cf, interfaceClass, justInTime, description);
884 }
885
886 public Object provideServiceProxy(String serviceId)
887 {
888 return getService(serviceId, Object.class);
889 }
890
891 public void run(String description, Runnable operation)
892 {
893 operationTracker.run(description, operation);
894 }
895
896 public <T> T invoke(String description, Invokable<T> operation)
897 {
898 return operationTracker.invoke(description, operation);
899 }
900 }