Save This Page
Home » tapestry-src-5.0.19 » org.apache.tapestry5.ioc.internal » [javadoc | source]
    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   }

Save This Page
Home » tapestry-src-5.0.19 » org.apache.tapestry5.ioc.internal » [javadoc | source]