001 // Copyright 2006, 2007, 2008, 2009, 2010, 2011 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.tapestry5.ioc.internal; 016 017 import org.apache.tapestry5.func.F; 018 import org.apache.tapestry5.func.Flow; 019 import org.apache.tapestry5.func.Mapper; 020 import org.apache.tapestry5.func.Predicate; 021 import org.apache.tapestry5.ioc.*; 022 import org.apache.tapestry5.ioc.annotations.Local; 023 import org.apache.tapestry5.ioc.def.*; 024 import org.apache.tapestry5.ioc.internal.services.PerthreadManagerImpl; 025 import org.apache.tapestry5.ioc.internal.services.RegistryShutdownHubImpl; 026 import org.apache.tapestry5.ioc.internal.util.*; 027 import org.apache.tapestry5.ioc.services.*; 028 import org.apache.tapestry5.ioc.util.AvailableValues; 029 import org.apache.tapestry5.ioc.util.UnknownValueException; 030 import org.apache.tapestry5.services.UpdateListenerHub; 031 import org.slf4j.Logger; 032 033 import java.lang.annotation.Annotation; 034 import java.lang.reflect.Constructor; 035 import java.lang.reflect.InvocationHandler; 036 import java.lang.reflect.Method; 037 import java.lang.reflect.Proxy; 038 import java.util.*; 039 040 @SuppressWarnings("all") 041 public class RegistryImpl implements Registry, InternalRegistry, ServiceProxyProvider 042 { 043 private static final String SYMBOL_SOURCE_SERVICE_ID = "SymbolSource"; 044 045 private static final String REGISTRY_SHUTDOWN_HUB_SERVICE_ID = "RegistryShutdownHub"; 046 047 static final String PERTHREAD_MANAGER_SERVICE_ID = "PerthreadManager"; 048 049 private static final String SERVICE_ACTIVITY_SCOREBOARD_SERVICE_ID = "ServiceActivityScoreboard"; 050 051 /** 052 * The set of marker annotations for a builtin service. 053 */ 054 private final static Set<Class> BUILTIN = CollectionFactory.newSet(); 055 056 // Split create/assign to appease generics gods 057 static 058 { 059 BUILTIN.add(Builtin.class); 060 } 061 062 /** 063 * Used to obtain the {@link org.apache.tapestry5.ioc.services.ClassFactory} service, which is 064 * crucial when creating 065 * runtime classes for proxies and the like. 066 */ 067 static final String CLASS_FACTORY_SERVICE_ID = "ClassFactory"; 068 069 static final String PLASTIC_PROXY_FACTORY_SERVICE_ID = "PlasticProxyFactory"; 070 071 static final String LOGGER_SOURCE_SERVICE_ID = "LoggerSource"; 072 073 private final OneShotLock lock = new OneShotLock(); 074 075 private final OneShotLock eagerLoadLock = new OneShotLock(); 076 077 private final Map<String, Object> builtinServices = CollectionFactory.newCaseInsensitiveMap(); 078 079 private final Map<String, Class> builtinTypes = CollectionFactory.newCaseInsensitiveMap(); 080 081 private final RegistryShutdownHubImpl registryShutdownHub; 082 083 private final LoggerSource loggerSource; 084 085 /** 086 * Map from service id to the Module that contains the service. 087 */ 088 private final Map<String, Module> serviceIdToModule = CollectionFactory.newCaseInsensitiveMap(); 089 090 private final Map<String, ServiceLifecycle2> lifecycles = CollectionFactory.newCaseInsensitiveMap(); 091 092 private final PerthreadManager perthreadManager; 093 094 private final ClassFactory classFactory; 095 096 private final PlasticProxyFactory proxyFactory; 097 098 private final ServiceActivityTracker tracker; 099 100 private SymbolSource symbolSource; 101 102 private final Map<Module, Set<ServiceDef2>> moduleToServiceDefs = CollectionFactory.newMap(); 103 104 /** 105 * From marker type to a list of marked service instances. 106 */ 107 private final Map<Class, List<ServiceDef2>> markerToServiceDef = CollectionFactory.newMap(); 108 109 private final Set<ServiceDef2> allServiceDefs = CollectionFactory.newSet(); 110 111 private final OperationTracker operationTracker; 112 113 private final TypeCoercerProxy typeCoercerProxy = new TypeCoercerProxyImpl(this); 114 115 private final Map<Class<? extends Annotation>, Annotation> cachedAnnotationProxies = CollectionFactory.newConcurrentMap(); 116 117 /** 118 * Constructs the registry from a set of module definitions and other resources. 119 * 120 * @param moduleDefs defines the modules (and builders, decorators, etc., within) 121 * @param classFactory TODO 122 * @param proxyFactory TODO 123 * @param loggerSource used to obtain Logger instances 124 */ 125 public RegistryImpl(Collection<ModuleDef> moduleDefs, ClassFactory classFactory, PlasticProxyFactory proxyFactory, 126 LoggerSource loggerSource) 127 { 128 assert moduleDefs != null; 129 assert classFactory != null; 130 assert proxyFactory != null; 131 assert loggerSource != null; 132 133 this.loggerSource = loggerSource; 134 135 operationTracker = new PerThreadOperationTracker(loggerSource.getLogger(Registry.class)); 136 137 this.classFactory = classFactory; 138 this.proxyFactory = proxyFactory; 139 140 Logger logger = loggerForBuiltinService(PERTHREAD_MANAGER_SERVICE_ID); 141 142 perthreadManager = new PerthreadManagerImpl(logger); 143 144 final ServiceActivityTrackerImpl scoreboardAndTracker = new ServiceActivityTrackerImpl(perthreadManager); 145 146 tracker = scoreboardAndTracker; 147 148 logger = loggerForBuiltinService(REGISTRY_SHUTDOWN_HUB_SERVICE_ID); 149 150 registryShutdownHub = new RegistryShutdownHubImpl(logger); 151 152 lifecycles.put("singleton", new SingletonServiceLifecycle()); 153 154 registryShutdownHub.addRegistryShutdownListener(new Runnable() 155 { 156 public void run() 157 { 158 scoreboardAndTracker.shutdown(); 159 } 160 }); 161 162 for (ModuleDef def : moduleDefs) 163 { 164 logger = this.loggerSource.getLogger(def.getLoggerName()); 165 166 Module module = new ModuleImpl(this, tracker, def, proxyFactory, logger); 167 168 Set<ServiceDef2> moduleServiceDefs = CollectionFactory.newSet(); 169 170 for (String serviceId : def.getServiceIds()) 171 { 172 ServiceDef2 serviceDef = module.getServiceDef(serviceId); 173 174 moduleServiceDefs.add(serviceDef); 175 allServiceDefs.add(serviceDef); 176 177 Module existing = serviceIdToModule.get(serviceId); 178 179 if (existing != null) 180 throw new RuntimeException(IOCMessages.serviceIdConflict(serviceId, 181 existing.getServiceDef(serviceId), serviceDef)); 182 183 serviceIdToModule.put(serviceId, module); 184 185 // The service is defined but will not have gone further than that. 186 tracker.define(serviceDef, Status.DEFINED); 187 188 for (Class marker : serviceDef.getMarkers()) 189 InternalUtils.addToMapList(markerToServiceDef, marker, serviceDef); 190 } 191 192 moduleToServiceDefs.put(module, moduleServiceDefs); 193 } 194 195 addBuiltin(SERVICE_ACTIVITY_SCOREBOARD_SERVICE_ID, ServiceActivityScoreboard.class, scoreboardAndTracker); 196 addBuiltin(LOGGER_SOURCE_SERVICE_ID, LoggerSource.class, this.loggerSource); 197 addBuiltin(CLASS_FACTORY_SERVICE_ID, ClassFactory.class, this.classFactory); 198 addBuiltin(PERTHREAD_MANAGER_SERVICE_ID, PerthreadManager.class, perthreadManager); 199 addBuiltin(REGISTRY_SHUTDOWN_HUB_SERVICE_ID, RegistryShutdownHub.class, registryShutdownHub); 200 addBuiltin(PLASTIC_PROXY_FACTORY_SERVICE_ID, PlasticProxyFactory.class, proxyFactory); 201 202 validateContributeDefs(moduleDefs); 203 204 scoreboardAndTracker.startup(); 205 206 SerializationSupport.setProvider(this); 207 } 208 209 /** 210 * Validate that each module's ContributeDefs correspond to an actual service. 211 */ 212 private void validateContributeDefs(Collection<ModuleDef> moduleDefs) 213 { 214 for (ModuleDef module : moduleDefs) 215 { 216 Set<ContributionDef> contributionDefs = module.getContributionDefs(); 217 218 for (ContributionDef cd : contributionDefs) 219 { 220 String serviceId = cd.getServiceId(); 221 222 ContributionDef3 cd3 = InternalUtils.toContributionDef3(cd); 223 224 // Ignore any optional contribution methods; there's no way to validate that 225 // they contribute to a known service ... that's the point of @Optional 226 227 if (cd3.isOptional()) 228 { 229 continue; 230 } 231 232 // Otherwise, check that the service being contributed to exists ... 233 234 if (cd3.getServiceId() != null) 235 { 236 if (!serviceIdToModule.containsKey(serviceId)) 237 { 238 throw new IllegalArgumentException( 239 IOCMessages.contributionForNonexistentService(cd)); 240 } 241 } else if (!isContributionForExistentService(module, cd3)) 242 { 243 throw new IllegalArgumentException( 244 IOCMessages.contributionForUnqualifiedService(cd3)); 245 } 246 } 247 } 248 249 } 250 251 /** 252 * Invoked when the contribution method didn't follow the naming convention and so doesn't identify 253 * a service by id; instead there was an @Contribute to identify the service interface. 254 */ 255 @SuppressWarnings("all") 256 private boolean isContributionForExistentService(ModuleDef moduleDef, final ContributionDef2 cd) 257 { 258 final Set<Class> contributionMarkers = new HashSet(cd.getMarkers()); 259 260 boolean localOnly = contributionMarkers.contains(Local.class); 261 262 Flow<ServiceDef2> serviceDefs = localOnly ? getLocalServiceDefs(moduleDef) : F.flow(allServiceDefs); 263 264 contributionMarkers.retainAll(getMarkerAnnotations()); 265 contributionMarkers.remove(Local.class); 266 267 // Match services with the correct interface AND having as markers *all* the marker annotations 268 269 Flow<ServiceDef2> filtered = serviceDefs.filter(F.and(new Predicate<ServiceDef2>() 270 { 271 public boolean accept(ServiceDef2 object) 272 { 273 return object.getServiceInterface().equals(cd.getServiceInterface()); 274 } 275 }, new Predicate<ServiceDef2>() 276 { 277 public boolean accept(ServiceDef2 serviceDef) 278 { 279 return serviceDef.getMarkers().containsAll(contributionMarkers); 280 } 281 } 282 )); 283 284 // That's a lot of logic; the good news is it will short-circuit as soon as it finds a single match, 285 // thanks to the laziness inside Flow. 286 287 return !filtered.isEmpty(); 288 } 289 290 private Flow<ServiceDef2> getLocalServiceDefs(final ModuleDef moduleDef) 291 { 292 return F.flow(moduleDef.getServiceIds()).map(new Mapper<String, ServiceDef2>() 293 { 294 public ServiceDef2 map(String value) 295 { 296 return InternalUtils.toServiceDef2(moduleDef.getServiceDef(value)); 297 } 298 }); 299 } 300 301 /** 302 * It's not unreasonable for an eagerly-loaded service to decide to start a thread, at which 303 * point we raise issues 304 * about improper publishing of the Registry instance from the RegistryImpl constructor. Moving 305 * eager loading of 306 * services out to its own method should ensure thread safety. 307 */ 308 public void performRegistryStartup() 309 { 310 eagerLoadLock.lock(); 311 312 List<EagerLoadServiceProxy> proxies = CollectionFactory.newList(); 313 314 for (Module m : moduleToServiceDefs.keySet()) 315 m.collectEagerLoadServices(proxies); 316 317 // TAPESTRY-2267: Gather up all the proxies before instantiating any of them. 318 319 for (EagerLoadServiceProxy proxy : proxies) 320 proxy.eagerLoadService(); 321 322 getService("RegistryStartup", Runnable.class).run(); 323 324 cleanupThread(); 325 } 326 327 public Logger getServiceLogger(String serviceId) 328 { 329 Module module = serviceIdToModule.get(serviceId); 330 331 assert module != null; 332 333 return loggerSource.getLogger(module.getLoggerName() + "." + serviceId); 334 } 335 336 private Logger loggerForBuiltinService(String serviceId) 337 { 338 return loggerSource.getLogger(TapestryIOCModule.class + "." + serviceId); 339 } 340 341 private <T> void addBuiltin(final String serviceId, final Class<T> serviceInterface, T service) 342 { 343 builtinTypes.put(serviceId, serviceInterface); 344 builtinServices.put(serviceId, service); 345 346 // Make sure each of the builtin services is also available via the Builtin annotation 347 // marker. 348 349 ServiceDef2 serviceDef = new ServiceDef2() 350 { 351 public ObjectCreator createServiceCreator(ServiceBuilderResources resources) 352 { 353 return null; 354 } 355 356 public Set<Class> getMarkers() 357 { 358 return BUILTIN; 359 } 360 361 public String getServiceId() 362 { 363 return serviceId; 364 } 365 366 public Class getServiceInterface() 367 { 368 return serviceInterface; 369 } 370 371 public String getServiceScope() 372 { 373 return ScopeConstants.DEFAULT; 374 } 375 376 public boolean isEagerLoad() 377 { 378 return false; 379 } 380 381 public boolean isPreventDecoration() 382 { 383 return true; 384 } 385 }; 386 387 for (Class marker : serviceDef.getMarkers()) 388 { 389 InternalUtils.addToMapList(markerToServiceDef, marker, serviceDef); 390 allServiceDefs.add(serviceDef); 391 } 392 393 tracker.define(serviceDef, Status.BUILTIN); 394 } 395 396 public synchronized void shutdown() 397 { 398 lock.lock(); 399 400 registryShutdownHub.fireRegistryDidShutdown(); 401 402 SerializationSupport.clearProvider(this); 403 } 404 405 public <T> T getService(String serviceId, Class<T> serviceInterface) 406 { 407 lock.check(); 408 409 T result = checkForBuiltinService(serviceId, serviceInterface); 410 if (result != null) 411 return result; 412 413 // Checking serviceId and serviceInterface is overkill; they have been checked and rechecked 414 // all the way to here. 415 416 Module containingModule = locateModuleForService(serviceId); 417 418 return containingModule.getService(serviceId, serviceInterface); 419 } 420 421 private <T> T checkForBuiltinService(String serviceId, Class<T> serviceInterface) 422 { 423 Object service = builtinServices.get(serviceId); 424 425 if (service == null) 426 return null; 427 428 try 429 { 430 return serviceInterface.cast(service); 431 } catch (ClassCastException ex) 432 { 433 throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, builtinTypes.get(serviceId), 434 serviceInterface)); 435 } 436 } 437 438 public void cleanupThread() 439 { 440 lock.check(); 441 442 perthreadManager.cleanup(); 443 } 444 445 private Module locateModuleForService(String serviceId) 446 { 447 Module module = serviceIdToModule.get(serviceId); 448 449 if (module == null) 450 throw new UnknownValueException(String.format("Service id '%s' is not defined by any module.", serviceId), 451 new AvailableValues("Defined service ids", serviceIdToModule)); 452 453 return module; 454 } 455 456 public <T> Collection<T> getUnorderedConfiguration(ServiceDef3 serviceDef, Class<T> objectType) 457 { 458 lock.check(); 459 460 final Collection<T> result = CollectionFactory.newList(); 461 462 for (Module m : moduleToServiceDefs.keySet()) 463 addToUnorderedConfiguration(result, objectType, serviceDef, m); 464 465 return result; 466 } 467 468 @SuppressWarnings("unchecked") 469 public <T> List<T> getOrderedConfiguration(ServiceDef3 serviceDef, Class<T> objectType) 470 { 471 lock.check(); 472 473 String serviceId = serviceDef.getServiceId(); 474 Logger logger = getServiceLogger(serviceId); 475 476 Orderer<T> orderer = new Orderer<T>(logger); 477 Map<String, OrderedConfigurationOverride<T>> overrides = CollectionFactory.newCaseInsensitiveMap(); 478 479 for (Module m : moduleToServiceDefs.keySet()) 480 addToOrderedConfiguration(orderer, overrides, objectType, serviceDef, m); 481 482 // An ugly hack ... perhaps we should introduce a new builtin service so that this can be 483 // accomplished in the normal way? 484 485 if (serviceId.equals("MasterObjectProvider")) 486 { 487 ObjectProvider contribution = new ObjectProvider() 488 { 489 public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator) 490 { 491 return findServiceByMarkerAndType(objectType, annotationProvider, null); 492 } 493 }; 494 495 orderer.add("ServiceByMarker", (T) contribution); 496 } 497 498 for (OrderedConfigurationOverride<T> override : overrides.values()) 499 override.apply(); 500 501 return orderer.getOrdered(); 502 } 503 504 public <K, V> Map<K, V> getMappedConfiguration(ServiceDef3 serviceDef, Class<K> keyType, Class<V> objectType) 505 { 506 lock.check(); 507 508 // When the key type is String, then a case insensitive map is used. 509 510 Map<K, V> result = newConfigurationMap(keyType); 511 Map<K, ContributionDef> keyToContribution = newConfigurationMap(keyType); 512 Map<K, MappedConfigurationOverride<K, V>> overrides = newConfigurationMap(keyType); 513 514 for (Module m : moduleToServiceDefs.keySet()) 515 addToMappedConfiguration(result, overrides, keyToContribution, keyType, objectType, serviceDef, m); 516 517 for (MappedConfigurationOverride<K, V> override : overrides.values()) 518 { 519 override.apply(); 520 } 521 522 return result; 523 } 524 525 @SuppressWarnings("unchecked") 526 private <K, V> Map<K, V> newConfigurationMap(Class<K> keyType) 527 { 528 if (keyType.equals(String.class)) 529 { 530 Map<String, K> result = CollectionFactory.newCaseInsensitiveMap(); 531 532 return (Map<K, V>) result; 533 } 534 535 return CollectionFactory.newMap(); 536 } 537 538 private <K, V> void addToMappedConfiguration(Map<K, V> map, Map<K, MappedConfigurationOverride<K, V>> overrides, 539 Map<K, ContributionDef> keyToContribution, Class<K> keyClass, Class<V> valueType, ServiceDef3 serviceDef, 540 final Module module) 541 { 542 String serviceId = serviceDef.getServiceId(); 543 Set<ContributionDef2> contributions = module.getContributorDefsForService(serviceDef); 544 545 if (contributions.isEmpty()) 546 return; 547 548 Logger logger = getServiceLogger(serviceId); 549 550 final ServiceResources resources = new ServiceResourcesImpl(this, module, serviceDef, proxyFactory, logger); 551 552 for (final ContributionDef def : contributions) 553 { 554 final MappedConfiguration<K, V> validating = new ValidatingMappedConfigurationWrapper<K, V>(valueType, 555 resources, typeCoercerProxy, map, overrides, serviceId, def, keyClass, keyToContribution); 556 557 String description = "Invoking " + def; 558 559 logger.debug(description); 560 561 operationTracker.run(description, new Runnable() 562 { 563 public void run() 564 { 565 def.contribute(module, resources, validating); 566 } 567 }); 568 } 569 } 570 571 private <T> void addToUnorderedConfiguration(Collection<T> collection, Class<T> valueType, ServiceDef3 serviceDef, 572 final Module module) 573 { 574 String serviceId = serviceDef.getServiceId(); 575 Set<ContributionDef2> contributions = module.getContributorDefsForService(serviceDef); 576 577 if (contributions.isEmpty()) 578 return; 579 580 Logger logger = getServiceLogger(serviceId); 581 582 final ServiceResources resources = new ServiceResourcesImpl(this, module, serviceDef, proxyFactory, logger); 583 584 for (final ContributionDef def : contributions) 585 { 586 final Configuration<T> validating = new ValidatingConfigurationWrapper<T>(valueType, resources, 587 typeCoercerProxy, collection, serviceId); 588 589 String description = "Invoking " + def; 590 591 logger.debug(description); 592 593 operationTracker.run(description, new Runnable() 594 { 595 public void run() 596 { 597 def.contribute(module, resources, validating); 598 } 599 }); 600 } 601 } 602 603 private <T> void addToOrderedConfiguration(Orderer<T> orderer, 604 Map<String, OrderedConfigurationOverride<T>> overrides, Class<T> valueType, ServiceDef3 serviceDef, 605 final Module module) 606 { 607 String serviceId = serviceDef.getServiceId(); 608 Set<ContributionDef2> contributions = module.getContributorDefsForService(serviceDef); 609 610 if (contributions.isEmpty()) 611 return; 612 613 Logger logger = getServiceLogger(serviceId); 614 615 final ServiceResources resources = new ServiceResourcesImpl(this, module, serviceDef, proxyFactory, logger); 616 617 for (final ContributionDef def : contributions) 618 { 619 final OrderedConfiguration<T> validating = new ValidatingOrderedConfigurationWrapper<T>(valueType, 620 resources, typeCoercerProxy, orderer, overrides, def); 621 622 String description = "Invoking " + def; 623 624 logger.debug(description); 625 626 operationTracker.run(description, new Runnable() 627 { 628 public void run() 629 { 630 def.contribute(module, resources, validating); 631 } 632 }); 633 } 634 } 635 636 public <T> T getService(Class<T> serviceInterface) 637 { 638 lock.check(); 639 640 return getServiceByTypeAndMarkers(serviceInterface); 641 } 642 643 public <T> T getService(Class<T> serviceInterface, Class<? extends Annotation>... markerTypes) 644 { 645 lock.check(); 646 647 return getServiceByTypeAndMarkers(serviceInterface, markerTypes); 648 } 649 650 private <T> T getServiceByTypeAlone(Class<T> serviceInterface) 651 { 652 List<String> serviceIds = findServiceIdsForInterface(serviceInterface); 653 654 if (serviceIds == null) 655 serviceIds = Collections.emptyList(); 656 657 switch (serviceIds.size()) 658 { 659 case 0: 660 661 throw new RuntimeException(IOCMessages.noServiceMatchesType(serviceInterface)); 662 663 case 1: 664 665 String serviceId = serviceIds.get(0); 666 667 return getService(serviceId, serviceInterface); 668 669 default: 670 671 Collections.sort(serviceIds); 672 673 throw new RuntimeException(IOCMessages.manyServiceMatches(serviceInterface, serviceIds)); 674 } 675 } 676 677 private <T> T getServiceByTypeAndMarkers(Class<T> serviceInterface, Class<? extends Annotation>... markerTypes) 678 { 679 if (markerTypes.length == 0) 680 { 681 return getServiceByTypeAlone(serviceInterface); 682 } 683 684 AnnotationProvider provider = createAnnotationProvider(markerTypes); 685 686 Set<ServiceDef2> matches = CollectionFactory.newSet(); 687 List<Class> markers = CollectionFactory.newList(); 688 689 findServiceDefsMatchingMarkerAndType(serviceInterface, provider, null, markers, matches); 690 691 return extractServiceFromMatches(serviceInterface, markers, matches); 692 } 693 694 private AnnotationProvider createAnnotationProvider(Class<? extends Annotation>... markerTypes) 695 { 696 final Map<Class<? extends Annotation>, Annotation> map = CollectionFactory.newMap(); 697 698 for (Class<? extends Annotation> markerType : markerTypes) 699 { 700 map.put(markerType, createAnnotationProxy(markerType)); 701 } 702 703 return new AnnotationProvider() 704 { 705 public <T extends Annotation> T getAnnotation(Class<T> annotationClass) 706 { 707 return annotationClass.cast(map.get(annotationClass)); 708 } 709 }; 710 } 711 712 private <A extends Annotation> Annotation createAnnotationProxy(final Class<A> annotationType) 713 { 714 Annotation result = cachedAnnotationProxies.get(annotationType); 715 716 if (result == null) 717 { 718 // We create a JDK proxy because its pretty quick and easy. 719 720 InvocationHandler handler = new InvocationHandler() 721 { 722 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 723 { 724 if (method.getName().equals("annotationType")) 725 { 726 return annotationType; 727 } 728 729 return method.invoke(proxy, args); 730 } 731 }; 732 733 result = (Annotation) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), 734 new Class[]{annotationType}, 735 handler); 736 737 cachedAnnotationProxies.put(annotationType, result); 738 } 739 740 return result; 741 } 742 743 private List<String> findServiceIdsForInterface(Class serviceInterface) 744 { 745 List<String> result = CollectionFactory.newList(); 746 747 for (Module module : moduleToServiceDefs.keySet()) 748 result.addAll(module.findServiceIdsForInterface(serviceInterface)); 749 750 for (Map.Entry<String, Object> entry : builtinServices.entrySet()) 751 { 752 if (serviceInterface.isInstance(entry.getValue())) 753 result.add(entry.getKey()); 754 } 755 756 Collections.sort(result); 757 758 return result; 759 } 760 761 public ServiceLifecycle2 getServiceLifecycle(String scope) 762 { 763 lock.check(); 764 765 ServiceLifecycle result = lifecycles.get(scope); 766 767 if (result == null) 768 { 769 ServiceLifecycleSource source = getService("ServiceLifecycleSource", ServiceLifecycleSource.class); 770 771 result = source.get(scope); 772 } 773 774 if (result == null) 775 throw new RuntimeException(IOCMessages.unknownScope(scope)); 776 777 return InternalUtils.toServiceLifecycle2(result); 778 } 779 780 public List<ServiceDecorator> findDecoratorsForService(ServiceDef3 serviceDef) 781 { 782 lock.check(); 783 784 assert serviceDef != null; 785 786 Logger logger = getServiceLogger(serviceDef.getServiceId()); 787 788 Orderer<ServiceDecorator> orderer = new Orderer<ServiceDecorator>(logger); 789 790 for (Module module : moduleToServiceDefs.keySet()) 791 { 792 Set<DecoratorDef> decoratorDefs = module.findMatchingDecoratorDefs(serviceDef); 793 794 if (decoratorDefs.isEmpty()) 795 continue; 796 797 ServiceResources resources = new ServiceResourcesImpl(this, module, serviceDef, proxyFactory, logger); 798 799 for (DecoratorDef decoratorDef : decoratorDefs) 800 { 801 ServiceDecorator decorator = decoratorDef.createDecorator(module, resources); 802 803 orderer.add(decoratorDef.getDecoratorId(), decorator, decoratorDef.getConstraints()); 804 } 805 } 806 807 return orderer.getOrdered(); 808 } 809 810 public List<ServiceAdvisor> findAdvisorsForService(ServiceDef3 serviceDef) 811 { 812 lock.check(); 813 814 assert serviceDef != null; 815 816 Logger logger = getServiceLogger(serviceDef.getServiceId()); 817 818 Orderer<ServiceAdvisor> orderer = new Orderer<ServiceAdvisor>(logger); 819 820 for (Module module : moduleToServiceDefs.keySet()) 821 { 822 Set<AdvisorDef> advisorDefs = module.findMatchingServiceAdvisors(serviceDef); 823 824 if (advisorDefs.isEmpty()) 825 continue; 826 827 ServiceResources resources = new ServiceResourcesImpl(this, module, serviceDef, proxyFactory, logger); 828 829 for (AdvisorDef advisorDef : advisorDefs) 830 { 831 ServiceAdvisor advisor = advisorDef.createAdvisor(module, resources); 832 833 orderer.add(advisorDef.getAdvisorId(), advisor, advisorDef.getConstraints()); 834 } 835 } 836 837 return orderer.getOrdered(); 838 } 839 840 public ClassFab newClass(Class serviceInterface) 841 { 842 lock.check(); 843 844 return classFactory.newClass(serviceInterface); 845 } 846 847 public <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator, 848 Module localModule) 849 { 850 lock.check(); 851 852 AnnotationProvider effectiveProvider = annotationProvider != null ? annotationProvider 853 : new NullAnnotationProvider(); 854 855 // We do a check here for known marker/type combinations, so that you can use a marker 856 // annotation 857 // to inject into a contribution method that contributes to MasterObjectProvider. 858 // We also force a contribution into MasterObjectProvider to accomplish the same thing. 859 860 T result = findServiceByMarkerAndType(objectType, annotationProvider, localModule); 861 862 if (result != null) 863 return result; 864 865 MasterObjectProvider masterProvider = getService(IOCConstants.MASTER_OBJECT_PROVIDER_SERVICE_ID, 866 MasterObjectProvider.class); 867 868 return masterProvider.provide(objectType, effectiveProvider, locator, true); 869 } 870 871 private Collection<ServiceDef2> filterByType(Class<?> objectType, Collection<ServiceDef2> serviceDefs) 872 { 873 Collection<ServiceDef2> result = CollectionFactory.newSet(); 874 875 for (ServiceDef2 sd : serviceDefs) 876 { 877 if (objectType.isAssignableFrom(sd.getServiceInterface())) 878 { 879 result.add(sd); 880 } 881 } 882 883 return result; 884 } 885 886 @SuppressWarnings("unchecked") 887 private <T> T findServiceByMarkerAndType(Class<T> objectType, AnnotationProvider provider, Module localModule) 888 { 889 if (provider == null) 890 return null; 891 892 Set<ServiceDef2> matches = CollectionFactory.newSet(); 893 List<Class> markers = CollectionFactory.newList(); 894 895 findServiceDefsMatchingMarkerAndType(objectType, provider, localModule, markers, matches); 896 897 898 // If didn't see @Local or any recognized marker annotation, then don't try to filter that 899 // way. Continue on, eventually to the MasterObjectProvider service. 900 901 if (markers.isEmpty()) 902 { 903 return null; 904 } 905 906 return extractServiceFromMatches(objectType, markers, matches); 907 } 908 909 /** 910 * Given markers and matches processed by {@link #findServiceDefsMatchingMarkerAndType(Class, org.apache.tapestry5.ioc.AnnotationProvider, Module, java.util.List, java.util.Set)}, this 911 * finds the singular match, or reports an error for 0 or 2+ matches. 912 */ 913 private <T> T extractServiceFromMatches(Class<T> objectType, List<Class> markers, Set<ServiceDef2> matches) 914 { 915 switch (matches.size()) 916 { 917 918 case 1: 919 920 ServiceDef def = matches.iterator().next(); 921 922 return getService(def.getServiceId(), objectType); 923 924 case 0: 925 926 // It's no accident that the user put the marker annotation at the injection 927 // point, since it matches a known marker annotation, it better be there for 928 // a reason. So if we don't get a match, we have to assume the user expected 929 // one, and that is an error. 930 931 // This doesn't help when the user places an annotation they *think* is a marker 932 // but isn't really a marker (because no service is marked by the annotation). 933 934 throw new RuntimeException(IOCMessages.noServicesMatchMarker(objectType, markers)); 935 936 default: 937 throw new RuntimeException(IOCMessages.manyServicesMatchMarker(objectType, markers, matches)); 938 } 939 } 940 941 private <T> void findServiceDefsMatchingMarkerAndType(Class<T> objectType, AnnotationProvider provider, Module localModule, List<Class> markers, 942 Set<ServiceDef2> matches) 943 { 944 assert provider != null; 945 946 boolean localOnly = localModule != null && provider.getAnnotation(Local.class) != null; 947 948 matches.addAll(filterByType(objectType, localOnly ? moduleToServiceDefs.get(localModule) : allServiceDefs)); 949 950 if (localOnly) 951 { 952 markers.add(Local.class); 953 } 954 955 for (Class marker : markerToServiceDef.keySet()) 956 { 957 if (provider.getAnnotation(marker) == null) 958 { 959 continue; 960 } 961 962 markers.add(marker); 963 964 matches.retainAll(markerToServiceDef.get(marker)); 965 966 if (matches.isEmpty()) 967 { 968 return; 969 } 970 } 971 } 972 973 public <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider) 974 { 975 return getObject(objectType, annotationProvider, this, null); 976 } 977 978 public void addRegistryShutdownListener(RegistryShutdownListener listener) 979 { 980 lock.check(); 981 982 registryShutdownHub.addRegistryShutdownListener(listener); 983 } 984 985 public void addRegistryShutdownListener(Runnable listener) 986 { 987 lock.check(); 988 989 registryShutdownHub.addRegistryShutdownListener(listener); 990 } 991 992 public void addRegistryWillShutdownListener(Runnable listener) 993 { 994 lock.check(); 995 996 registryShutdownHub.addRegistryWillShutdownListener(listener); 997 } 998 999 public String expandSymbols(String input) 1000 { 1001 lock.check(); 1002 1003 // Again, a bit of work to avoid instantiating the SymbolSource until absolutely necessary. 1004 1005 if (!InternalUtils.containsSymbols(input)) 1006 return input; 1007 1008 return getSymbolSource().expandSymbols(input); 1009 } 1010 1011 /** 1012 * Defers obtaining the symbol source until actually needed. 1013 */ 1014 private synchronized SymbolSource getSymbolSource() 1015 { 1016 if (symbolSource == null) 1017 symbolSource = getService(SYMBOL_SOURCE_SERVICE_ID, SymbolSource.class); 1018 1019 return symbolSource; 1020 } 1021 1022 public <T> T autobuild(String description, final Class<T> clazz) 1023 { 1024 return invoke(description, new Invokable<T>() 1025 { 1026 public T invoke() 1027 { 1028 return autobuild(clazz); 1029 } 1030 }); 1031 } 1032 1033 public <T> T autobuild(final Class<T> clazz) 1034 { 1035 assert clazz != null; 1036 final Constructor constructor = InternalUtils.findAutobuildConstructor(clazz); 1037 1038 if (constructor == null) 1039 { 1040 throw new RuntimeException(IOCMessages.noAutobuildConstructor(clazz)); 1041 } 1042 1043 Map<Class, Object> resourcesMap = CollectionFactory.newMap(); 1044 resourcesMap.put(OperationTracker.class, RegistryImpl.this); 1045 1046 InjectionResources resources = new MapInjectionResources(resourcesMap); 1047 1048 ObjectCreator<T> plan = InternalUtils.createConstructorConstructionPlan(this, this, resources, null, "Invoking " + proxyFactory.getConstructorLocation(constructor).toString(), constructor); 1049 1050 return plan.createObject(); 1051 } 1052 1053 public <T> T proxy(Class<T> interfaceClass, Class<? extends T> implementationClass) 1054 { 1055 return proxy(interfaceClass, implementationClass, this); 1056 } 1057 1058 public <T> T proxy(Class<T> interfaceClass, Class<? extends T> implementationClass, ObjectLocator locator) 1059 { 1060 assert interfaceClass != null; 1061 assert implementationClass != null; 1062 1063 if (InternalUtils.SERVICE_CLASS_RELOADING_ENABLED && InternalUtils.isLocalFile(implementationClass)) 1064 return createReloadingProxy(interfaceClass, implementationClass, locator); 1065 1066 return createNonReloadingProxy(interfaceClass, implementationClass, locator); 1067 } 1068 1069 private <T> T createNonReloadingProxy(Class<T> interfaceClass, final Class<? extends T> implementationClass, 1070 final ObjectLocator locator) 1071 { 1072 final ObjectCreator<T> autobuildCreator = new ObjectCreator<T>() 1073 { 1074 public T createObject() 1075 { 1076 return locator.autobuild(implementationClass); 1077 } 1078 }; 1079 1080 ObjectCreator<T> justInTime = new ObjectCreator<T>() 1081 { 1082 private T delegate; 1083 1084 public synchronized T createObject() 1085 { 1086 if (delegate == null) 1087 delegate = autobuildCreator.createObject(); 1088 1089 return delegate; 1090 } 1091 }; 1092 1093 return proxyFactory.createProxy(interfaceClass, justInTime, 1094 String.format("<Autobuild proxy %s(%s)>", implementationClass.getName(), interfaceClass.getName())); 1095 } 1096 1097 private <T> T createReloadingProxy(Class<T> interfaceClass, final Class<? extends T> implementationClass, 1098 ObjectLocator locator) 1099 { 1100 ReloadableObjectCreator creator = new ReloadableObjectCreator(implementationClass.getClassLoader(), 1101 implementationClass.getName(), loggerSource.getLogger(implementationClass), this, locator); 1102 1103 getService(UpdateListenerHub.class).addUpdateListener(creator); 1104 1105 return proxyFactory.createProxy(interfaceClass, (ObjectCreator<T>) creator, 1106 String.format("<Autoreload proxy %s(%s)>", implementationClass.getName(), interfaceClass.getName())); 1107 } 1108 1109 public Object provideServiceProxy(String serviceId) 1110 { 1111 return getService(serviceId, Object.class); 1112 } 1113 1114 public void run(String description, Runnable operation) 1115 { 1116 operationTracker.run(description, operation); 1117 } 1118 1119 public <T> T invoke(String description, Invokable<T> operation) 1120 { 1121 return operationTracker.invoke(description, operation); 1122 } 1123 1124 public Set<Class> getMarkerAnnotations() 1125 { 1126 return markerToServiceDef.keySet(); 1127 } 1128 }