Package org.springframework.xd.module.core

Source Code of org.springframework.xd.module.core.SimpleModule$ModuleParentContextCloserApplicationListener

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

    }
  }

}
TOP

Related Classes of org.springframework.xd.module.core.SimpleModule$ModuleParentContextCloserApplicationListener

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.