INT-2275: any outbound-channel-adapter in <chain>

Add re-init logic for nested chains
Add logic about nested element for AbstractChannelAdapterParser
Refactor of DefaultOutboundChannelAdapterParser
Test for non-last nested chain with some outbound-channel-adapter
Improve XSD for chain-type
Manual outbound-channel-adapter ability for chain
Integration tests for all outbound-channel-adapter within <chain>
Remove redundant 'return-value-required' attribute from <stored-proc-outbound-channel-adapter>
Add support 'expectReply' for FileWritingMessageHandler

INT-2275 polishing & refactor FileOutbound*Parser

HttpRequestExecutingMessageHandlerTests polishing

INT-2275: polishing JavaDoc
This commit is contained in:
Artem Bilan
2012-01-03 23:26:16 +02:00
committed by Oleg Zhurakousky
parent 4d5b8d5be1
commit 45c429ee2b
58 changed files with 1302 additions and 318 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2012 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.
@@ -54,6 +54,7 @@ import java.nio.charset.Charset;
* @author Iwein Fuld
* @author Alex Peters
* @author Oleg Zhurakousky
* @author Artem Bilan
*/
public class FileWritingMessageHandler extends AbstractReplyProducingMessageHandler {
@@ -71,6 +72,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
private volatile Charset charset = Charset.defaultCharset();
private volatile boolean expectReply = true;
public FileWritingMessageHandler(File destinationDirectory) {
Assert.notNull(destinationDirectory, "Destination directory must not be null.");
@@ -92,7 +94,15 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
public void setTemporaryFileSuffix(String temporaryFileSuffix) {
this.temporaryFileSuffix = temporaryFileSuffix;
}
/**
* Specify whether a reply Message is expected. If not, this handler will simply return null for a
* successful response or throw an Exception for a non-successful response. The default is true.
*/
public void setExpectReply(boolean expectReply) {
this.expectReply = expectReply;
}
protected String getTemporaryFileSuffix() {
return temporaryFileSuffix;
}
@@ -169,6 +179,11 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
catch (Exception e) {
throw new MessageHandlingException(requestMessage, "failed to write Message payload to file", e);
}
if (!this.expectReply) {
return null;
}
if (resultFile != null) {
if (originalFileFromHeader == null && payload instanceof File) {
return MessageBuilder.withPayload(resultFile)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2012 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.
@@ -22,8 +22,6 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.util.StringUtils;
/**
* Parser for the &lt;outbound-channel-adapter/&gt; element of the 'file'
@@ -32,36 +30,14 @@ import org.springframework.util.StringUtils;
* @author Mark Fisher
* @author Iwein Fuld
* @author Oleg Zhurakousky
* @author Artem Bilan
*/
public class FileOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
BeanDefinitionBuilder handlerBuilder = FileWritingMessageHandlerBeanDefinitionBuilder.configure(
element, IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME, parserContext);
if (handlerBuilder != null){
String remoteFileNameGenerator = element.getAttribute("filename-generator");
String remoteFileNameGeneratorExpression = element.getAttribute("filename-generator-expression");
boolean hasRemoteFileNameGenerator = StringUtils.hasText(remoteFileNameGenerator);
boolean hasRemoteFileNameGeneratorExpression = StringUtils.hasText(remoteFileNameGeneratorExpression);
if (hasRemoteFileNameGenerator || hasRemoteFileNameGeneratorExpression) {
if (hasRemoteFileNameGenerator && hasRemoteFileNameGeneratorExpression) {
parserContext.getReaderContext().error("at most one of 'filename-generator-expression' or 'filename-generator' " +
"is allowed on file outbound adapter/gateway", element);
}
if (hasRemoteFileNameGenerator) {
handlerBuilder.addPropertyReference("fileNameGenerator", remoteFileNameGenerator);
}
else {
BeanDefinitionBuilder fileNameGeneratorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.file.DefaultFileNameGenerator");
fileNameGeneratorBuilder.addPropertyValue("expression", remoteFileNameGeneratorExpression);
handlerBuilder.addPropertyValue("fileNameGenerator", fileNameGeneratorBuilder.getBeanDefinition());
}
}
}
return (handlerBuilder != null ? handlerBuilder.getBeanDefinition() : null);
BeanDefinitionBuilder handlerBuilder = FileWritingMessageHandlerBeanDefinitionBuilder.configure(element, false, parserContext);
return handlerBuilder.getBeanDefinition();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2012 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.
@@ -16,18 +16,19 @@
package org.springframework.integration.file.config;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
import org.springframework.util.StringUtils;
/**
* Parser for the 'outbound-gateway' element of the file namespace.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Artem Bilan
* @since 1.0.3
*/
public class FileOutboundGatewayParser extends AbstractConsumerEndpointParser {
@@ -39,31 +40,8 @@ public class FileOutboundGatewayParser extends AbstractConsumerEndpointParser {
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
String replyChannel = element.getAttribute("reply-channel");
BeanDefinitionBuilder handlerBuilder =
FileWritingMessageHandlerBeanDefinitionBuilder.configure(element, replyChannel, parserContext);
String remoteFileNameGenerator = element.getAttribute("filename-generator");
String remoteFileNameGeneratorExpression = element.getAttribute("filename-generator-expression");
boolean hasRemoteFileNameGenerator = StringUtils.hasText(remoteFileNameGenerator);
boolean hasRemoteFileNameGeneratorExpression = StringUtils.hasText(remoteFileNameGeneratorExpression);
if (hasRemoteFileNameGenerator || hasRemoteFileNameGeneratorExpression) {
if (hasRemoteFileNameGenerator && hasRemoteFileNameGeneratorExpression) {
parserContext.getReaderContext().error("at most one of 'filename-generator-expression' or 'filename-generator' " +
"is allowed on file outbound adapter/gateway", element) ;
}
if (hasRemoteFileNameGenerator) {
handlerBuilder.addPropertyReference("fileNameGenerator", remoteFileNameGenerator);
}
else {
BeanDefinitionBuilder fileNameGeneratorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.file.DefaultFileNameGenerator");
fileNameGeneratorBuilder.addPropertyValue("expression", remoteFileNameGeneratorExpression);
handlerBuilder.addPropertyValue("fileNameGenerator", fileNameGeneratorBuilder.getBeanDefinition());
}
}
BeanDefinitionBuilder handlerBuilder = FileWritingMessageHandlerBeanDefinitionBuilder.configure(element, true, parserContext);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(handlerBuilder, element, "reply-channel", "outputChannel");
return handlerBuilder;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2012 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.
@@ -16,6 +16,7 @@
package org.springframework.integration.file.config;
import org.springframework.integration.file.DefaultFileNameGenerator;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -29,32 +30,42 @@ import org.springframework.util.StringUtils;
* {@link org.springframework.integration.file.FileWritingMessageHandler}.
*
* @author Mark Fisher
* @author Artem Bilan
* @since 1.0.3
*/
abstract class FileWritingMessageHandlerBeanDefinitionBuilder {
static BeanDefinitionBuilder configure(Element element, String outputChannelBeanName, ParserContext parserContext) {
if (outputChannelBeanName == null) {
parserContext.getReaderContext().error("outputChannelBeanName must not be null", element);
return null;
}
static BeanDefinitionBuilder configure(Element element, boolean expectReply, ParserContext parserContext) {
String directory = element.getAttribute("directory");
if (!StringUtils.hasText(directory)) {
parserContext.getReaderContext().error("directory is required", element);
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition("org.springframework.integration.file.config.FileWritingMessageHandlerFactoryBean");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FileWritingMessageHandlerFactoryBean.class);
builder.addPropertyValue("directory", directory);
if (StringUtils.hasText(outputChannelBeanName)) {
builder.addPropertyReference("outputChannel", outputChannelBeanName);
}
builder.addPropertyValue("expectReply", expectReply);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-create-directory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "delete-source-files");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "temporary-file-suffix");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "charset");
String fileNameGenerator = element.getAttribute("filename-generator");
if (StringUtils.hasText(fileNameGenerator)) {
builder.addPropertyReference("fileNameGenerator", fileNameGenerator);
String remoteFileNameGenerator = element.getAttribute("filename-generator");
String remoteFileNameGeneratorExpression = element.getAttribute("filename-generator-expression");
boolean hasRemoteFileNameGenerator = StringUtils.hasText(remoteFileNameGenerator);
boolean hasRemoteFileNameGeneratorExpression = StringUtils.hasText(remoteFileNameGeneratorExpression);
if (hasRemoteFileNameGenerator || hasRemoteFileNameGeneratorExpression) {
if (hasRemoteFileNameGenerator && hasRemoteFileNameGeneratorExpression) {
parserContext.getReaderContext().error("at most one of 'filename-generator-expression' or 'filename-generator' " +
"is allowed on file outbound adapter/gateway", element);
}
if (hasRemoteFileNameGenerator) {
builder.addPropertyReference("fileNameGenerator", remoteFileNameGenerator);
}
else {
BeanDefinitionBuilder fileNameGeneratorBuilder = BeanDefinitionBuilder
.genericBeanDefinition(DefaultFileNameGenerator.class);
fileNameGeneratorBuilder.addPropertyValue("expression", remoteFileNameGeneratorExpression);
builder.addPropertyValue("fileNameGenerator", fileNameGeneratorBuilder.getBeanDefinition());
}
}
return builder;
}

View File

@@ -29,6 +29,7 @@ import org.springframework.integration.file.FileWritingMessageHandler;
* @author Iwein Fuld
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @since 1.0.3
*/
public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageHandlerFactoryBean<FileWritingMessageHandler>{
@@ -48,7 +49,9 @@ public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageH
private volatile Long sendTimeout;
private volatile String temporaryFileSuffix;
private volatile boolean expectReply = true;
public void setDirectory(File directory) {
this.directory = directory;
}
@@ -80,7 +83,11 @@ public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageH
public void setTemporaryFileSuffix(String temporaryFileSuffix) {
this.temporaryFileSuffix = temporaryFileSuffix;
}
public void setExpectReply(boolean expectReply) {
this.expectReply = expectReply;
}
@Override
protected FileWritingMessageHandler createHandler() {
FileWritingMessageHandler handler = new FileWritingMessageHandler(this.directory);
@@ -105,6 +112,7 @@ public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageH
if (this.temporaryFileSuffix != null) {
handler.setTemporaryFileSuffix(this.temporaryFileSuffix);
}
handler.setExpectReply(this.expectReply);
return handler;
}
}

View File

@@ -0,0 +1,30 @@
<?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:context="http://www.springframework.org/schema/context"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
<si:chain input-channel="outboundChainChannel">
<si:header-enricher>
<si:header name="#{T(org.springframework.integration.file.FileHeaders).FILENAME}" value="${test.file}"/>
</si:header-enricher>
<file:outbound-channel-adapter directory="${work.dir}"/>
</si:chain>
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties" value="#{T(org.springframework.integration.file.FileOutboundChannelAdapterInsideChainTests).placeholderProperties}"/>
</bean>
<context:property-placeholder properties-ref="placeholderProperties"/>
</beans>

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2002-2012 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.file;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.FileCopyUtils;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import static org.junit.Assert.*;
/**
* //INT-2275
*
* @author Artem Bilan
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class FileOutboundChannelAdapterInsideChainTests {
public static final String TEST_FILE_NAME = FileOutboundChannelAdapterInsideChainTests.class.getSimpleName();
public static final String WORK_DIR_NAME = System.getProperty("java.io.tmpdir") + "/" + FileOutboundChannelAdapterInsideChainTests.class.getSimpleName() + "Dir";
public static final String SAMPLE_CONTENT = "test";
public static Properties placeholderProperties = new Properties();
static {
placeholderProperties.put("test.file", TEST_FILE_NAME);
placeholderProperties.put("work.dir", WORK_DIR_NAME);
}
@Autowired
private MessageChannel outboundChainChannel;
private static File workDir;
@BeforeClass
public static void setupClass() {
workDir = new File(WORK_DIR_NAME);
workDir.mkdir();
workDir.deleteOnExit();
}
@AfterClass
public static void cleanUp() {
if (workDir != null && workDir.exists()) {
for (File file : workDir.listFiles()) {
file.delete();
}
}
workDir.delete();
}
@Test //INT-2275
public void testFileOutboundChannelAdapterWithinChain() throws IOException {
Message<String> message = MessageBuilder.withPayload(SAMPLE_CONTENT).build();
outboundChainChannel.send(message);
File testFile = new File(workDir, TEST_FILE_NAME);
assertTrue(testFile.exists());
byte[] testFileContent = FileCopyUtils.copyToByteArray(testFile);
assertEquals(new String(testFileContent), SAMPLE_CONTENT);
}
}