info.magnolia.test.mock.AbstractComponentProvider.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.test.mock.AbstractComponentProvider.java

Source

/**
 * This file Copyright (c) 2011-2012 Magnolia International
 * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
 *
 *
 * This file is dual-licensed under both the Magnolia
 * Network Agreement and the GNU General Public License.
 * You may elect to use one or the other of these licenses.
 *
 * This file is distributed in the hope that it will be
 * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
 * Redistribution, except as permitted by whichever of the GPL
 * or MNA you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or
 * modify this file under the terms of the GNU General
 * Public License, Version 3, as published by the Free Software
 * Foundation.  You should have received a copy of the GNU
 * General Public License, Version 3 along with this program;
 * if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 2. For the Magnolia Network Agreement (MNA), this file
 * and the accompanying materials are made available under the
 * terms of the MNA which accompanies this distribution, and
 * is available at http://www.magnolia-cms.com/mna.html
 *
 * Any modifications to this file must keep this entire header
 * intact.
 *
 */
package info.magnolia.test.mock;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import info.magnolia.cms.util.DeprecationUtil;
import info.magnolia.objectfactory.Classes;
import info.magnolia.objectfactory.ComponentFactory;
import info.magnolia.objectfactory.ComponentFactoryUtil;
import info.magnolia.objectfactory.ComponentProvider;
import info.magnolia.objectfactory.ConfiguredComponentFactory;
import info.magnolia.objectfactory.LazyObservedComponentFactory;
import info.magnolia.objectfactory.MgnlInstantiationException;
import info.magnolia.objectfactory.configuration.ComponentConfiguration;
import info.magnolia.objectfactory.configuration.ProviderConfiguration;
import info.magnolia.objectfactory.configuration.ComponentProviderConfiguration;
import info.magnolia.objectfactory.configuration.ConfiguredComponentConfiguration;
import info.magnolia.objectfactory.configuration.ImplementationConfiguration;
import info.magnolia.objectfactory.configuration.InstanceConfiguration;

/**
 * Abstract ComponentProvider that supports component factories. Subclasses are responsible for registering components
 * and factories. Both components and factories are created on-demand when registered as classes.
 *
 * @version $Id$
 */
public abstract class AbstractComponentProvider implements ComponentProvider {

    private static class ComponentDefinition<T> {

        private Class<T> type;
        private T instance;
        private ComponentFactory<T> factory;
        private Class<? extends ComponentFactory<T>> factoryClass;
        private Class<? extends T> implementationType;

        public Class<T> getType() {
            return type;
        }

        public void setType(Class<T> type) {
            this.type = type;
        }

        public T getInstance() {
            return instance;
        }

        public void setInstance(T instance) {
            this.instance = instance;
        }

        public ComponentFactory<T> getFactory() {
            return factory;
        }

        public void setFactory(ComponentFactory<T> factory) {
            this.factory = factory;
        }

        /**
         * The implementation type is unknown for components created by ComponentFactoryUtil.
         */
        public Class<? extends T> getImplementationType() {
            return implementationType;
        }

        public void setImplementationType(Class<? extends T> implementationType) {
            this.implementationType = implementationType;
        }

        public Class<? extends ComponentFactory<T>> getFactoryClass() {
            return factoryClass;
        }

        public void setFactoryClass(Class<? extends ComponentFactory<T>> factoryClass) {
            this.factoryClass = factoryClass;
        }

        public boolean isFactory() {
            return factoryClass != null || factory != null;
        }
    }

    private final Logger log = LoggerFactory.getLogger(getClass());

    private ComponentProvider parent;
    private final Map<Class<?>, ComponentDefinition<?>> definitions = new ConcurrentHashMap<Class<?>, ComponentDefinition<?>>();
    private final ThreadLocal<List<Class<?>>> creationStack = new ThreadLocal<List<Class<?>>>();

    protected AbstractComponentProvider() {
    }

    protected AbstractComponentProvider(ComponentProvider parent) {
        this();
        this.parent = parent;
    }

    @Override
    public ComponentProvider getParent() {
        return parent;
    }

    @Override
    public <T> T getSingleton(Class<T> type) {
        DeprecationUtil.isDeprecated();
        return getComponent(type);
    }

    @Override
    public synchronized <T> T getComponent(Class<T> type) {
        ComponentDefinition<T> definition = getComponentDefinition(type);
        if (definition == null) {
            if (parent != null) {
                return parent.getComponent(type);
            }
            // Register the component on-demand
            if (!Classes.isConcrete(type)) {
                throw new MgnlInstantiationException("No concrete implementation defined for " + type);
            }
            registerImplementation(type, type);
            definition = getComponentDefinition(type);
        }
        T instance = definition.getInstance();
        if (instance == null) {
            log.debug("No instance for {} yet, creating new one.", type);
            instance = newInstance(type);
            definition.setInstance(instance);
            log.debug("New instance for {} created: {}", type, instance);
        }
        return instance;
    }

    @Override
    public synchronized <T> T newInstance(Class<T> type, Object... parameters) {
        if (type == null) {
            log.error("type can't be null", new Throwable());
            return null;
        }

        List<Class<?>> stack = creationStack.get();
        try {

            if (stack == null) {
                stack = new ArrayList<Class<?>>();
                stack.add(type);
                creationStack.set(stack);
            } else if (stack.contains(type)) {
                throw new CircularDependencyException(type, stack);
            } else {
                stack.add(type);
            }

            ComponentDefinition<T> definition = getComponentDefinition(type);
            if (definition == null) {
                if (parent != null) {
                    return parent.newInstance(type, parameters);
                }
                if (!Classes.isConcrete(type)) {
                    throw new MgnlInstantiationException("No concrete implementation defined for " + type);
                }
                return createInstance(type, parameters);
            }
            if (definition.isFactory()) {
                return this.<T>instantiateFactoryIfNecessary(definition).newInstance();
            }
            return createInstance(definition.getImplementationType(), parameters);
        } catch (Exception e) {
            if (e instanceof MgnlInstantiationException) {
                throw (MgnlInstantiationException) e;
            }
            throw new MgnlInstantiationException("Can't instantiate an implementation of this class ["
                    + type.getName() + "]: " + ExceptionUtils.getMessage(e), e);
        } finally {
            stack.remove(type);
            if (stack.isEmpty()) {
                creationStack.remove();
            }
        }
    }

    @Override
    public <T> Class<? extends T> getImplementation(Class<T> type) throws ClassNotFoundException {
        ComponentDefinition<T> definition = getComponentDefinition(type);
        if (definition == null) {
            if (parent != null) {
                return parent.getImplementation(type);
            }
            return type;
        }
        if (definition.isFactory()) {
            return type;
        }
        return definition.getImplementationType();
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void configure(ComponentProviderConfiguration configuration) {

        for (Map.Entry<Class, ComponentConfiguration> entry : configuration.getComponents().entrySet()) {
            ComponentConfiguration value = entry.getValue();
            if (value instanceof ImplementationConfiguration) {
                ImplementationConfiguration config = (ImplementationConfiguration) value;
                registerImplementation(config.getType(), config.getImplementation());
            } else if (value instanceof InstanceConfiguration) {
                InstanceConfiguration config = (InstanceConfiguration) value;
                registerInstance(config.getType(), config.getInstance());
            } else if (value instanceof ProviderConfiguration) {
                ProviderConfiguration config = (ProviderConfiguration) value;
                registerComponentFactory(config.getType(), config.getProviderClass());
            } else if (value instanceof ConfiguredComponentConfiguration) {
                ConfiguredComponentConfiguration config = (ConfiguredComponentConfiguration) value;
                registerConfiguredComponent(config.getType(), config.getWorkspace(), config.getPath(),
                        config.isObserved());
            }
        }

        for (Map.Entry<Class<?>, Class<?>> entry : configuration.getTypeMapping().entrySet()) {
            registerImplementation((Class) entry.getKey(), (Class) entry.getValue());
        }
    }

    private <T> void registerComponentFactory(Class<T> type, Class<? extends ComponentFactory<T>> factoryClass) {
        ComponentDefinition<T> definition = new ComponentDefinition<T>();
        definition.setType(type);
        definition.setFactoryClass(factoryClass);
        definitions.put(type, definition);
    }

    public synchronized <T> void registerConfiguredComponent(Class<T> type, final String workspace,
            final String path, boolean observed) {
        final ComponentFactory<T> factory;
        if (observed) {
            factory = new LazyObservedComponentFactory<T>(workspace, path, type, this);
        } else {
            factory = new ConfiguredComponentFactory<T>(path, workspace, this);
        }
        registerComponentFactory(type, factory);

    }

    @SuppressWarnings("unchecked")
    public synchronized <T> void registerImplementation(Class<T> type, Class<? extends T> implementationType) {
        if (definitions.containsKey(type))
            throw new MgnlInstantiationException("Component already registered for type " + type.getName());
        if (implementationType == null) {
            throw new MgnlInstantiationException("ImplementationConfiguration type is not set a for type " + type);
        }
        if (!Classes.isConcrete(implementationType)) {
            throw new MgnlInstantiationException(
                    "ImplementationConfiguration type is not a concrete class for type " + type);
        }
        if (ComponentFactory.class.isAssignableFrom(implementationType)) {
            ComponentDefinition<T> definition = new ComponentDefinition<T>();
            definition.setType(type);
            definition.setFactoryClass((Class<? extends ComponentFactory<T>>) implementationType);
            definitions.put(type, definition);
        } else {
            ComponentDefinition<T> definition = new ComponentDefinition<T>();
            definition.setType(type);
            definition.setImplementationType(implementationType);
            definitions.put(type, definition);
        }
    }

    public synchronized <T> void registerComponentFactory(Class<T> type, ComponentFactory<T> componentFactory) {
        if (type == null) {
            throw new NullPointerException(
                    "Null type is not allowed. Please check your type definition and classes on the classpath.");
        }
        if (definitions.containsKey(type)) {
            throw new MgnlInstantiationException("Component already registered for type " + type.getName());
        }
        ComponentDefinition<T> definition = new ComponentDefinition<T>();
        definition.setType(type);
        definition.setFactory(componentFactory);
        definitions.put(type, definition);
    }

    public synchronized <T> void registerInstance(Class<T> type, T instance) {
        if (definitions.containsKey(type)) {
            throw new MgnlInstantiationException("Component already registered for type " + type.getName());
        }
        ComponentDefinition<T> definition = new ComponentDefinition<T>();
        definition.setType(type);
        definition.setInstance(instance);
        definitions.put(type, definition);
    }

    @SuppressWarnings("unchecked")
    private <T> ComponentDefinition<T> getComponentDefinition(Class<T> type) {
        return (ComponentDefinition<T>) definitions.get(type);
    }

    private <T> ComponentFactory<T> instantiateFactoryIfNecessary(ComponentDefinition<T> definition)
            throws InvocationTargetException, IllegalAccessException, InstantiationException {
        if (definition.getFactoryClass() != null && definition.getFactory() == null) {
            definition.setFactory(ComponentFactoryUtil.createFactory(definition.getFactoryClass(), this));
        }
        return definition.getFactory();
    }

    protected <T> T createInstance(Class<T> implementationType, Object... parameters) {

        return Classes.getClassFactory().newInstance(implementationType, parameters);
    }

    protected synchronized void removeComponent(Class<?> type) {
        definitions.remove(type);
    }

    protected synchronized void clear() {
        definitions.clear();
    }
}