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