Package org.springframework.xd.module.core

Source Code of org.springframework.xd.module.core.CompositeModule

/*
* Copyright 2013 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.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
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.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.handler.BridgeHandler;
import org.springframework.messaging.MessageChannel;
import org.springframework.util.Assert;
import org.springframework.xd.module.ModuleDeploymentProperties;
import org.springframework.xd.module.ModuleDescriptor;
import org.springframework.xd.module.ModuleType;

/**
* @author Mark Fisher
* @author David Turanski
*/
public class CompositeModule extends AbstractModule {

  public static final String OPTION_SEPARATOR = ".";

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

  private final GenericApplicationContext context = new GenericApplicationContext();

  private final List<Module> modules;

  private final Properties properties = new Properties();

  private final AtomicInteger propertiesCounter = new AtomicInteger();

  private final AtomicBoolean isRunning = new AtomicBoolean();

  public CompositeModule(ModuleDescriptor descriptor, ModuleDeploymentProperties deploymentProperties,
      List<Module> modules) {
    super(descriptor, deploymentProperties);
    this.modules = modules;
    this.validate();
  }
  //TODO: This is specific to XD stream composition. Eventually we may want to support more generic composite modules.
  private void validate() {
    Assert.isTrue(modules != null && modules.size() > 0, "at least one module required");
    ModuleType inferredType = null;
    if (modules.size() == 1) {
      inferredType = modules.get(0).getType();
    }
    else {
      ModuleType firstType = modules.get(0).getType();
      ModuleType lastType = modules.get(modules.size() - 1).getType();
      boolean hasInput = firstType != ModuleType.source;
      boolean hasOutput = lastType != ModuleType.sink;
      if (hasInput && hasOutput) {
        inferredType = ModuleType.processor;
      }
      else if (hasInput) {
        inferredType = ModuleType.sink;
      }
      else if (hasOutput) {
        inferredType = ModuleType.source;
      }
    }
    Assert.isTrue(inferredType == this.getType(),
        "invalid composite module: inferred type=" + inferredType + ", stated type=" + this.getType());
  }

  @Override
  public void initialize() {
    List<AbstractEndpoint> endpoints = new ArrayList<AbstractEndpoint>();
    MessageChannel previousOutputChannel = null;
    for (int i = 0; i < this.modules.size(); i++) {
      Module module = this.modules.get(i);
      module.initialize();
      MessageChannel inputChannel = module.getComponent("input", MessageChannel.class);
      MessageChannel outputChannel = module.getComponent("output", MessageChannel.class);
      if (i == 0 && inputChannel != null) {
        // this will act as THE input for the composite module
        // if the first module has no input, the composite is a source
        this.context.getBeanFactory().registerSingleton("input", inputChannel);
      }
      if (i > 0) {
        // first module MAY have 'input', all others MUST
        Assert.notNull(inputChannel, "each module after the first must provide 'input'");
      }
      if (previousOutputChannel != null) {
        BridgeHandler handler = new BridgeHandler();
        handler.setBeanFactory(this.context.getBeanFactory());
        handler.setOutputChannel(inputChannel);
        handler.afterPropertiesSet();
        ConsumerEndpointFactoryBean bridgeFactoryBean = new ConsumerEndpointFactoryBean();
        bridgeFactoryBean.setInputChannel(previousOutputChannel);
        bridgeFactoryBean.setHandler(handler);
        try {
          // TODO: might not be necessary to pass this context, but the FB requires non-null
          bridgeFactoryBean.setBeanFactory(this.context.getBeanFactory());
          bridgeFactoryBean.afterPropertiesSet();
          AbstractEndpoint endpoint = bridgeFactoryBean.getObject();
          endpoints.add(endpoint);
          this.context.getBeanFactory().registerSingleton("bridge-" + i, endpoint);
          endpoint.setComponentName("bridge-" + i);
          endpoint.afterPropertiesSet();
        }
        catch (Exception e) {
          throw new IllegalStateException("failed to start bridge for CompositeModule", e);
        }
      }
      if (i < this.modules.size() - 1) {
        // last module MAY have 'output', all others MUST
        Assert.notNull(outputChannel, "each module before the last must provide 'output'");
      }
      previousOutputChannel = outputChannel;
      if (i == this.modules.size() - 1 && outputChannel != null) {
        // this will act as THE output for the composite module
        // if the final module has no outputChannel, the composite is a sink
        this.context.getBeanFactory().registerSingleton("output", outputChannel);
      }
    }
    for (int i = endpoints.size() - 1; i >= 0; i--) {
      endpoints.get(i).start();
    }
    initContext();
    if (logger.isInfoEnabled()) {
      logger.info("initialized module: " + this.toString());
    }
  }

  @Override
  public ConfigurableApplicationContext getApplicationContext() {
    return context;
  }

  private void initContext() {
    Assert.state(context != null, "An ApplicationContext is required");
    boolean propertyConfigurerPresent = false;
    for (String name : context.getBeanDefinitionNames()) {
      if (name.startsWith("org.springframework.context.support.PropertySourcesPlaceholderConfigurer")) {
        propertyConfigurerPresent = true;
        break;
      }
    }
    if (!propertyConfigurerPresent) {
      PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer();
      placeholderConfigurer.setEnvironment(context.getEnvironment());
      context.addBeanFactoryPostProcessor(placeholderConfigurer);
    }
    context.setId(this.toString());
    context.refresh();
  }

  @Override
  public void setParentContext(ApplicationContext parentContext) {
    this.context.setParent(parentContext);
  }

  @Override
  public void addListener(ApplicationListener<?> listener) {
    for (Module module : modules) {
      module.addListener(listener);
    }
  }

  @Override
  public void addSource(Object source) {
    Assert.notNull(source,"source cannot be null");
    Assert.isInstanceOf(Resource.class,source,"unsupported source: " + source.getClass().getName());
    Resource resource = (Resource)source;
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this.context);
    reader.loadBeanDefinitions(resource);
  }

  @Override
  public void addProperties(Properties properties) {
    this.registerPropertySource(properties);
    this.properties.putAll(properties);
    for (Module module : this.modules) {
      module.addProperties(properties);
    }
  }

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

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

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

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

  @Override
  public void destroy() {
    for (Module module : this.modules) {
      module.destroy();
    }
    if (this.context != null) {
      this.context.destroy();
    }
  }

  /*
   * Lifecycle implementation
   */

  @Override
  public void start() {
    Assert.state(this.context != null, "An ApplicationContext is required");
    if (this.isRunning.compareAndSet(false, true)) {
      for (int i = this.modules.size() - 1; i >= 0; i--) {
        Module module = this.modules.get(i);
        module.start();
      }
      try {
        this.context.start();
      }
      catch (BeansException be) {
        this.context.destroy();
        throw be;
      }
      if (logger.isInfoEnabled()) {
        logger.info("started module: " + this.toString());
      }
    }
  }

  @Override
  public void stop() {
    if (this.isRunning.compareAndSet(true, false)) {
      for (Module module : this.modules) {
        module.stop();
      }
      this.context.stop();
      if (logger.isInfoEnabled()) {
        logger.info("stopped module: " + this.toString());
      }
    }
  }

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


}
TOP

Related Classes of org.springframework.xd.module.core.CompositeModule

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.