-aSimplified flow configuration and enhanced documentation mechanism

This commit is contained in:
David Turanski
2011-07-08 12:44:40 -04:00
parent 8811bb397c
commit c392e19887
33 changed files with 255 additions and 577 deletions

View File

@@ -4,6 +4,7 @@
<classpathentry kind="src" output="target/classes" path="src/main/resources"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
<classpathentry kind="src" path="target/generated-sources/xjc"/>
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>

View File

@@ -26,8 +26,14 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.springframework.ide.eclipse.core.springbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.springframework.ide.eclipse.core.springnature</nature>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>

View File

@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<beansProjectDescription>
<version>1</version>
<pluginVersion><![CDATA[2.7.0.201106102322-M2]]></pluginVersion>
<pluginVersion><![CDATA[2.7.0.201107040453-RELEASE]]></pluginVersion>
<configSuffixes>
<configSuffix><![CDATA[xml]]></configSuffix>
</configSuffixes>
<enableImports><![CDATA[false]]></enableImports>
<configs>
<config>src/test/resources/META-INF/spring/integration/flows/subflow1-context.xml</config>
<config>src/test/resources/FlowConfigNamespaceTest-context.xml</config>
<config>src/test/resources/FlowClientNamespaceTest-context.xml</config>
<config>src/test/resources/ref-bean-config.xml</config>
<config>src/test/resources/META-INF/spring/integration/flows/subflow1/subflow1-context.xml</config>
</configs>
<configSets>
</configSets>

22
README
View File

@@ -5,7 +5,7 @@ A flow is a Spring Integration message flow intended for reuse. A flow is access
to internal channels.
A flow may expose multiple inputs and multiple outputs. A port mapping is defined for each input and has multiple
outputs associated with it. See src/test/resources/META-INF/spring/integration/flows/subflow1-context.xml, for example.
outputs associated with it. See src/test/resources/META-INF/spring/integration/flows/subflow1/subflow1-context.xml, for example.
Generally, a message flow may behave like a router. For example, a flow may define
a primary output and a discard output. Additionally, it may act like a delayer, providing no immediate response. Or it
@@ -14,9 +14,8 @@ could act as an outbound channel adapter, providing no output.
The goal is to support these, and possibly other semantics. Additional goals are:
- Encapsulation: the flow channels, and component should not be included the consumer application context. The consumer need
not know how the flow is configured (i.e, it is contained in a jar).
- Configuration: The flow may define optional or required properties to be provided by the consumer. The flow may define optional
or required bean definitions provided by the consumer (e.g, a generic XML processor may require an OXM marshaller)
- The flow should be self describing, in terms of ports, properties, and beans it exposes
- Configuration: The flow may reference properties or beans to be provided by the consumer, e.g, a generic XML processor may require an OXM marshaller.
- The flow should provide a description, in terms of ports, properties, and beans it exposes
- It should be easy to implement or wrap an existing configuration as a flow and provide a better option than simple importing
a spring configuration file and sending a message to one of its input channels
@@ -27,6 +26,15 @@ The flow consumer instantiates a flow and defines one or more flow outbound-gate
<int-flow:flow id="subflow1"/>
The flow also supports a properties attribute and a referenced-bean-locations attribute used to inject properties and a list of configuration files respectively.
Additionally any bean definition may be or property in the parent context may be inherited by the flow.
An optional flow-id may be used to resolve the flow configuration. By default the id attribute is used as the flow-id. The flow-id is useful if there are multiple
instances of the same flow, each configured differently. Each instance would have a unique id and the same flow-id.
An optional help attribute will output the flow's documentation to the STDOUT if set to 'true'.
<int-flow:outbound-gateway flow="subflow1" input-channel="another-input"
output-channel="another-output" input-port="gateway-input"/>
@@ -35,17 +43,17 @@ ports. The output port name is contained in the response header 'flow.output.por
The input-port attribute is optional -- if the flow defines a single input port it will be mapped by default.
The flow also supports a properties attribute and a referenced-bean-locations attribute used to inject properties and a list of configuration files respectively.
Additionally any bean definition may be or property in the parent context may be inherited by the flow.
Flow Implementation
-------------------
The flow element creates a Flow instance (eventually, configured with properties, referenced beans, etc.) The flow id is used to derive the flow's
spring bean definition file by convention (META-INF/spring/flows/[flow-id]-context.xml). This bean definition file and any referenced bean locations
spring bean definition file by convention (classpath:META-INF/spring/flows/[flow-id]/*.xml). This bean definition file and any referenced bean locations
will be used to create a child application context. The flow context must provide a FlowConfiguration containing the metadata describing the input ports,
output ports, and referenced beans and properties.
The flow implementer should also create a text file classpath:META-INF/spring/flows/[flow-id]/flow.doc which will contain the 'help' contents.
Currently, all defined outputs are bridged to a PublishSubscribeChannel which acts as a single 'flowOutputChannel'.
Each flow outbound-gateway instance is backed by a FlowMessageHandler that bridges the 'flow output channel' to its own QueueChannel. This emulates
a JMS topic. Each flow message handler sends the request message to the flow input channel corresponding to the input port and checks

33
pom.xml
View File

@@ -9,7 +9,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-flow</artifactId>
<version>2.0.5.BUILD-SNAPSHOT</version>
<version>2.1.0.BUILD-SNAPSHOT</version>
<name>Spring Integration Flow Support</name>
<licenses>
<license>
@@ -108,10 +108,36 @@
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-xml</artifactId>
<version>${spring.integration.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>xmlunit</groupId>
<artifactId>xmlunit</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugins>
<!-- Generate JAXB classes from schema into target -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
@@ -121,7 +147,8 @@
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>

View File

@@ -19,32 +19,27 @@
package org.springframework.integration.flow;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ChannelNamePortConfiguration extends NamedResourceConfiguration implements FlowProviderPortConfiguration {
public class ChannelNamePortConfiguration implements PortConfiguration {
private PortMetadata inputPortMetadata;
private List<PortMetadata> outputPortMetadataList;
public ChannelNamePortConfiguration(PortMetadata inputPortMetadata, List<PortMetadata> outputPortMetadataList) {
super(outputPortMetadataList);
this.outputPortMetadataList = outputPortMetadataList;
this.inputPortMetadata = inputPortMetadata;
}
@Override
public String getInputPortName() {
return this.inputPortMetadata.getName();
return this.inputPortMetadata.getPortName();
}
@Override
public String getInputChannel() {
return this.inputPortMetadata.getChannelName();
}
@Override
public String getInputPortDescription() {
return this.inputPortMetadata.getDescription();
}
@Override
@@ -59,9 +54,8 @@ public class ChannelNamePortConfiguration extends NamedResourceConfiguration imp
@Override
public List<String> getOutputPortNames() {
List<String> results = new ArrayList<String>();
for (NamedResourceMetadata resourceMetadata : getConfiguredResources()) {
PortMetadata portMetadata = (PortMetadata) resourceMetadata;
results.add(portMetadata.getName());
for (PortMetadata portMetadata : outputPortMetadataList ) {
results.add(portMetadata.getPortName());
}
/**
* consistent with ClientPortConfiguration impl.
@@ -73,12 +67,14 @@ public class ChannelNamePortConfiguration extends NamedResourceConfiguration imp
return results;
}
/* (non-Javadoc)
* @see org.springframework.integration.flow.FlowProviderPortConfiguration#getOutputPortMetadata()
*/
@Override
public List<NamedResourceMetadata> getOutputPortMetadata() {
return Collections.unmodifiableList(super.getConfiguredResources());
public PortMetadata find(String portName){
for (PortMetadata portMetadata : outputPortMetadataList ) {
if (portName.equals(portMetadata.getPortName())){
return portMetadata;
}
}
return null;
}
}

View File

@@ -49,7 +49,9 @@ public class Flow implements InitializingBean, BeanNameAware, ChannelResolver, A
private volatile Properties flowProperties;
private volatile String name;
private volatile String beanName;
private volatile String flowId;
private volatile ChannelResolver flowChannelResolver;
@@ -67,10 +69,18 @@ public class Flow implements InitializingBean, BeanNameAware, ChannelResolver, A
@Override
public void afterPropertiesSet() {
if (this.flowId == null){
this.flowId = this.beanName;
}
if (this.help) {
System.out.println(FlowUtils.getDocumentation(this.flowId));
}
if (configLocations == null) {
configLocations = new String[] { String.format(
"classpath*:META-INF/spring/integration/flows/%s-context.xml", this.name) };
"classpath:META-INF/spring/integration/flows/%s/*.xml", this.flowId) };
}
if (referencedBeanLocations != null) {
@@ -87,9 +97,7 @@ public class Flow implements InitializingBean, BeanNameAware, ChannelResolver, A
this.flowConfiguration = flowContext.getBean(FlowConfiguration.class);
Assert.notNull(flowConfiguration, "flow context does not contain a flow configuration");
if (this.help) {
System.out.println(displayFlowConfiguration());
}
validatePortMapping();
@@ -109,15 +117,19 @@ public class Flow implements InitializingBean, BeanNameAware, ChannelResolver, A
@Override
public void setBeanName(String name) {
this.name = name;
this.beanName = name;
}
public String getBeanName() {
return this.name;
return this.beanName;
}
public void setReferencedBeanLocations(String[] referencedBeanLocations) {
public void setFlowId(String flowId) {
this.flowId = flowId;
}
public void setReferencedBeanLocations(String[] referencedBeanLocations) {
this.referencedBeanLocations = referencedBeanLocations;
}
@@ -142,58 +154,12 @@ public class Flow implements InitializingBean, BeanNameAware, ChannelResolver, A
return flowChannelResolver.resolveChannelName(channelName);
}
/**
*
*/
private String displayFlowConfiguration() {
StringBuilder sb = new StringBuilder();
sb.append("\nFlow configuration for [").append(this.getBeanName()).append("]:\n");
sb.append("Port configuration:\n");
for (FlowProviderPortConfiguration portConfiguration : this.getFlowConfiguration().getPortConfigurations()) {
sb.append("\tinput port:").append(portConfiguration.getInputPortName()).append("\n\n\t")
.append(portConfiguration.getInputPortDescription()).append("\n\n").append("\toutput ports:\n");
for (NamedResourceMetadata metadata : portConfiguration.getOutputPortMetadata()) {
sb.append("\t\t").append(metadata.getName()).append("\t")
.append(metadata.getDescription()).append("\n");
}
NamedResourceConfiguration referencedBeansConfig = this.getFlowConfiguration()
.getReferencedBeansConfiguration();
if (referencedBeansConfig != null && !referencedBeansConfig.getConfiguredResources().isEmpty()) {
sb.append("\nReferenced beans:\n");
for (NamedResourceMetadata metadata : referencedBeansConfig.getConfiguredResources()) {
sb.append("\t").append(metadata).append("\n");
}
}
NamedResourceConfiguration propertiesConfig = this.getFlowConfiguration().getPropertiesConfiguration();
if (propertiesConfig != null && !propertiesConfig.getConfiguredResources().isEmpty()) {
sb.append("\nFlow properties:\n");
for (NamedResourceMetadata metadata : propertiesConfig.getConfiguredResources()) {
sb.append("\t").append(metadata).append("\n");
}
}
}
return sb.toString();
}
private void addReferencedProperties() {
if (flowProperties != null) {
PropertySource<?> propertySource = new PropertiesPropertySource("flowProperties", flowProperties);
NamedResourceConfiguration propertiesConfiguration = this.getFlowConfiguration()
.getPropertiesConfiguration();
if (propertiesConfiguration != null) {
for (NamedResourceMetadata resource : propertiesConfiguration.getRequiredResources()) {
Assert.isTrue(propertySource.containsProperty(resource.getName()), "Flow [" + this.name
+ "] is missing required property [" + resource.getName() + "]");
}
}
MutablePropertySources propertySources = flowContext.getEnvironment().getPropertySources();
propertySources.addLast(propertySource);
@@ -212,7 +178,7 @@ public class Flow implements InitializingBean, BeanNameAware, ChannelResolver, A
/*
* create a bridge for each target output port to the flow outputChannel
*/
for (FlowProviderPortConfiguration targetPortConfiguration : this.getFlowConfiguration()
for (PortConfiguration targetPortConfiguration : this.getFlowConfiguration()
.getPortConfigurations()) {
for (String outputPort : targetPortConfiguration.getOutputPortNames()) {
String targetOutputChannelName = (String) targetPortConfiguration.getOutputChannel(outputPort);

View File

@@ -24,47 +24,35 @@ import java.util.List;
*/
public class FlowConfiguration {
private final List<FlowProviderPortConfiguration> portConfigurations;
private volatile NamedResourceConfiguration propertiesConfiguration;
private volatile NamedResourceConfiguration referencedBeansConfiguration;
public FlowConfiguration(List<FlowProviderPortConfiguration> portConfigurations) {
private final List<PortConfiguration> portConfigurations;
/**
*
* @param portConfigurations
*/
public FlowConfiguration(List<PortConfiguration> portConfigurations) {
this.portConfigurations = portConfigurations;
}
public FlowProviderPortConfiguration getConfigurationForInputPort(String inputPortName) {
for (FlowProviderPortConfiguration pc : portConfigurations) {
/**
*
* @param inputPortName
* @return
*/
public PortConfiguration getConfigurationForInputPort(String inputPortName) {
for (PortConfiguration pc : portConfigurations) {
if (pc.getInputPortName().equals(inputPortName)) {
return pc;
}
}
return null;
}
public void setPropertiesConfiguration(NamedResourceConfiguration propertiesConfiguration) {
this.propertiesConfiguration = propertiesConfiguration;
}
public void setReferenceedBeansConfiguration(NamedResourceConfiguration referencedBeansConfiguration) {
this.setReferencedBeansConfiguration(referencedBeansConfiguration);
}
public List<FlowProviderPortConfiguration> getPortConfigurations() {
/**
*
* @return
*/
public List<PortConfiguration> getPortConfigurations() {
return portConfigurations;
}
public NamedResourceConfiguration getPropertiesConfiguration() {
return propertiesConfiguration;
}
public void setReferencedBeansConfiguration(NamedResourceConfiguration referencedBeansConfiguration) {
this.referencedBeansConfiguration = referencedBeansConfiguration;
}
public NamedResourceConfiguration getReferencedBeansConfiguration() {
return referencedBeansConfiguration;
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright 2002-2011 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.integration.flow;
import java.util.List;
/**
*
* @author David Turanski
*
*/
public interface FlowProviderPortConfiguration extends PortConfiguration {
public abstract String getInputPortDescription();
public abstract List<NamedResourceMetadata> getOutputPortMetadata();
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright 2002-2011 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.integration.flow;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
*
* @author David Turanski
*
*/
public class NamedResourceConfiguration {
private final List<? extends NamedResourceMetadata> resourceMetadata;
public NamedResourceConfiguration(List<? extends NamedResourceMetadata> resourceMetadata) {
this.resourceMetadata = resourceMetadata;
}
public boolean isRequired(String namedResource) {
for (NamedResourceMetadata metadata : resourceMetadata) {
if (metadata.getName().equals(namedResource)) {
return metadata.isRequired();
}
}
return false;
}
public List<NamedResourceMetadata> getConfiguredResources() {
return Collections.unmodifiableList(resourceMetadata);
}
public List<NamedResourceMetadata> getRequiredResources() {
List<NamedResourceMetadata> requiredResources = new ArrayList<NamedResourceMetadata>();
for (NamedResourceMetadata metadata : resourceMetadata) {
if (metadata.isRequired()) {
requiredResources.add(metadata);
}
}
return requiredResources;
}
public NamedResourceMetadata find(String name) {
for (NamedResourceMetadata metadata : resourceMetadata) {
if (metadata.getName().equals(name)) {
return metadata;
}
}
return null;
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright 2002-2011 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.integration.flow;
import org.springframework.util.Assert;
/**
*
* @author David Turanski
*
*/
public class NamedResourceMetadata {
private final String name;
private final String description;
private final boolean required;
public NamedResourceMetadata(String name, String description, boolean required) {
Assert.hasText(name, "name is required");
this.name = name;
this.description = description;
this.required = required;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public boolean isRequired() {
return required;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getName()).append("\t")
.append(required? "required" : "optional")
.append("\t")
.append(getDescription());
return sb.toString();
}
}

View File

@@ -30,5 +30,5 @@ public interface PortConfiguration {
public Collection<String> getOutputPortNames();
public Object getOutputChannel(String portName);
// TODO: Do we need error channel?
}

View File

@@ -20,20 +20,25 @@ package org.springframework.integration.flow;
* @author David Turanski
*
*/
public class PortMetadata extends NamedResourceMetadata {
public class PortMetadata {
private final String channelName;
private final String portName;
public PortMetadata(String portName, String channelName) {
this(portName, "", channelName);
this.portName = portName;
this.channelName = channelName;
}
public PortMetadata(String portName, String description, String channelName) {
super(portName, description, true);
this.channelName = channelName;
}
public String getChannelName() {
return channelName;
}
public String getChannelName() {
return channelName;
}
public String getPortName() {
return portName;
}
}

View File

@@ -23,7 +23,7 @@ import org.springframework.integration.config.AbstractSimpleMessageHandlerFactor
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.flow.Flow;
import org.springframework.integration.flow.FlowProviderPortConfiguration;
import org.springframework.integration.flow.PortConfiguration;
import org.springframework.integration.flow.handler.FlowMessageHandler;
import org.springframework.util.Assert;
@@ -45,7 +45,7 @@ public class FlowMessageHandlerFactoryBean extends AbstractSimpleMessageHandlerF
private volatile PollableChannel flowReceiveChannel;
private volatile FlowProviderPortConfiguration flowConfiguration;
private volatile PortConfiguration flowConfiguration;
@Override
protected MessageHandler createHandler() {

View File

@@ -12,6 +12,12 @@
*/
package org.springframework.integration.flow.config;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
@@ -19,41 +25,71 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.SubscribableChannel;
import org.springframework.integration.handler.BridgeHandler;
import org.springframework.util.ResourceUtils;
/**
* @author David Turanski
*
*
*/
public class FlowUtils {
/**
* Create a bridge
* @param inputChannel
* @param outputChannel
*/
public static void bridgeChannels(SubscribableChannel inputChannel, MessageChannel outputChannel) {
BridgeHandler bridgeHandler = new BridgeHandler();
bridgeHandler.setOutputChannel(outputChannel);
inputChannel.subscribe(bridgeHandler);
/**
* Create a bridge
*
* @param inputChannel
* @param outputChannel
*/
public static void bridgeChannels(SubscribableChannel inputChannel, MessageChannel outputChannel) {
BridgeHandler bridgeHandler = new BridgeHandler();
bridgeHandler.setOutputChannel(outputChannel);
inputChannel.subscribe(bridgeHandler);
}
/**
* Register a bean with "flow" prefix
* @param beanDefinition
* @param registry
* @return
*/
public static String registerBeanDefinition(BeanDefinition beanDefinition, BeanDefinitionRegistry registry){
String beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, registry);
beanName = "flow."+ beanName;
String strIndex = StringUtils.substringAfter(beanName,"#");
int index = Integer.valueOf(strIndex);
while (registry.isBeanNameInUse(beanName)){
index++;
beanName = beanName.replaceAll("#\\d$","#"+ (index));
}
registry.registerBeanDefinition(beanName, beanDefinition);
return beanName;
}
/**
* Register a bean with "flow" prefix
*
* @param beanDefinition
* @param registry
* @return
*/
public static String registerBeanDefinition(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) {
String beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, registry);
beanName = "flow." + beanName;
String strIndex = StringUtils.substringAfter(beanName, "#");
int index = Integer.valueOf(strIndex);
while (registry.isBeanNameInUse(beanName)) {
index++;
beanName = beanName.replaceAll("#\\d$", "#" + (index));
}
registry.registerBeanDefinition(beanName, beanDefinition);
return beanName;
}
public static String getDocumentation(String flowName) {
String path = String.format("classpath:META-INF/spring/integration/flows/%s/flow.doc", flowName);
try {
File file = ResourceUtils.getFile(path);
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
StringBuilder result = new StringBuilder();
while ((line = br.readLine()) != null) {
result.append(line).append("\n");
}
br.close();
return result.toString();
} catch (FileNotFoundException e) {
return "no help available";
} catch (IOException e) {
e.printStackTrace();
return "no help available";
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.flow.config.xml;
import java.util.List;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
@@ -24,8 +25,6 @@ import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.flow.ChannelNamePortConfiguration;
import org.springframework.integration.flow.FlowConfiguration;
import org.springframework.integration.flow.NamedResourceConfiguration;
import org.springframework.integration.flow.NamedResourceMetadata;
import org.springframework.integration.flow.PortMetadata;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
@@ -57,19 +56,8 @@ public class FlowConfigurationParser implements BeanDefinitionParser {
flowConfigurationBuilder.addConstructorArgValue(portConfigList);
BeanDefinition referencedProperties = this.buildNamedResourceConfiguration(element, "referenced-property");
flowConfigurationBuilder.addPropertyValue("propertiesConfiguration", referencedProperties);
BeanDefinition referencedBeans = this.buildNamedResourceConfiguration(element, "referenced-bean");
flowConfigurationBuilder.addPropertyValue("referencedBeansConfiguration", referencedBeans);
BeanDefinitionReaderUtils.registerWithGeneratedName(flowConfigurationBuilder.getBeanDefinition(),
parserContext.getRegistry());
parserContext.getRegistry());
return null;
}
@@ -101,38 +89,10 @@ public class FlowConfigurationParser implements BeanDefinitionParser {
return portConfigurationBuilder.getBeanDefinition();
}
private BeanDefinition buildNamedResourceConfiguration(Element parent, String elementName) {
ManagedList<Object> namedResourceList = new ManagedList<Object>();
List<Element> namedResources = DomUtils.getChildElementsByTagName(parent, elementName);
for (Element el : namedResources ) {
BeanDefinitionBuilder namedResourceBuilder = BeanDefinitionBuilder.genericBeanDefinition(NamedResourceMetadata.class);
boolean required = ("true".equals(el.getAttribute("required")));
String name = el.getAttribute("id");
String description = getChildElementText(el, "description", "");
namedResourceBuilder.addConstructorArgValue(name);
namedResourceBuilder.addConstructorArgValue(description);
namedResourceBuilder.addConstructorArgValue(required);
namedResourceList.add(namedResourceBuilder.getBeanDefinition());
}
BeanDefinitionBuilder namedResourceConfigurationBuilder = BeanDefinitionBuilder.genericBeanDefinition(NamedResourceConfiguration.class);
namedResourceConfigurationBuilder.addConstructorArgValue(namedResourceList);
return namedResourceConfigurationBuilder.getBeanDefinition();
}
private String getChildElementText(Element parent, String elementName, String defaultValue ){
String value = defaultValue;
Element child = DomUtils.getChildElementByTagName(parent, elementName);
if (child != null ) {
value = child.getTextContent();
}
return value;
}
private BeanDefinition buildPortMetadata(Element element, Element portElement) {
BeanDefinitionBuilder portMetadataBuilder = BeanDefinitionBuilder.genericBeanDefinition(PortMetadata.class);
portMetadataBuilder.addConstructorArgValue(portElement.getAttribute("name"));
String description = getChildElementText(portElement, "description", "");
portMetadataBuilder.addConstructorArgValue(description);
portMetadataBuilder.addConstructorArgValue(portElement.getAttribute("channel"));
return portMetadataBuilder.getBeanDefinition();
}

View File

@@ -29,7 +29,7 @@ public class FlowNamespaceHandler extends AbstractIntegrationNamespaceHandler {
registerBeanDefinitionParser("flow", new FlowParser());
registerBeanDefinitionParser("outbound-gateway", new FlowOutboundGatewayParser());
registerBeanDefinitionParser("flow-configuration", new FlowConfigurationParser());
registerBeanDefinitionParser("port-mapping", new FlowProviderPortConfigurationParser());
registerBeanDefinitionParser("port-mapping", new FlowPortConfigurationParser());
}
}

View File

@@ -45,6 +45,7 @@ public class FlowParser implements BeanDefinitionParser {
IntegrationNamespaceUtils.setValueIfAttributeDefined(flowBuilder, element, "referenced-bean-locations");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(flowBuilder, element, "properties");
IntegrationNamespaceUtils.setValueIfAttributeDefined(flowBuilder, element, "help");
IntegrationNamespaceUtils.setValueIfAttributeDefined(flowBuilder, element, "flow-id");
BeanDefinition beanDefinition = flowBuilder.getBeanDefinition();
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);

View File

@@ -25,7 +25,7 @@ import org.w3c.dom.Element;
* @author David Turanski
*
*/
public class FlowProviderPortConfigurationParser implements BeanDefinitionParser {
public class FlowPortConfigurationParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element arg0, ParserContext arg1) {

View File

@@ -84,7 +84,7 @@ public class FlowMessageHandler extends AbstractReplyProducingMessageHandler {
}
}
} catch (MessagingException me) {
log.debug("caught exception - failed message: " + me.getFailedMessage());
log.error(me.getMessage(), me);
if (conversationId.equals(me.getFailedMessage().getHeaders().get("flow.conversation.id"))) {
return new ErrorMessage(me);
}

View File

@@ -44,6 +44,14 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="flow-id" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Flow id
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
@@ -105,8 +113,7 @@
<xsd:element name="port-mapping" type="PortMappingType" />
</xsd:choice>
<xsd:element name="referenced-bean" minOccurs="0" maxOccurs="unbounded" type="NamedResourceType"/>
<xsd:element name="referenced-property" minOccurs="0" maxOccurs="unbounded" type="NamedResourceType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
@@ -129,52 +136,17 @@
<xsd:attribute name="channel" type="xsd:string" use="required" />
</xsd:complexType>
<xsd:complexType name="FlowPortType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines an integration flow port binding
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="BasePortType">
<xsd:sequence>
<xsd:element name="description" minOccurs="0"
maxOccurs="1" type="xsd:string" />
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="FlowOutputPortType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines an integration flow output port binding
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="FlowPortType">
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="PortMappingType">
<xsd:sequence>
<xsd:element name="input-port" minOccurs="1" maxOccurs="1"
nillable="false" type="FlowPortType">
nillable="false" type="BasePortType">
</xsd:element>
<xsd:element name="output-port" minOccurs="0" maxOccurs="unbounded" type="FlowOutputPortType"/>
<xsd:element name="output-port" minOccurs="0" maxOccurs="unbounded" type="BasePortType"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="NamedResourceType">
<xsd:sequence>
<xsd:element name="description" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="required" type="xsd:boolean" default="false">
</xsd:attribute>
<xsd:attribute name="id" use="required" type="xsd:ID">
</xsd:attribute>
</xsd:complexType>
</xsd:schema>

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2002-2011 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.integration.flow
import org.junit.Test
/**
*
* @author David Turanski
*
*/
class NamedResourcesConfigurationTest {
def resourceMetadataList = [
new NamedResourceMetadata('resource1','describes resource1',true),
new NamedResourceMetadata('resource2','describes resource2',false),
new NamedResourceMetadata('resource3','describes resource3',true)
]
@Test
public void testGetRequired(){
def namedResourceConfiguration = new NamedResourceConfiguration(resourceMetadataList);
def required = namedResourceConfiguration.getRequiredResources()
assert required.size() == 2
required.each { assert it.required; assert namedResourceConfiguration.isRequired(it.name) }
}
@Test
public void testGetAll(){
def namedResourceConfiguration = new NamedResourceConfiguration(resourceMetadataList);
assert namedResourceConfiguration.getConfiguredResources().size() == 3
}
}

View File

@@ -15,13 +15,14 @@
*/
package org.springframework.integration.flow.config.xml;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.flow.FlowConfiguration;
import org.springframework.integration.flow.FlowProviderPortConfiguration;
import org.springframework.integration.flow.PortConfiguration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -40,10 +41,10 @@ public class FlowConfigNamespaceTest {
public void test() {
assertNotNull(flowConfiguration.getPortConfigurations());
assertEquals(2, flowConfiguration.getPortConfigurations().size());
FlowProviderPortConfiguration pc0 = flowConfiguration.getPortConfigurations().get(0);
PortConfiguration pc0 = flowConfiguration.getPortConfigurations().get(0);
assertEquals("input", pc0.getInputPortName());
assertEquals("subflow-input", pc0.getInputChannel());
assertEquals("", pc0.getInputPortDescription());
assertEquals("subflow-output", pc0.getOutputChannel("output"));
assertEquals(1, pc0.getOutputPortNames().size());
}

View File

@@ -18,7 +18,6 @@ package org.springframework.integration.flow.config.xml;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@@ -54,7 +53,7 @@ public class FlowWithErrorTest {
@Test
public void testDirectCallWithErrorChannel(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/spring/integration/flows/subflow5-context.xml");
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/spring/integration/flows/subflow5/subflow5-context.xml");
MessageChannel input = applicationContext.getBean("subflow-input",MessageChannel.class);
SubscribableChannel error = applicationContext.getBean("errorChannel",SubscribableChannel.class);

View File

@@ -17,10 +17,8 @@ package org.springframework.integration.flow.config.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
@@ -50,16 +48,5 @@ public class FlowWithReferencesTest {
assertEquals("undefined",reply.getHeaders().get("property.value.2"));
}
@Test
public void testMissingPropertyReference() {
try {
new ClassPathXmlApplicationContext("classpath:/FlowWithMissingReferencesTest-context.xml");
fail("should throw exception");
} catch (BeanCreationException e) {
if(!("Flow [subflow3] is missing required property [key1]".equals(e.getCause().getMessage() ) ) ) {
e.printStackTrace();
fail(e.getMessage());
}
}
}
}

View File

@@ -1,37 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int-flow="http://www.springframework.org/schema/integration/flow"
xsi:schemaLocation="http://www.springframework.org/schema/integration/flow http://www.springframework.org/schema/integration/flow/spring-integration-flow-2.0.xsd
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int-flow="http://www.springframework.org/schema/integration/flow"
xsi:schemaLocation="http://www.springframework.org/schema/integration/flow http://www.springframework.org/schema/integration/flow/spring-integration-flow-2.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<int-flow:flow-configuration>
<int-flow:flow-configuration>
<int-flow:port-mapping>
<int-flow:input-port name="input" channel="subflow-input">
<int-flow:description></int-flow:description>
</int-flow:input-port>
<int-flow:output-port name="output" channel="subflow-output">
<int-flow:description></int-flow:description>
</int-flow:output-port>
</int-flow:port-mapping>
<int-flow:port-mapping-ref bean="portConfig1" />
<int-flow:port-mapping>
<int-flow:input-port name="input"
channel="subflow-input" />
<int-flow:port-mapping>
<int-flow:input-port name="input2" channel="subflow-input2">
<int-flow:description></int-flow:description>
</int-flow:input-port>
<int-flow:output-port name="output2" channel="subflow-output2">
<int-flow:description></int-flow:description>
</int-flow:output-port>
</int-flow:port-mapping>
<int-flow:output-port name="output"
channel="subflow-output" />
<int-flow:port-mapping-ref bean="portConfig2" />
</int-flow:port-mapping>
</int-flow:flow-configuration>
<int-flow:port-mapping-ref bean="portConfig1" />
<int-flow:port-mapping>
<int-flow:input-port name="input2" channel="subflow-input2" />
<int-flow:output-port name="output2" channel="subflow-output2"/>
</int-flow:port-mapping>
<int-flow:port-mapping>
<int-flow:input-port name="input2"
channel="subflow-input2" />
<int-flow:output-port name="output2"
channel="subflow-output2" />
</int-flow:port-mapping>
<int-flow:port-mapping-ref bean="portConfig2" />
</int-flow:flow-configuration>
<int-flow:port-mapping>
<int-flow:input-port name="input2" channel="subflow-input2" />
<int-flow:output-port name="output2"
channel="subflow-output2" />
</int-flow:port-mapping>
</beans>

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-flow="http://www.springframework.org/schema/integration/flow"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/flow http://www.springframework.org/schema/integration/flow/spring-integration-flow-2.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd">
<util:properties id="flowProperties">
<prop key="key2">val1</prop>
</util:properties>
<!-- Instantiate the flow -->
<int-flow:flow id="subflow3" help="true" referenced-bean-locations="classpath:ref-bean-config.xml" properties="flowProperties"/>
</beans>

View File

@@ -11,18 +11,9 @@
<int-flow:flow-configuration>
<int-flow:port-mapping>
<int-flow:input-port name="gateway-input"
channel="subflow-input">
<int-flow:description></int-flow:description>
</int-flow:input-port>
<int-flow:output-port name="gateway-output"
channel="hello-output">
<int-flow:description></int-flow:description>
</int-flow:output-port>
<int-flow:output-port name="gateway-discard"
channel="world-output">
<int-flow:description></int-flow:description>
</int-flow:output-port>
<int-flow:input-port name="gateway-input" channel="subflow-input"/>
<int-flow:output-port name="gateway-output" channel="hello-output"/>
<int-flow:output-port name="gateway-discard" channel="world-output"/>
</int-flow:port-mapping>
</int-flow:flow-configuration>

View File

@@ -19,7 +19,6 @@
<int-flow:input-port name="input" channel="subflow-input" />
<int-flow:output-port name="output" channel="subflow-output" />
</int-flow:port-mapping>
<int-flow:referenced-bean id="refbean" required="true" />
</int-flow:flow-configuration>
<int:channel id="subflow-output" />

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-flow="http://www.springframework.org/schema/integration/flow"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/flow http://www.springframework.org/schema/integration/flow/spring-integration-flow-2.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- Need to set ignore-unresolvable true or provide defaults for required properties -->
<context:property-placeholder ignore-unresolvable="true"/>
<int-flow:flow-configuration>
<int-flow:port-mapping>
<int-flow:input-port name="input" channel="subflow-input">
<int-flow:description>This optionally describes the flow</int-flow:description>
</int-flow:input-port>
<int-flow:output-port name="output" channel="subflow-output">
<int-flow:description>This describes the purpose/ necessary conditions for this output</int-flow:description>
</int-flow:output-port>
</int-flow:port-mapping>
<int-flow:referenced-property id="key1" required="true">
<int-flow:description>This is an example of a required property</int-flow:description>
</int-flow:referenced-property>
<int-flow:referenced-property id="key2" required="false">
<int-flow:description>This is an example of an optional property</int-flow:description>
</int-flow:referenced-property>
</int-flow:flow-configuration>
<int:channel id="subflow-output" />
</beans>

View File

@@ -13,12 +13,8 @@
<int-flow:flow-configuration>
<int-flow:port-mapping>
<int-flow:input-port name="input" channel="subflow-input">
<int-flow:description>This optionally describes the flow</int-flow:description>
</int-flow:input-port>
<int-flow:output-port name="output" channel="subflow-output">
<int-flow:description>This describes the purpose/ necessary conditions for this output</int-flow:description>
</int-flow:output-port>
<int-flow:input-port name="input" channel="subflow-input"/>
<int-flow:output-port name="output" channel="subflow-output"/>
</int-flow:port-mapping>
</int-flow:flow-configuration>

View File

@@ -13,16 +13,9 @@
<int-flow:flow-configuration>
<int-flow:port-mapping>
<int-flow:input-port name="input" channel="subflow-input">
<int-flow:description>This optionally describes the flow</int-flow:description>
</int-flow:input-port>
<int-flow:output-port name="output" channel="subflow-output">
<int-flow:description>This describes the purpose/ necessary conditions for this output</int-flow:description>
</int-flow:output-port>
<int-flow:output-port name="error" channel="errorChannel">
<int-flow:description>This describes the purpose/ necessary conditions for this output</int-flow:description>
</int-flow:output-port>
<int-flow:input-port name="input" channel="subflow-input"/>
<int-flow:output-port name="output" channel="subflow-output"/>
<int-flow:output-port name="error" channel="errorChannel"/>
</int-flow:port-mapping>
</int-flow:flow-configuration>

View File

@@ -16,7 +16,7 @@
</logger>
<logger name="org.springframework.integration">
<level value="debug" />
<level value="info" />
</logger>
<!-- Root Logger -->