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