Package org.apache.tapestry.ioc.internal

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

// Copyright 2006, 2007 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 static org.apache.tapestry.ioc.internal.util.Defense.notNull;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
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.AnnotationProvider;
import org.apache.tapestry.ioc.Configuration;
import org.apache.tapestry.ioc.IOCConstants;
import org.apache.tapestry.ioc.LogSource;
import org.apache.tapestry.ioc.MappedConfiguration;
import org.apache.tapestry.ioc.ObjectLocator;
import org.apache.tapestry.ioc.ObjectProvider;
import org.apache.tapestry.ioc.OrderedConfiguration;
import org.apache.tapestry.ioc.Registry;
import org.apache.tapestry.ioc.ServiceDecorator;
import org.apache.tapestry.ioc.ServiceLifecycle;
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.RegistryShutdownHubImpl;
import org.apache.tapestry.ioc.internal.services.ThreadCleanupHubImpl;
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.TapestryIOCModule;
import org.apache.tapestry.ioc.services.ThreadCleanupHub;

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

    private static final String REGISTRY_SHUTDOWN_HUB_SERVICE_ID = "RegistryShutdownHub";

    static final String THREAD_CLEANUP_HUB_SERVICE_ID = "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 = "ClassFactory";

    static final String LOG_SOURCE_SERVICE_ID = "LogSource";

    private final OneShotLock _lock = new OneShotLock();

    private final OneShotLock _eagerLoadLock = new OneShotLock();

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

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

    private final RegistryShutdownHubImpl _registryShutdownHub;

    private final LogSource _logSource;

    /** Map from service id to the Module that contains the service. */
    private final Map<String, Module> _serviceIdToModule = newCaseInsensitiveMap();

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

    private final ThreadCleanupHubImpl _cleanupHub;

    private final ClassFactory _classFactory;

    private SymbolSource _symbolSource;

    private final List<Module> _modules = newList();

    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 classFactory
     *            TODO
     * @param logSource
     *            used to obtain Log instances
     */
    public RegistryImpl(Collection<ModuleDef> moduleDefs, ClassFactory classFactory,
            LogSource logSource)
    {
        _logSource = logSource;

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

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

            _modules.add(module);

            for (String serviceId : def.getServiceIds())
            {
                Module existing = _serviceIdToModule.get(serviceId);

                if (existing != null)
                    throw new RuntimeException(IOCMessages.serviceIdConflict(serviceId, existing
                            .getServiceDef(serviceId), module.getServiceDef(serviceId)));

                _serviceIdToModule.put(serviceId, module);
            }
        }

        addBuiltin(LOG_SOURCE_SERVICE_ID, LogSource.class, _logSource);

        _classFactory = classFactory;

        addBuiltin(CLASS_FACTORY_SERVICE_ID, ClassFactory.class, _classFactory);

        Log log = logForBuiltinService(THREAD_CLEANUP_HUB_SERVICE_ID);

        _cleanupHub = new ThreadCleanupHubImpl(log);

        addBuiltin(THREAD_CLEANUP_HUB_SERVICE_ID, ThreadCleanupHub.class, _cleanupHub);

        log = logForBuiltinService(REGISTRY_SHUTDOWN_HUB_SERVICE_ID);

        _registryShutdownHub = new RegistryShutdownHubImpl(log);

        addBuiltin(
                REGISTRY_SHUTDOWN_HUB_SERVICE_ID,
                RegistryShutdownHub.class,
                _registryShutdownHub);

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

    /**
     * It's not unreasonable for an eagerly-loaded service to decide to start a thread, at which
     * point we raise issues about improper publishing of the Registry instance from the
     * RegistryImpl constructor. Moving eager loading of services out to its own method should
     * ensure thread safety.
     */
    public void eagerLoadServices()
    {
        _eagerLoadLock.lock();

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

        cleanupThread();
    }

    public Log logForService(String serviceId)
    {
        Module module = _serviceIdToModule.get(serviceId);

        assert module != null;

        return _logSource.getLog(module.getLogName() + "." + serviceId);
    }

    private Log logForBuiltinService(String serviceId)
    {
        return _logSource.getLog(TapestryIOCModule.class + "." + serviceId);
    }

    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, usually from another module. */
    public <T> T getService(String serviceId, Class<T> serviceInterface)
    {
        _lock.check();

        T result = checkForBuiltinService(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);
    }

    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));
        }
    }

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

        _cleanupHub.cleanup();
    }

    private Module locateModuleForService(String serviceId)
    {
        Module module = _serviceIdToModule.get(serviceId);

        if (module == null)
            throw new RuntimeException(IOCMessages.noSuchService(serviceId, _serviceIdToModule
                    .keySet()));

        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 = _modules;

        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 = null;

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

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

        Collection<Module> modules = _modules;

        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();

        // When the key type is String, then a case insensitive map is used for both cases.

        final Map<K, V> result = newConfigurationMap(keyType);
        Map<K, ContributionDef> keyToContribution = newConfigurationMap(keyType);

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

        Collection<Module> modules = _modules;

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

        return result;
    }

    @SuppressWarnings("unchecked")
    private <K, V> Map<K, V> newConfigurationMap(Class<K> keyType)
    {
        if (keyType.equals(String.class))
        {
            Map<String, K> result = newCaseInsensitiveMap();

            return (Map<K, V>) result;
        }

        return newMap();
    }

    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 = logForService(serviceId);

        boolean debug = log.isDebugEnabled();

        ObjectLocator locator = new ServiceResourcesImpl(this, module, serviceDef, _classFactory,
                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 = logForService(serviceId);

        boolean debug = log.isDebugEnabled();

        ObjectLocator locator = new ServiceResourcesImpl(this, module, serviceDef, _classFactory,
                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 = logForService(serviceId);
        boolean debug = log.isDebugEnabled();

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

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

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

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

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

        List<String> serviceIds = findServiceIdsForInterface(serviceInterface);

        if (serviceIds == null) serviceIds = Collections.emptyList();

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

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

            case 1:

                String serviceId = serviceIds.get(0);

                return getService(serviceId, serviceInterface);

            default:

                Collections.sort(serviceIds);

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

    private List<String> findServiceIdsForInterface(Class serviceInterface)
    {
        List<String> result = newList();

        for (Module module : _modules)
            result.addAll(module.findServiceIdsForInterface(serviceInterface));

        for (Map.Entry<String, Object> entry : _builtinServices.entrySet())
        {
            if (serviceInterface.isInstance(entry.getValue())) result.add(entry.getKey());
        }

        Collections.sort(result);

        return result;
    }

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

        ServiceLifecycle result = _lifecycles.get(scope);

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

        if (result == null) throw new RuntimeException(IOCMessages.unknownScope(scope));

        return result;
    }

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

        assert serviceDef != null;

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

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

        for (Module module : _modules)
        {
            Set<DecoratorDef> decorators = module.findMatchingDecoratorDefs(serviceDef);

            if (decorators.isEmpty()) continue;

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

            for (DecoratorDef dd : decorators)
            {
                ServiceDecorator sd = dd.createDecorator(module, resources);

                orderer.add(dd.getDecoratorId(), sd, dd.getConstraints());
            }
        }

        return orderer.getOrdered();
    }

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

        return _classFactory.newClass(serviceInterface);
    }

    private <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider,
            ObjectLocator locator)
    {
        _lock.check();

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

        AnnotationProvider effectiveProvider = annotationProvider != null ? annotationProvider
                : new NullAnnotationProvider();

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

    public <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider)
    {
        _lock.check();

        return getObject(objectType, annotationProvider, this);
    }

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

        _registryShutdownHub.addRegistryShutdownListener(listener);
    }

    public String expandSymbols(String input)
    {
        _lock.check();

        // 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;
    }

    public <T> T autobuild(Class<T> clazz)
    {
        notNull(clazz, "clazz");

        Constructor constructor = InternalUtils.findAutobuildConstructor(clazz);

        if (constructor == null)
            throw new RuntimeException(IOCMessages.noAutobuildConstructor(clazz));

        Throwable failure = null;
        // An empty map, because when performing autobuilding outside the context of building a
        // service, we don't have defaults for Log, service id, etc.

        Map<Class, Object> empty = Collections.emptyMap();

        try
        {
            Object[] parameters = InternalUtils.calculateParametersForConstructor(
                    constructor,
                    this,
                    empty);

            return clazz.cast(constructor.newInstance(parameters));
        }
        catch (InvocationTargetException ite)
        {
            failure = ite.getTargetException();
        }
        catch (Exception ex)
        {
            failure = ex;
        }

        String description = _classFactory.getConstructorLocation(constructor).toString();

        throw new RuntimeException(IOCMessages.autobuildConstructorError(description, failure),
                failure);

    }

}
TOP

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

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.