Package org.springframework.xd.dirt.stream

Source Code of org.springframework.xd.dirt.stream.XDStreamParser

/*
* 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.dirt.stream;


import static org.springframework.xd.dirt.stream.dsl.XDDSLMessages.*;

import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.springframework.data.repository.CrudRepository;
import org.springframework.util.Assert;
import org.springframework.validation.BindException;
import org.springframework.xd.dirt.core.BaseDefinition;
import org.springframework.xd.dirt.module.ModuleRegistry;
import org.springframework.xd.dirt.module.NoSuchModuleException;
import org.springframework.xd.dirt.plugins.ModuleConfigurationException;
import org.springframework.xd.dirt.stream.ParsingContext.Position;
import org.springframework.xd.dirt.stream.dsl.ArgumentNode;
import org.springframework.xd.dirt.stream.dsl.ModuleNode;
import org.springframework.xd.dirt.stream.dsl.SinkChannelNode;
import org.springframework.xd.dirt.stream.dsl.SourceChannelNode;
import org.springframework.xd.dirt.stream.dsl.StreamConfigParser;
import org.springframework.xd.dirt.stream.dsl.StreamDefinitionException;
import org.springframework.xd.dirt.stream.dsl.StreamNode;
import org.springframework.xd.module.CompositeModuleDefinition;
import org.springframework.xd.module.ModuleDefinition;
import org.springframework.xd.module.ModuleDescriptor;
import org.springframework.xd.module.ModuleType;
import org.springframework.xd.module.core.CompositeModule;
import org.springframework.xd.module.options.ModuleOptionsMetadata;
import org.springframework.xd.module.options.ModuleOptionsMetadataResolver;

/**
* Parser to convert a DSL string for a stream into a list of
* {@link org.springframework.xd.module.ModuleDescriptor}
* objects that comprise the given stream.
*
* @author Andy Clement
* @author Gunnar Hillert
* @author Glenn Renfro
* @author Mark Fisher
* @author Patrick Peralta
* @author Eric Bottard
* @since 1.0
*/
public class XDStreamParser implements XDParser {

  /**
   * Repository for user defined modules.
   */
  private final ModuleRegistry moduleRegistry;

  /**
   * Resolver for module options metadata.
   */
  private final ModuleOptionsMetadataResolver moduleOptionsMetadataResolver;

  /**
   * Optional definition repository used to obtain sub-stream/label
   * references.
   *
   * @see org.springframework.xd.dirt.stream.dsl.StreamConfigParser
   */
  private CrudRepository<? extends BaseDefinition, String> repository;


  /**
   * Construct an {@code XDStreamParser}.
   *
   * @param repository                     repository for stream definitions (optional)
   * @param moduleRegistry                 registry for modules
   * @param moduleOptionsMetadataResolver  resolver for module options metadata
   */
  public XDStreamParser(CrudRepository<? extends BaseDefinition, String> repository,
      ModuleRegistry moduleRegistry,
      ModuleOptionsMetadataResolver moduleOptionsMetadataResolver) {
    Assert.notNull(moduleRegistry, "moduleRegistry can not be null");
    Assert.notNull(moduleOptionsMetadataResolver, "moduleOptionsMetadataResolver can not be null");
    this.repository = repository;
    this.moduleRegistry = moduleRegistry;
    this.moduleOptionsMetadataResolver = moduleOptionsMetadataResolver;
  }

  /**
   * Construct an {@code XDStreamParser}.
   *
   * @param moduleRegistry                 registry for modules
   * @param moduleOptionsMetadataResolver  resolver for module options metadata
   */
  public XDStreamParser(ModuleRegistry moduleRegistry,
      ModuleOptionsMetadataResolver moduleOptionsMetadataResolver) {
    this(null, moduleRegistry, moduleOptionsMetadataResolver);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List<ModuleDescriptor> parse(String name, String config, ParsingContext parsingContext) {

    StreamConfigParser parser = new StreamConfigParser(repository);
    StreamNode stream = parser.parse(name, config);
    Deque<ModuleDescriptor.Builder> builders = new LinkedList<ModuleDescriptor.Builder>();

    List<ModuleNode> moduleNodes = stream.getModuleNodes();
    for (int m = moduleNodes.size() - 1; m >= 0; m--) {
      ModuleNode moduleNode = moduleNodes.get(m);
      ModuleDescriptor.Builder builder =
          new ModuleDescriptor.Builder()
              .setGroup(name)
              .setModuleName(moduleNode.getName())
              .setModuleLabel(moduleNode.getLabelName())
              .setIndex(m);
      if (moduleNode.hasArguments()) {
        ArgumentNode[] arguments = moduleNode.getArguments();
        for (ArgumentNode argument : arguments) {
          builder.setParameter(argument.getName(), argument.getValue());
        }
      }
      builders.add(builder);
    }

    SourceChannelNode sourceChannel = stream.getSourceChannelNode();
    if (sourceChannel != null) {
      if (parsingContext.supportsNamedChannels()) {
        builders.getLast().setSourceChannelName(sourceChannel.getChannelName());
      }
      else {
        throw new StreamDefinitionException(config, sourceChannel.getStartPos(),
            NAMED_CHANNELS_UNSUPPORTED_HERE);
      }
    }

    SinkChannelNode sinkChannel = stream.getSinkChannelNode();
    if (sinkChannel != null) {
      if (parsingContext.supportsNamedChannels()) {
        builders.getFirst().setSinkChannelName(sinkChannel.getChannelName());
      }
      else {
        throw new StreamDefinitionException(config, sinkChannel.getChannelNode().getStartPos(),
            NAMED_CHANNELS_UNSUPPORTED_HERE);
      }
    }

    // Now that we know about source and sink channel names,
    // do a second pass to determine type. Also convert to composites.
    // And while we're at it (and type is known), validate module name and options
    List<ModuleDescriptor> result = new ArrayList<ModuleDescriptor>(builders.size());
    for (ModuleDescriptor.Builder builder : builders) {
      builder.setType(determineType(builder, builders.size() - 1, parsingContext));

      // definition is guaranteed to be non-null here
      ModuleDefinition moduleDefinition = moduleRegistry.findDefinition(builder.getModuleName(), builder.getType());
      builder.setModuleDefinition(moduleDefinition);
      ModuleOptionsMetadata optionsMetadata = moduleOptionsMetadataResolver.resolve(moduleDefinition);
      if (parsingContext.shouldBindAndValidate()) {
        try {
          optionsMetadata.interpolate(builder.getParameters());
        }
        catch (BindException e) {
          throw ModuleConfigurationException.fromBindException(builder.getModuleName(),
              builder.getType(), e);
        }
      }

      result.add(buildModuleDescriptor(builder));
    }
    return result;
  }

  /**
   * For a given module builder, determine the type of module based on:
   * <ol>
   *     <li>module name</li>
   *     <li>module position in the stream</li>
   *     <li>presence of (or lack thereof) named channels</li>
   * </ol>
   *
   * @param builder          builder object
   * @param lastIndex        index of last module in the stream
   * @param parsingContext   parsing context
   * @return module type
   * @throws NoSuchModuleException if the module type does not exist
   */
  private ModuleType determineType(ModuleDescriptor.Builder builder, int lastIndex, ParsingContext parsingContext) {
    ModuleType moduleType = determineTypeFromNamedChannels(builder, lastIndex, parsingContext);
    if (moduleType != null) {
      return moduleType;
    }
    String name = builder.getModuleName();
    int index = builder.getIndex();

    return resolveModuleType(name, parsingContext.allowed(Position.of(index, lastIndex)));
  }

  /**
   * Attempt to guess the type of a module given the presence of named channels
   * references at the start or end of the stream definition.
   *
   * @return module type, or null if no named channels were present
   */
  private ModuleType determineTypeFromNamedChannels(ModuleDescriptor.Builder builder, int lastIndex,
      ParsingContext parsingContext) {
    // Should this fail for composed module too?
    if (parsingContext == ParsingContext.job
        && (builder.getSourceChannelName() != null || builder.getSinkChannelName() != null)) {
      throw new RuntimeException("TODO");
    }
    ModuleType type = null;
    String moduleName = builder.getModuleName();
    int index = builder.getIndex();
    if (builder.getSourceChannelName() != null) { // preceded by >, so not a source
      if (index == lastIndex) { // this is the final module of the stream
        if (builder.getSinkChannelName() != null) { // but followed by >, so not a sink
          type = ModuleType.processor;
        }
        else { // final module and no >, so IS a sink
          type = ModuleType.sink;
        }
      }
      else { // not final module, must be a processor
        type = ModuleType.processor;
      }
    }
    else if (builder.getSinkChannelName() != null) { // followed by >, so not a sink
      if (index == 0) { // first module in a stream, and not preceded by >, so IS a source
        type = ModuleType.source;
      }
      else { // not first module, and followed by >, so not a source or sink
        type = ModuleType.processor;
      }
    }
    return (type == null) ? null : resolveModuleType(moduleName, type);
  }

  /**
   * Return a {@link org.springframework.xd.module.ModuleDescriptor}
   * per the specifications indicated by the provided builder. If the module
   * is a composite module, the children modules are also built and included
   * under {@link org.springframework.xd.module.ModuleDescriptor#getChildren()}.
   *
   * @param builder builder object
   * @return new instance of {@code ModuleDescriptor}
   */
  private ModuleDescriptor buildModuleDescriptor(ModuleDescriptor.Builder builder) {
    ModuleDefinition def = moduleRegistry.findDefinition(builder.getModuleName(), builder.getType());
    if (def.isComposed()) {
      String dsl = ((CompositeModuleDefinition) def).getDslDefinition();
      List<ModuleDescriptor> children = parse(def.getName(), dsl, ParsingContext.module);

      // Preserve the options set for the "parent" module in the parameters map
      Map<String, String> parameters = new HashMap<String, String>(builder.getParameters());

      // Pretend that options were set on the composed module itself
      // (eases resolution wrt defaults later)
      for (ModuleDescriptor child : children) {
        for (String key : child.getParameters().keySet()) {
          String prefix = child.getModuleName() + CompositeModule.OPTION_SEPARATOR;
          builder.setParameter(prefix + key, child.getParameters().get(key));
        }
      }

      // This is to copy options from parent to this (which may override
      // what was set above)
      for (Map.Entry<String, String> entry : parameters.entrySet()) {
        builder.setParameter(entry.getKey(), entry.getValue());
      }

      // Since ModuleDescriptor is immutable, the children created
      // by the parse method above have to be recreated since the group
      // name needs to be modified
      List<ModuleDescriptor> list = new ArrayList<ModuleDescriptor>();
      for (ModuleDescriptor child : children) {
        ModuleDescriptor.Builder childBuilder =
            ModuleDescriptor.Builder.fromModuleDescriptor(child);
        childBuilder.setGroup(builder.getGroup() + "." + child.getModuleName());
        list.add(childBuilder.build());
      }

      builder.addChildren(list);
    }
    return builder.build();
  }

  /**
   * Return the module type for the module name. Based on the position
   * in the stream definition, the module <b>must</b> be one of the
   * types passed into {@code candidates}.
   *
   * @param moduleName name of module
   * @param candidates the list of module types the module can be
   * @return the module type for the module name
   * @throws NoSuchModuleException if no module with this name exists for one of
   *                               the types present in {@code candidates}
   */
  private ModuleType resolveModuleType(String moduleName, ModuleType... candidates) {
    for (ModuleType type : candidates) {
      ModuleDefinition def = moduleRegistry.findDefinition(moduleName, type);
      if (def != null) {
        return type;
      }
    }
    throw new NoSuchModuleException(moduleName, candidates);
  }

}
TOP

Related Classes of org.springframework.xd.dirt.stream.XDStreamParser

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.