All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wamblee.system.spring.SpringComponent Maven / Gradle / Ivy

/*
 * Copyright 2005-2010 the original author or authors.
 * 
 * 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.wamblee.system.spring;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.RootBeanDefinition;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;

import org.wamblee.system.core.AbstractComponent;
import org.wamblee.system.core.DefaultScope;
import org.wamblee.system.core.ProvidedInterface;
import org.wamblee.system.core.RequiredInterface;
import org.wamblee.system.core.Scope;
import org.wamblee.system.core.SystemAssemblyException;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Represents a system configured based on spring. The spring config files that
 * are configured should not contain any PropertyPlaceholderConfigurer objects.
 * 
 * @author Erik Brakkee
 */
public class SpringComponent extends AbstractComponent {
    private static final String CONTEXT_KEY = "context";

    static final ThreadLocal THIS = new ThreadLocal();

    static final ThreadLocal SCOPE = new ThreadLocal();

    private Properties properties;

    private String[] configFiles;

    private Map provided;

    private Map required;

    private Map propertyObjects;

    /**
     * Constructs a spring system.
     * 
     * @param aName
     *            Name of the system.
     * @param aConfigFil
     *            Spring config files to read.
     * @param aProvided
     *            Map of bean name to service descriptor describing the bean
     *            names that the spring config files use for each required
     *            service.
     * @param aRequired
     *            Map of bean name to service descriptor describing the bean
     *            names that the spring config files use for each required
     *            service.
     */
    public SpringComponent(String aName, String[] aConfigFiles,
        Map aProvided,
        Map aRequired) {
        super(aName, aProvided.values().toArray(new ProvidedInterface[0]),
            aRequired.keySet().toArray(new RequiredInterface[0]));
        properties = new Properties();
        configFiles = aConfigFiles;
        provided = aProvided;
        required = aRequired;
        propertyObjects = new HashMap();
    }

    /**
     * Must be called to make a property available in the application context.
     * 
     * @param aKey
     *            Property key.
     * @param aValue
     *            Property value.
     */
    public void setProperty(String aKey, String aValue) {
        properties.put(aKey, aValue);
    }

    public void addProperties(Properties aProperties) {
        for (Object key : aProperties.keySet()) {
            setProperty((String) key, aProperties.getProperty((String) key));
        }
    }

    public void addProperties(String aBeanname, Properties aProperties) {
        propertyObjects.put(aBeanname, aProperties);
    }

    public Properties getProperties(String aBeanname) {
        return propertyObjects.get(aBeanname);
    }

    @Override
    protected Scope doStart(Scope aExternalScope) {
        SpringComponent old = THIS.get();
        Scope oldScope = SCOPE.get();
        THIS.set(this);

        Scope scope = new DefaultScope(getProvidedInterfaces().toArray(
            new ProvidedInterface[0]), aExternalScope);
        SCOPE.set(scope);

        try {
            GenericApplicationContext parentContext = new GenericApplicationContext();

            registerRequiredServices(parentContext);
            registerPropertyObjects(parentContext);

            parentContext.refresh();

            System.out.println("Parent context " + parentContext);

            AbstractApplicationContext context = parseConfigFiles(parentContext);

            context.addBeanFactoryPostProcessor(new PropertySetter(properties));
            context.refresh();

            exposeProvidedServices(context, aExternalScope);

            scope.put(CONTEXT_KEY, context);

            return scope;
        } catch (Exception e) {
            throw new SystemAssemblyException(
                "Failed to assemble spring system " + getName(), e);
        } finally {
            THIS.set(old);
            SCOPE.set(oldScope);
        }
    }

    private void exposeProvidedServices(AbstractApplicationContext aContext,
        Scope aScope) {
        // Call addService for each provided service.
        for (String name : provided.keySet()) {
            Object svc = aContext.getBean(name);

            if (svc == null) {
                throw new IllegalArgumentException(getQualifiedName() +
                    ": service '" + name + "' is null");
            }

            addInterface(provided.get(name), svc, aScope);
            System.out.println("addService " + provided.get(name) + " " + svc);
        }
    }

    private AbstractApplicationContext parseConfigFiles(
        GenericApplicationContext aParentContext) {
        // Parse spring config files
        return new ClassPathXmlApplicationContext((String[]) configFiles,
            false, aParentContext);
    }

    private void registerRequiredServices(
        GenericApplicationContext aParentContext) {
        // Register required services in a parent context
        for (RequiredInterface requiredIntf : getRequiredInterfaces()) {
            String beanName = required.get(requiredIntf);

            if ((beanName != null) && (beanName.length() > 0)) {
                ConstructorArgumentValues cargs = new ConstructorArgumentValues();
                cargs.addGenericArgumentValue(requiredIntf.getName());

                BeanDefinition definition = new RootBeanDefinition(
                    RequiredServiceBean.class, cargs,
                    new MutablePropertyValues());
                aParentContext.registerBeanDefinition(beanName, definition);
            } else {
                // The required interface is not required by the spring config
                // but by the sub-class directly.
            }
        }
    }

    private void registerPropertyObjects(
        GenericApplicationContext aParentContext) {
        for (String beanName : propertyObjects.keySet()) {
            ConstructorArgumentValues cargs = new ConstructorArgumentValues();
            cargs.addGenericArgumentValue(PropertySetter
                .createPropertyFile(propertyObjects.get(beanName)));

            BeanDefinition definition = new RootBeanDefinition(
                ConfiguredProperties.class, cargs, new MutablePropertyValues());
            aParentContext.registerBeanDefinition(beanName, definition);
        }
    }

    @Override
    protected void doStop(Scope aRuntime) {
        AbstractApplicationContext context = (AbstractApplicationContext) aRuntime
            .get(CONTEXT_KEY);
        context.close();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy