org.springframework.xd.module.core.SimpleModule.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.xd.module.core.SimpleModule.java

Source

/*
 * Copyright 2013-2014 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.springframework.xd.module.core;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.builder.ParentContextCloserApplicationListener;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.ContextIdApplicationContextInitializer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.validation.BindException;
import org.springframework.xd.module.ModuleDefinition;
import org.springframework.xd.module.ModuleDeploymentProperties;
import org.springframework.xd.module.ModuleDescriptor;
import org.springframework.xd.module.SimpleModuleDefinition;
import org.springframework.xd.module.options.ModuleOptions;
import org.springframework.xd.module.options.PassthruModuleOptionsMetadata;

/**
 * A {@link Module} implementation backed by a Spring {@link ApplicationContext}.
 *
 * @author Mark Fisher
 * @author David Turanski
 * @author Gary Russell
 * @author Dave Syer
 * @author Ilayaperumal Gopinathan
 * @author Eric Bottard
 */
public abstract class SimpleModule extends AbstractModule {

    private final Log logger = LogFactory.getLog(this.getClass());

    private ConfigurableApplicationContext context;

    private final SpringApplicationBuilder application;

    private final AtomicInteger propertiesCounter = new AtomicInteger();

    private final Properties properties = new Properties();

    private final MutablePropertySources propertySources = new MutablePropertySources();

    private ConfigurableApplicationContext parent;

    private final List<ApplicationListener<?>> listeners = new ArrayList<ApplicationListener<?>>();

    private ModuleOptions moduleOptions;

    private final ClassLoader classLoader;

    public SimpleModule(ModuleDescriptor descriptor, ModuleDeploymentProperties deploymentProperties) {
        this(descriptor, deploymentProperties, null, defaultModuleOptions());
    }

    public SimpleModule(ModuleDescriptor descriptor, ModuleDeploymentProperties deploymentProperties,
            ClassLoader classLoader, ModuleOptions moduleOptions) {
        super(descriptor, deploymentProperties);
        this.moduleOptions = moduleOptions;
        application = new SpringApplicationBuilder().sources(PropertyPlaceholderAutoConfiguration.class).web(false)
                .showBanner(false);

        this.classLoader = classLoader;

        if (classLoader != null) {
            application.resourceLoader(new PathMatchingResourcePatternResolver(classLoader));
        }

        // Also add options as properties for now, b/c other parts of the system
        // (eg type conversion plugin) expects it
        this.properties.putAll(moduleOptionsToProperties(moduleOptions));

        application.profiles(moduleOptions.profilesToActivate());

        this.configureModuleApplicationContext((SimpleModuleDefinition) this.getDescriptor().getModuleDefinition());
    }

    /**
     * Subclasses implement this method to configure the application context from sources contained in the
      * {@link org.springframework.xd.module.ModuleDefinition}
     */
    protected abstract void configureModuleApplicationContext(SimpleModuleDefinition moduleDefinition);

    private Map<Object, Object> moduleOptionsToProperties(ModuleOptions moduleOptions) {
        Map<Object, Object> result = new HashMap<Object, Object>();
        EnumerablePropertySource<?> ps = moduleOptions.asPropertySource();
        for (String propname : ps.getPropertyNames()) {
            Object value = ps.getProperty(propname);
            if (value != null) {
                result.put(propname, value.toString());
            }
        }
        return result;
    }

    @Override
    public void setParentContext(ApplicationContext parent) {
        this.parent = (ConfigurableApplicationContext) parent;
    }

    @Override
    public void addSource(Object source) {
        application.sources(source);
    }

    @Override
    public void addProperties(Properties properties) {
        this.registerPropertySource(properties);
        this.properties.putAll(properties);
    }

    @Override
    public void addListener(ApplicationListener<?> listener) {
        this.listeners.add(listener);
    }

    @Override
    public Properties getProperties() {
        return this.properties;
    }

    public ConfigurableApplicationContext getApplicationContext() {
        return this.context;
    }

    @Override
    public <T> T getComponent(Class<T> requiredType) {
        return (this.context.isActive()) ? this.context.getBean(requiredType) : null;
    }

    public ClassLoader getClassLoader() {
        return classLoader;
    }

    @Override
    public <T> T getComponent(String componentName, Class<T> requiredType) {
        if (this.context.isActive() && this.context.containsBean(componentName)) {
            return context.getBean(componentName, requiredType);
        }
        return null;
    }

    private void registerPropertySource(Properties properties) {
        int propertiesIndex = this.propertiesCounter.getAndIncrement();
        String propertySourceName = "properties-" + propertiesIndex;
        PropertySource<?> propertySource = new PropertiesPropertySource(propertySourceName, properties);
        this.propertySources.addLast(propertySource);
    }

    /**
     * Initialize this module by creating its application context, and provide it with a special
     * {@link org.springframework.core.env.Environment} that knows how to resolve module options placeholders.
     */
    @Override
    public void initialize() {
        this.application.initializers(new ContextIdApplicationContextInitializer(this.toString()));
        ConfigurableEnvironment parentEnvironment = parent == null ? null : parent.getEnvironment();
        ModuleEnvironment environment = new ModuleEnvironment(moduleOptions.asPropertySource(), parentEnvironment);
        for (PropertySource<?> source : propertySources) {
            environment.getPropertySources().addFirst(source);
        }
        this.application.parent(parent);
        this.application.environment(environment);
        if (this.listeners.size() > 0) {
            application.listeners(this.listeners.toArray(new ApplicationListener<?>[this.listeners.size()]));
        }
        this.application.listeners(new ModuleParentContextCloserApplicationListener(getDescriptor().getIndex()));

        if (this.classLoader != null) {

            final ClassLoader defaultClassLoader = Thread.currentThread().getContextClassLoader();

            try {
                Thread.currentThread().setContextClassLoader(this.classLoader);
                this.context = this.application.run();
            } finally {
                Thread.currentThread().setContextClassLoader(defaultClassLoader);
            }
        } else {
            this.context = this.application.run();
        }

        if (logger.isInfoEnabled()) {
            logger.info("initialized module: " + this.toString());
        }
    }

    @Override
    public void start() {
        try {
            context.start();
        } catch (BeansException be) {
            // Make sure the context is destroyed; this will allow possible destruction of life-cycle beans registered
            // (example: MBeans)
            destroy();
            throw be;
        }
    }

    @Override
    public void stop() {
        if (context.isActive()) {
            context.stop(); // Shouldn't need to close() as well?
        }
    }

    @Override
    public boolean isRunning() {
        return context.isRunning();
    }

    @Override
    public void destroy() {
        if (context instanceof DisposableBean) {
            try {
                ((DisposableBean) context).destroy();
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
        }
    }

    private static ModuleOptions defaultModuleOptions() {
        try {
            return new PassthruModuleOptionsMetadata().interpolate(Collections.<String, String>emptyMap());
        } catch (BindException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Dedicated sublcass of {@link ParentContextCloserApplicationListener} used to create its own version of
     * ContextCloserListener that is aware of module order. Special care is taken so that no strong references to the
     * module context are retained (this is a *static* inner class).
     *
     * @author Eric Bottard
     */
    private static final class ModuleParentContextCloserApplicationListener
            extends ParentContextCloserApplicationListener {

        private final int index;

        public ModuleParentContextCloserApplicationListener(int index) {
            this.index = index;
        }

        @Override
        protected ContextCloserListener createContextCloserListener(ConfigurableApplicationContext child) {
            return new ModuleContextCloserListener(child, index);
        }

        /**
         * Module context closer listener that sets the order based on the module deployment index.
         */
        final static class ModuleContextCloserListener extends ContextCloserListener implements Ordered {

            private int index;

            public ModuleContextCloserListener(ConfigurableApplicationContext moduleContext, int index) {
                super(moduleContext);
                this.index = index;
            }

            @Override
            public int getOrder() {
                // Make sure producer modules get closed before the consumer modules (sink/processor)
                // by setting them the highest precedence. Smaller values come first.
                return index;
            }

        }
    }

}