/*
* 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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
/**
* Representation of a module in the context of a defined stream or job.
*
* @author Mark Fisher
* @author Gary Russell
* @author Luke Taylor
* @author Ilayaperumal Gopinathan
* @author Patrick Peralta
*/
public class ModuleDescriptor implements Comparable<ModuleDescriptor> {
/**
* Symbolic name of a module. This may be generated to a default
* value or specified in the DSL string.
*/
private final String moduleLabel;
/**
* Name of deployable unit this module instance belongs to (such as a stream or job).
*/
private final String group;
/**
* Name of source channel, if defined by the stream/job definition.
* May be {@code null}.
*/
private final String sourceChannelName;
/**
* Name of sink channel, if defined by the stream/job definition.
* May be {@code null}.
*/
private final String sinkChannelName;
/**
* Position in stream/job definition relative to the other modules
* in the definition. 0 indicates the first/leftmost position.
*/
private final int index;
/**
* Parameters for module. This is specific to the type of module - for
* instance an http module would include a port number as a parameter.
*/
private final Map<String, String> parameters;
/**
* If this is a composite module, this list contains the modules that
* this module consists of; otherwise this list is empty.
*/
private final List<ModuleDescriptor> children;
/**
* Provides the name and type of the module. Typically this module is present under
* {@code $XD_HOME/modules/[module_type]/[module_name]}.
* Also contains information required for module deployment (i.e. classpath).
*/
private final ModuleDefinition moduleDefinition;
/**
* Construct a {@code ModuleDescriptor}. This constructor is private; use
* {@link org.springframework.xd.module.ModuleDescriptor.Builder} to create a new instance.
*
* @param moduleLabel label used for module in stream/job definition
* @param group group this module belongs to (stream/job)
* @param sourceChannelName name of source channel; may be {@code null}
* @param sinkChannelName name of sink channel; may be {@code null}
* @param index position of module in stream/job definition
* @param moduleDefinition module definition
* @param parameters module parameters; may be {@code null}
* @param children if this is a composite module, this list contains the modules
* that comprise this module; may be {@code null}
*/
private ModuleDescriptor(String moduleLabel, String group,
String sourceChannelName, String sinkChannelName,
int index, ModuleDefinition moduleDefinition,
Map<String, String> parameters, List<ModuleDescriptor> children) {
Assert.notNull(moduleLabel, "moduleLabel must not be null");
Assert.notNull(group, "group must not be null");
this.moduleLabel = moduleLabel;
this.group = group;
this.sourceChannelName = sourceChannelName;
this.sinkChannelName = sinkChannelName;
this.index = index;
this.moduleDefinition = moduleDefinition;
this.parameters = parameters == null
? Collections.<String, String>emptyMap()
: Collections.unmodifiableMap(new HashMap<String, String>(parameters));
this.children = children == null
? Collections.<ModuleDescriptor>emptyList()
: Collections.unmodifiableList(new ArrayList<ModuleDescriptor>(children));
}
/**
* Return name of module. Typically this module is present under
* {@code $XD_HOME/modules/[module type]}.
*
* @return module name
*/
public String getModuleName() {
return moduleDefinition.getName();
}
/**
* Return symbolic name of a module. This may be explicitly specified in
* the DSL string, which is required if the stream contains another module
* with the same name. Otherwise, it will be the same as the module name.
*
* @return module label
*/
public String getModuleLabel() {
return moduleLabel;
}
/**
* Return name of deployable unit this module instance belongs to
* (such as a stream or job).
*
* @return group name
*/
public String getGroup() {
return group;
}
/**
* Return position in stream/job definition relative to the other
* modules in the definition. 0 indicates the first/leftmost position.
*
* @return module index
*/
public int getIndex() {
return index;
}
/**
* Return the module type.
*
* @return module type
*/
public ModuleType getType() {
return moduleDefinition.getType();
}
/**
* Return name of source channel, if defined by the stream/job definition.
* May be {@code null}.
*
* @return source channel name, or {@code null} if no source channel defined
*/
public String getSourceChannelName() {
return sourceChannelName;
}
/**
* Return name of sink channel, if defined by the stream/job definition.
* May be {@code null}.
*
* @return sink channel name, or {@code null} if no sink channel defined
*/
public String getSinkChannelName() {
return sinkChannelName;
}
/**
* Return parameters for module. This is specific to the type of module - for
* instance an http module would include a port number as a parameter.
*
* @return read-only map of module parameters
*/
public Map<String, String> getParameters() {
return this.parameters;
}
/**
* If this is a composite module, this list contains the modules that this
* module consists of; otherwise this list is empty.
*
* @return sub modules for this module, or empty list if this is not a composite module
*/
public List<ModuleDescriptor> getChildren() {
return children;
}
/**
* Return the {@link ModuleDefinition} for this module. {@code ModuleDefinition}
* contains information required to load the module (i.e. classpath).
*
* @return module definition for this module descriptor
*/
public ModuleDefinition getModuleDefinition() {
return moduleDefinition;
}
/**
* Return true if this is a composed module. A composed module consists
* of one or more child modules.
*
* @return true if this is a composed modules
* @see #children
*/
public boolean isComposed() {
return !children.isEmpty();
}
/**
* Create a new {@link Key} for this {@code ModuleDescriptor}.
* @return new {@code Key}
*/
public Key createKey() {
return new Key(getGroup(), getType(), getModuleLabel());
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return new ToStringCreator(this)
.append("moduleName", moduleDefinition != null ? moduleDefinition.getName() : null)
.append("moduleLabel", moduleLabel)
.append("group", group)
.append("sourceChannelName", sourceChannelName)
.append("sinkChannelName", sinkChannelName)
.append("index", index)
.append("type", moduleDefinition != null ? moduleDefinition.getType() : null)
.append("parameters", parameters)
.append("children", children).toString();
}
/**
* {@inheritDoc}
*/
@Override
public int compareTo(ModuleDescriptor o) {
Assert.notNull(o, "ModuleDescriptor must not be null");
return (index < o.index) ? -1 : ((index == o.index) ? 0 : 1);
}
/**
* Builder object for {@link org.springframework.xd.module.ModuleDescriptor}.
* This object is mutable to allow for flexibility in specifying module
* type/fields/parameters during parsing.
*/
public static class Builder {
/**
* @see org.springframework.xd.module.ModuleDescriptor#moduleName
*/
private String moduleName;
/**
* @see org.springframework.xd.module.ModuleDescriptor#moduleLabel
*/
private String moduleLabel;
/**
* @see org.springframework.xd.module.ModuleDescriptor#group
*/
private String group;
/**
* @see org.springframework.xd.module.ModuleDescriptor#sourceChannelName
*/
private String sourceChannelName;
/**
* @see org.springframework.xd.module.ModuleDescriptor#sinkChannelName
*/
private String sinkChannelName;
/**
* @see org.springframework.xd.module.ModuleDescriptor#index
*/
private int index;
/**
* @see org.springframework.xd.module.ModuleDescriptor#type
*/
private ModuleType type;
/**
* @see org.springframework.xd.module.ModuleDescriptor#parameters
*/
private final Map<String, String> parameters = new HashMap<String, String>();
/**
* @see org.springframework.xd.module.ModuleDescriptor#children
*/
private final List<ModuleDescriptor> children = new ArrayList<ModuleDescriptor>();
private ModuleDefinition moduleDefinition;
/**
* Set the module name.
*
* @param moduleName name of module
* @return this builder object
*
* @see ModuleDescriptor#getModuleName
*/
public Builder setModuleName(String moduleName) {
this.moduleName = moduleName;
return this;
}
/**
* Set the module label.
*
* @param moduleLabel name of module label
* @return this builder object
*
* @see org.springframework.xd.module.ModuleDescriptor#moduleLabel
*/
public Builder setModuleLabel(String moduleLabel) {
this.moduleLabel = moduleLabel;
return this;
}
/**
* Set the module group.
*
* @param group name of module group
* @return this builder object
*
* @see org.springframework.xd.module.ModuleDescriptor#group
*/
public Builder setGroup(String group) {
this.group = group;
return this;
}
/**
* Set the module source channel name.
*
* @param sourceChannelName name of source channel; may be {@code null}
* @return this builder object
*
* @see org.springframework.xd.module.ModuleDescriptor#sourceChannelName
*/
public Builder setSourceChannelName(String sourceChannelName) {
this.sourceChannelName = sourceChannelName;
return this;
}
/**
* Set the module sink channel name.
*
* @param sinkChannelName name of sink channel; may be {@code null}
* @return this builder object
*
* @see org.springframework.xd.module.ModuleDescriptor#sinkChannelName
*/
public Builder setSinkChannelName(String sinkChannelName) {
this.sinkChannelName = sinkChannelName;
return this;
}
/**
* Set the module index.
*
* @param index position of module in stream/job definition
* @return this builder object
*
* @see org.springframework.xd.module.ModuleDescriptor#index
*/
public Builder setIndex(int index) {
this.index = index;
return this;
}
/**
* Set the module type.
*
* @param type module type
* @return this builder object
*
* @see ModuleDescriptor#getType()
*/
public Builder setType(ModuleType type) {
this.type = type;
return this;
}
/**
* Add the list of children to the list of sub modules. This only applies if
* this builder is for a composite module.
*
* @param children sub modules
* @return this builder object
*
* @see org.springframework.xd.module.ModuleDescriptor#children
*/
public Builder addChildren(List<ModuleDescriptor> children) {
this.children.addAll(children);
return this;
}
/**
* Set a module parameter.
*
* @param name parameter name
* @param value parameter value
* @return this builder object
*
* @see org.springframework.xd.module.ModuleDescriptor#parameters
*/
public Builder setParameter(String name, String value) {
this.parameters.put(name, value);
return this;
}
/**
* Add the contents of the provided map to the map of module parameters.
*
* @param parameters module parameters
* @return this builder object
*
* @see org.springframework.xd.module.ModuleDescriptor#parameters
*/
public Builder addParameters(Map<String, String> parameters) {
this.parameters.putAll(parameters);
return this;
}
/**
* Return name of module. Typically this module is present under
* {@code $XD_HOME/modules/[module type]}.
*
* @return module name
*/
public String getModuleName() {
return moduleName;
}
/**
* Return symbolic name of a module. This may be explicitly specified in
* the DSL string, which is required if the stream contains another module
* with the same name. Otherwise, it will be the same as the module name.
*
* @return module label
*/
public String getModuleLabel() {
return moduleLabel;
}
/**
* Return name of deployable unit this module instance belongs to
* (such as a stream or job).
*
* @return group name
*/
public String getGroup() {
return group;
}
/**
* Return name of source channel, if defined by the stream/job definition.
* May be {@code null}.
*
* @return source channel name, or {@code null} if no source channel defined
*/
public String getSourceChannelName() {
return sourceChannelName;
}
/**
* Return name of sink channel, if defined by the stream/job definition.
* May be {@code null}.
*
* @return sink channel name, or {@code null} if no sink channel defined
*/
public String getSinkChannelName() {
return sinkChannelName;
}
/**
* Return position in stream/job definition relative to the other modules in
* the definition. 0 indicates the first/leftmost position.
*
* @return module index
*/
public int getIndex() {
return index;
}
/**
* Return the module type.
*
* @return module type
*/
public ModuleType getType() {
return type;
}
/**
* Return parameters for module. This is specific to the type of module -
* for instance an http module would include a port number as a parameter.
* <br />
* Note that the contents of this map are <b>mutable</b>.
*
* @return map of module parameters
*/
public Map<String, String> getParameters() {
return parameters;
}
/**
* Create a {@code Builder} object pre-populated with the configuration
* for the provided {@link org.springframework.xd.module.ModuleDescriptor}.
*
* @param descriptor module descriptor
* @return pre-populated builder object
*/
public static Builder fromModuleDescriptor(ModuleDescriptor descriptor) {
Builder builder = new Builder();
builder.setModuleName(descriptor.getModuleName());
builder.setModuleLabel(descriptor.getModuleLabel());
builder.setGroup(descriptor.getGroup());
builder.setSourceChannelName(descriptor.getSourceChannelName());
builder.setSinkChannelName(descriptor.getSinkChannelName());
builder.setIndex(descriptor.getIndex());
builder.setType(descriptor.getType());
builder.setModuleDefinition(descriptor.getModuleDefinition());
builder.addParameters(descriptor.getParameters());
builder.addChildren(descriptor.getChildren());
return builder;
}
/**
* Return a new instance of {@link org.springframework.xd.module.ModuleDescriptor}.
*
* @return new instance of {@code ModuleDescriptor}
*/
public ModuleDescriptor build() {
String label = moduleLabel != null ? moduleLabel : getModuleDefinition().getName();
return new ModuleDescriptor(label, group,
sourceChannelName, sinkChannelName,
index, getModuleDefinition(),
parameters, children);
}
public Builder setModuleDefinition(ModuleDefinition moduleDefinition) {
this.moduleDefinition = moduleDefinition;
return this;
}
public ModuleDefinition getModuleDefinition() {
if (moduleDefinition == null) {
moduleDefinition = new ModuleDefinition(moduleName, type) {
@Override
public boolean isComposed() {
return false;
}
};
}
return moduleDefinition;
}
}
/**
* Key to be used in Map of ModuleDescriptors.
*/
public static class Key implements Comparable<Key> {
/**
* Group name.
*/
private final String group;
/**
* Module type.
*/
private final ModuleType type;
/**
* Module label.
*/
private final String label;
/**
* Construct a key.
*
* @param group group name
* @param type module type
* @param label module label
*/
public Key(String group, ModuleType type, String label) {
Assert.notNull(group, "Group is required");
Assert.notNull(type, "Type is required");
Assert.hasText(label, "Label is required");
this.group = group;
this.type = type;
this.label = label;
}
/**
* Return the name of the stream.
*
* @return stream name
*/
public String getGroup() {
return group;
}
/**
* Return the module type.
*
* @return module type
*/
public ModuleType getType() {
return type;
}
/**
* Return the module label.
*
* @return module label
*/
public String getLabel() {
return label;
}
/**
* {@inheritDoc}
*/
@Override
public int compareTo(Key other) {
int c = type.compareTo(other.getType());
if (c == 0) {
c = label.compareTo(other.getLabel());
}
if (c == 0) {
c = group.compareTo(other.getGroup());
}
return c;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof Key) {
Key other = (Key) o;
return group.equals(other.getGroup())
&& type.equals(other.getType()) && label.equals(other.getLabel());
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
int result = group.hashCode();
result = 31 * result + type.hashCode();
result = 31 * result + label.hashCode();
return result;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "ModuleDeploymentKey{" +
"stream='" + group + '\'' +
", type=" + type +
", label='" + label + '\'' +
'}';
}
}
}