Package org.apache.tapestry.ioc.internal

Source Code of org.apache.tapestry.ioc.internal.RegistryImpl

// Copyright 2006 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.tapestry.ioc.internal;

import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.tapestry.ioc.Configuration;
import org.apache.tapestry.ioc.IOCConstants;
import org.apache.tapestry.ioc.IOCUtilities;
import org.apache.tapestry.ioc.LogSource;
import org.apache.tapestry.ioc.MappedConfiguration;
import org.apache.tapestry.ioc.ObjectProvider;
import org.apache.tapestry.ioc.OrderedConfiguration;
import org.apache.tapestry.ioc.Registry;
import org.apache.tapestry.ioc.RegistryBuilder;
import org.apache.tapestry.ioc.ServiceDecorator;
import org.apache.tapestry.ioc.ServiceLifecycle;
import org.apache.tapestry.ioc.ServiceLocator;
import org.apache.tapestry.ioc.ServiceResources;
import org.apache.tapestry.ioc.def.ContributionDef;
import org.apache.tapestry.ioc.def.DecoratorDef;
import org.apache.tapestry.ioc.def.ModuleDef;
import org.apache.tapestry.ioc.def.ServiceDef;
import org.apache.tapestry.ioc.internal.services.ClassFactoryImpl;
import org.apache.tapestry.ioc.internal.services.RegistryShutdownHubImpl;
import org.apache.tapestry.ioc.internal.services.ThreadCleanupHubImpl;
import org.apache.tapestry.ioc.internal.util.CollectionFactory;
import org.apache.tapestry.ioc.internal.util.InternalUtils;
import org.apache.tapestry.ioc.internal.util.OneShotLock;
import org.apache.tapestry.ioc.internal.util.Orderer;
import org.apache.tapestry.ioc.services.ClassFab;
import org.apache.tapestry.ioc.services.ClassFactory;
import org.apache.tapestry.ioc.services.RegistryShutdownHub;
import org.apache.tapestry.ioc.services.RegistryShutdownListener;
import org.apache.tapestry.ioc.services.ServiceLifecycleSource;
import org.apache.tapestry.ioc.services.SymbolSource;
import org.apache.tapestry.ioc.services.ThreadCleanupHub;

public class RegistryImpl implements Registry, InternalRegistry
{
    private static final String SYMBOL_SOURCE_SERVICE_ID = "tapestry.ioc.SymbolSource";

    private static final String REGISTRY_SHUTDOWN_HUB_SERVICE_ID = "tapestry.ioc.RegistryShutdownHub";

    static final String THREAD_CLEANUP_HUB_SERVICE_ID = "tapestry.ioc.ThreadCleanupHub";

    /**
     * Used to obtain the {@link org.apache.tapestry.ioc.services.ClassFactory} service, which is
     * crucial when creating runtime classes for proxies and the like.
     */
    static final String CLASS_FACTORY_SERVICE_ID = "tapestry.ioc.ClassFactory";

    static final String LOG_SOURCE_SERVICE_ID = "tapestry.ioc.LogSource";

    private final OneShotLock _lock = new OneShotLock();

    private final Map<String, Object> _builtinServices = newCaseInsensitiveMap();

    private final Map<String, Class> _builtinTypes = newCaseInsensitiveMap();

    private final RegistryShutdownHubImpl _registryShutdownHub;

    private final LogSource _logSource;

    /** Keyed on module id. */
    private final Map<String, Module> _modules = newCaseInsensitiveMap();

    private final Map<String, ServiceLifecycle> _lifecycles = newCaseInsensitiveMap();

    /**
     * Service implementation overrides, keyed on service id. Service implementations are most
     * useful when perfroming integration tests on services. As one service can bring in another, we
     * have to stop at a certain "bounary" services by provide stub/ mock objects as their
     * implementations.
     */
    private Map<String, Object> _serviceOverrides;

    private final ThreadCleanupHubImpl _cleanupHub;

    private final ClassFactory _classFactory;

    private SymbolSource _symbolSource;

    public static final class OrderedConfigurationToOrdererAdaptor<T> implements
            OrderedConfiguration<T>
    {
        private final Orderer<T> _orderer;

        public OrderedConfigurationToOrdererAdaptor(Orderer<T> orderer)
        {
            _orderer = orderer;
        }

        public void add(String id, T object, String... constraints)
        {
            _orderer.add(id, object, constraints);
        }
    }

    /**
     * Constructs the registry from a set of module definitions and other resources.
     *
     * @param moduleDefs
     *            defines the modules (and builders, decorators, etc., within)
     * @param contextClassLoader
     *            the class loader used to load classes
     * @param logSource
     *            used to obtain Log instances
     * @param serviceOverrides
     *            overrides for service implementation (used in testing, see
     *            {@link RegistryBuilder#addServiceOverride(String, Object)})
     */
    public RegistryImpl(Collection<ModuleDef> moduleDefs, ClassLoader contextClassLoader,
            LogSource logSource, Map<String, Object> serviceOverrides)
    {
        _logSource = logSource;

        _serviceOverrides = serviceOverrides;

        for (ModuleDef def : moduleDefs)
        {
            Log log = _logSource.getLog(def.getModuleId());

            Module module = new ModuleImpl(this, def, log);

            _modules.put(def.getModuleId(), module);
        }

        addBuiltin(LOG_SOURCE_SERVICE_ID, LogSource.class, _logSource);

        Log log = _logSource.getLog(RegistryImpl.CLASS_FACTORY_SERVICE_ID);

        _classFactory = new ClassFactoryImpl(contextClassLoader, log);

        addBuiltin(RegistryImpl.CLASS_FACTORY_SERVICE_ID, ClassFactory.class, _classFactory);

        log = _logSource.getLog(THREAD_CLEANUP_HUB_SERVICE_ID);

        _cleanupHub = new ThreadCleanupHubImpl(log);

        addBuiltin(THREAD_CLEANUP_HUB_SERVICE_ID, ThreadCleanupHub.class, _cleanupHub);

        log = _logSource.getLog(REGISTRY_SHUTDOWN_HUB_SERVICE_ID);

        _registryShutdownHub = new RegistryShutdownHubImpl(log);

        addBuiltin(
                REGISTRY_SHUTDOWN_HUB_SERVICE_ID,
                RegistryShutdownHub.class,
                _registryShutdownHub);

        _lifecycles.put("singleton", new SingletonServiceLifecycle());

        // Ask all modules to eager-load any services marked with @EagerLoad

        for (Module m : _modules.values())
            m.eagerLoadServices();
    }

    private <T> void addBuiltin(String serviceId, Class<T> serviceInterface, T service)
    {
        _builtinTypes.put(serviceId, serviceInterface);
        _builtinServices.put(serviceId, service);
    }

    public synchronized void shutdown()
    {
        _lock.lock();

        _registryShutdownHub.fireRegistryDidShutdown();
    }

    /** Internal access, usualy from another module. */
    public <T> T getService(String serviceId, Class<T> serviceInterface, Module module)
    {
        _lock.check();

        T result = checkForBuiltinService(serviceId, serviceInterface);
        if (result != null) return result;

        result = checkForServiceOverrides(serviceId, serviceInterface);
        if (result != null) return result;

        // Checking serviceId and serviceInterface is overkill; they have been checked and rechecked
        // all the way to here.

        Module containingModule = locateModuleForService(serviceId);

        return containingModule.getService(serviceId, serviceInterface, module);
    }

    private <T> T checkForBuiltinService(String serviceId, Class<T> serviceInterface)
    {
        Object service = _builtinServices.get(serviceId);

        if (service == null) return null;

        try
        {
            return serviceInterface.cast(service);
        }
        catch (ClassCastException ex)
        {
            throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, _builtinTypes
                    .get(serviceId), serviceInterface));
        }
    }

    private <T> T checkForServiceOverrides(String serviceId, Class<T> serviceInterface)
    {
        Object service = _serviceOverrides.get(serviceId);

        if (service == null) return null;

        try
        {
            return serviceInterface.cast(service);
        }
        catch (ClassCastException ex)
        {
            throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, service
                    .getClass(), serviceInterface));
        }
    }

    /**
     * Access for a service from the outside world (limited to publically visible services).
     */
    public <T> T getService(String serviceId, Class<T> serviceInterface)
    {
        _lock.check();

        return getService(expandSymbols(serviceId), serviceInterface, null);
    }

    public void cleanupThread()
    {
        _lock.check();

        _cleanupHub.cleanup();
    }

    private Module locateModuleForService(String serviceId)
    {
        String moduleId = IOCUtilities.extractModuleId(serviceId);

        Module module = _modules.get(moduleId);

        if (module == null) throw new RuntimeException(IOCMessages.noSuchModule(moduleId));

        return module;
    }

    public <T> Collection<T> getUnorderedConfiguration(ServiceDef serviceDef, Class<T> objectType)
    {
        _lock.check();

        final Collection<T> result = newList();

        Configuration<T> configuration = new Configuration<T>()
        {
            public void add(T object)
            {
                result.add(object);
            }
        };

        Collection<Module> modules = modulesThatContributeToService(serviceDef);

        for (Module m : modules)
            addToUnorderedConfiguration(configuration, objectType, serviceDef, m);

        return result;
    }

    public <T> List<T> getOrderedConfiguration(ServiceDef serviceDef, Class<T> objectType)
    {
        _lock.check();

        Log log = getLog(serviceDef.getServiceId());

        final Orderer<T> orderer = new Orderer<T>(log);

        OrderedConfiguration<T> configuration = new OrderedConfigurationToOrdererAdaptor<T>(orderer);

        Collection<Module> modules = modulesThatContributeToService(serviceDef);

        for (Module m : modules)
            addToOrderedConfiguration(configuration, objectType, serviceDef, m);

        return orderer.getOrdered();
    }

    public <K, V> Map<K, V> getMappedConfiguration(ServiceDef serviceDef, Class<K> keyType,
            Class<V> objectType)
    {
        _lock.check();

        final Map<K, V> result = newMap();

        MappedConfiguration<K, V> configuration = new MappedConfiguration<K, V>()
        {
            public void add(K key, V value)
            {
                result.put(key, value);
            }
        };

        Map<K, ContributionDef> keyToContribution = newMap();

        Collection<Module> modules = modulesThatContributeToService(serviceDef);

        for (Module m : modules)
            addToMappedConfiguration(
                    configuration,
                    keyToContribution,
                    keyType,
                    objectType,
                    serviceDef,
                    m);

        return result;
    }

    private Collection<Module> modulesThatContributeToService(ServiceDef serviceDef)
    {
        if (serviceDef.isPrivate())
        {
            String moduleId = IOCUtilities.extractModuleId(serviceDef.getServiceId());
            Module module = _modules.get(moduleId);

            return Arrays.asList(module);
        }

        return _modules.values();
    }

    private <K, V> void addToMappedConfiguration(MappedConfiguration<K, V> configuration,
            Map<K, ContributionDef> keyToContribution, Class<K> keyClass, Class<V> valueType,
            ServiceDef serviceDef, Module module)
    {
        String serviceId = serviceDef.getServiceId();
        Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);

        if (contributions.isEmpty()) return;

        Log log = getLog(serviceId);
        boolean debug = log.isDebugEnabled();

        ServiceLocator locator = new ServiceResourcesImpl(this, module, serviceDef, log);

        for (ContributionDef def : contributions)
        {
            MappedConfiguration<K, V> validating = new ValidatingMappedConfigurationWrapper<K, V>(
                    serviceId, def, log, keyClass, valueType, keyToContribution, configuration);

            if (debug) log.debug(IOCMessages.invokingMethod(def));

            def.contribute(module, locator, validating);
        }

    }

    private <T> void addToUnorderedConfiguration(Configuration<T> configuration,
            Class<T> valueType, ServiceDef serviceDef, Module module)
    {
        String serviceId = serviceDef.getServiceId();
        Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);

        if (contributions.isEmpty()) return;

        Log log = getLog(serviceId);
        boolean debug = log.isDebugEnabled();

        ServiceLocator locator = new ServiceResourcesImpl(this, module, serviceDef, log);

        for (ContributionDef def : contributions)
        {
            Configuration<T> validating = new ValidatingConfigurationWrapper<T>(serviceId, log,
                    valueType, def, configuration);

            if (debug) log.debug(IOCMessages.invokingMethod(def));

            def.contribute(module, locator, validating);
        }
    }

    private <T> void addToOrderedConfiguration(OrderedConfiguration<T> configuration,
            Class<T> valueType, ServiceDef serviceDef, Module module)
    {
        String serviceId = serviceDef.getServiceId();
        Set<ContributionDef> contributions = module.getContributorDefsForService(serviceId);

        if (contributions.isEmpty()) return;

        Log log = getLog(serviceId);
        boolean debug = log.isDebugEnabled();

        ServiceLocator locator = new ServiceResourcesImpl(this, module, serviceDef, log);

        for (ContributionDef def : contributions)
        {
            OrderedConfiguration<T> validating = new ValidatingOrderedConfigurationWrapper<T>(
                    serviceId, module.getModuleId(), def, log, valueType, configuration);

            if (debug) log.debug(IOCMessages.invokingMethod(def));

            def.contribute(module, locator, validating);
        }
    }

    // Seems like something that could be cached.
    public <T> T getService(Class<T> serviceInterface, Module module)
    {
        _lock.check();

        List<String> ids = CollectionFactory.newList();

        for (Module m : _modules.values())
        {
            Collection<String> matchingIds = m.findServiceIdsForInterface(serviceInterface, module);
            ids.addAll(matchingIds);
        }

        switch (ids.size())
        {
            case 0:

                throw new RuntimeException(IOCMessages.noServiceMatchesType(serviceInterface));

            case 1:

                String serviceId = ids.get(0);

                return getService(serviceId, serviceInterface, module);

            default:

                Collections.sort(ids);

                throw new RuntimeException(IOCMessages.manyServiceMatches(serviceInterface, ids));
        }
    }

    public <T> T getService(Class<T> serviceInterface)
    {
        _lock.check();

        return getService(serviceInterface, null);
    }

    public ServiceLifecycle getServiceLifecycle(String lifecycle)
    {
        _lock.check();

        ServiceLifecycle result = _lifecycles.get(lifecycle);

        if (result == null)
        {
            ServiceLifecycleSource source = getService(
                    "tapestry.ioc.ServiceLifecycleSource",
                    ServiceLifecycleSource.class);
            result = source.get(lifecycle);
        }

        if (result == null) throw new RuntimeException(IOCMessages.unknownLifecycle(lifecycle));

        return result;
    }

    public List<ServiceDecorator> findDecoratorsForService(ServiceDef serviceDef)
    {
        _lock.check();

        Log log = getLog(serviceDef.getServiceId());

        Orderer<DecoratorDef> orderer = new Orderer<DecoratorDef>(log);

        addDecoratorDefsToOrderer(orderer, serviceDef);

        List<DecoratorDef> ordered = orderer.getOrdered();

        return convertDecoratorDefsToServiceDecorators(ordered, serviceDef, log);
    }

    private List<ServiceDecorator> convertDecoratorDefsToServiceDecorators(
            List<DecoratorDef> ordered, ServiceDef serviceDef, Log log)
    {
        List<ServiceDecorator> result = newList();

        ServiceResources resources = null;
        String moduleId = null;
        Module module = null;

        for (DecoratorDef dd : ordered)
        {
            String decoratorModuleId = IOCUtilities.extractModuleId(dd.getDecoratorId());

            // Whenever the module id containing the decorator changes,
            // "refresh" the resources, etc., to point to the (new) module.
            // This means that the ServiceResources will identify services within
            // the decorators module (important in terms of service visibility
            // and abbreviated service ids).

            if (!decoratorModuleId.equals(moduleId))
            {
                moduleId = decoratorModuleId;

                module = _modules.get(moduleId);

                resources = new ServiceResourcesImpl(this, module, serviceDef, log);
            }

            ServiceDecorator decorator = dd.createDecorator(module, resources);

            result.add(decorator);
        }

        return result;
    }

    private void addDecoratorDefsToOrderer(Orderer<DecoratorDef> orderer, ServiceDef serviceDef)
    {
        if (serviceDef.isPrivate())
        {
            Set<DecoratorDef> privateDecorators = findDecoratorsDefsForPrivateService(serviceDef);
            addToOrderer(orderer, privateDecorators);
        }
        else
        {
            for (Module m : _modules.values())
            {
                Set<DecoratorDef> moduleDecorators = m.findMatchingDecoratorDefs(serviceDef);
                addToOrderer(orderer, moduleDecorators);
            }
        }
    }

    private void addToOrderer(Orderer<DecoratorDef> orderer, Set<DecoratorDef> decorators)
    {
        for (DecoratorDef df : decorators)
        {
            orderer.add(df.getDecoratorId(), df, df.getConstraints());
        }
    }

    private Set<DecoratorDef> findDecoratorsDefsForPrivateService(ServiceDef serviceDef)
    {
        String moduleId = IOCUtilities.extractModuleId(serviceDef.getServiceId());
        Module module = _modules.get(moduleId);

        return module.findMatchingDecoratorDefs(serviceDef);
    }

    public Log getLog(Class clazz)
    {
        _lock.check();

        return _logSource.getLog(clazz);
    }

    public Log getLog(String name)
    {
        _lock.check();

        return _logSource.getLog(name);
    }

    public ClassFab newClass(Class serviceInterface)
    {
        _lock.check();

        return _classFactory.newClass(serviceInterface);
    }

    public <T> T getObject(String reference, Class<T> objectType, ServiceLocator locator)
    {
        _lock.check();

        ObjectProvider masterProvider = getService(
                IOCConstants.MASTER_OBJECT_PROVIDER_SERVICE_ID,
                ObjectProvider.class);

        return masterProvider.provide(reference, objectType, locator);
    }

    public <T> T getObject(String reference, Class<T> objectType)
    {
        _lock.check();

        // Concerened about this causing potential endless loops.
        return getObject(reference, objectType, this);
    }

    public void addRegistryShutdownListener(RegistryShutdownListener listener)
    {
        _lock.check();

        _registryShutdownHub.addRegistryShutdownListener(listener);
    }

    public String expandSymbols(String input)
    {
        // Again, a bit of work to avoid instantiating the SymbolSource until absolutely necessary.

        if (!InternalUtils.containsSymbols(input)) return input;

        return getSymbolSource().expandSymbols(input);
    }

    /** Defers obtaining the symbol source until actually needed. */
    private synchronized SymbolSource getSymbolSource()
    {
        if (_symbolSource == null)
            _symbolSource = getService(SYMBOL_SOURCE_SERVICE_ID, SymbolSource.class);

        return _symbolSource;
    }

}
TOP

Related Classes of org.apache.tapestry.ioc.internal.RegistryImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.