INT-666 Added support for the 'outbound-gateway' element in the file namespace.

This commit is contained in:
Mark Fisher
2009-07-01 00:51:05 +00:00
parent e8bc39d475
commit 6da3307943
8 changed files with 376 additions and 52 deletions

View File

@@ -29,6 +29,7 @@ import org.springframework.core.io.Resource;
import org.springframework.integration.core.Message;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.handler.ReplyMessageHolder;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.integration.message.MessageHandler;
import org.springframework.integration.message.MessageHandlingException;
import org.springframework.util.Assert;
@@ -148,7 +149,14 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
throw new MessageHandlingException(requestMessage, "failed to write Message payload to file", e);
}
if (resultFile != null) {
replyMessageHolder.set(resultFile);
if (originalFileFromHeader == null && payload instanceof File) {
replyMessageHolder.set(MessageBuilder.withPayload(resultFile)
.setHeader(FileHeaders.ORIGINAL_FILE, (File) payload)
.build());
}
else {
replyMessageHolder.set(resultFile);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 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.
@@ -29,6 +29,7 @@ public class FileNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("inbound-channel-adapter", new FileInboundChannelAdapterParser());
registerBeanDefinitionParser("outbound-channel-adapter", new FileOutboundChannelAdapterParser());
registerBeanDefinitionParser("outbound-gateway", new FileOutboundGatewayParser());
registerBeanDefinitionParser("file-to-string-transformer", new FileToStringTransformerParser());
registerBeanDefinitionParser("file-to-bytes-transformer", new FileToByteArrayTransformerParser());
}

View File

@@ -22,9 +22,7 @@ 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.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.util.StringUtils;
/**
* Parser for the <outbound-channel-adapter/> element of the 'file'
@@ -37,23 +35,9 @@ public class FileOutboundChannelAdapterParser extends AbstractOutboundChannelAda
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
String directory = element.getAttribute("directory");
if (!StringUtils.hasText(directory)) {
parserContext.getReaderContext().error("directory is required", element);
}
if (directory.indexOf(':') == -1) {
directory = "file:" + directory;
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition("org.springframework.integration.file.FileWritingMessageHandler");
builder.addConstructorArgValue(directory);
builder.addPropertyReference("outputChannel", IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "delete-source-files");
String fileNameGenerator = element.getAttribute("filename-generator");
if (StringUtils.hasText(fileNameGenerator)) {
builder.addPropertyReference("fileNameGenerator", fileNameGenerator);
}
return builder.getBeanDefinition();
BeanDefinitionBuilder builder = FileWritingMessageHandlerBeanDefinitionBuilder.configure(
element, IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME, parserContext);
return (builder != null ? builder.getBeanDefinition() : null);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2002-2009 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.config;
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;
/**
* Parser for the 'outbound-gateway' element of the file namespace.
*
* @author Mark Fisher
* @since 1.0.3
*/
public class FileOutboundGatewayParser extends AbstractConsumerEndpointParser {
@Override
protected String getInputChannelAttributeName() {
return "request-channel";
}
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
String replyChannel = element.getAttribute("reply-channel");
return FileWritingMessageHandlerBeanDefinitionBuilder.configure(element, replyChannel, parserContext);
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2002-2009 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.config;
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.IntegrationNamespaceUtils;
import org.springframework.util.StringUtils;
/**
* A common helper class for the 'outbound-channel-adapter' and 'outbound-gateway'
* element parsers. Both of those are responsible for creating an instance of
* {@link org.springframework.integration.file.FileWritingMessageHandler}.
*
* @author Mark Fisher
* @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;
}
String directory = element.getAttribute("directory");
if (!StringUtils.hasText(directory)) {
parserContext.getReaderContext().error("directory is required", element);
}
if (directory.indexOf(':') == -1) {
directory = "file:" + directory;
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition("org.springframework.integration.file.FileWritingMessageHandler");
builder.addConstructorArgValue(directory);
if (StringUtils.hasText(outputChannelBeanName)) {
builder.addPropertyReference("outputChannel", outputChannelBeanName);
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "delete-source-files");
String fileNameGenerator = element.getAttribute("filename-generator");
if (StringUtils.hasText(fileNameGenerator)) {
builder.addPropertyReference("fileNameGenerator", fileNameGenerator);
}
return builder;
}
}

View File

@@ -82,41 +82,81 @@
Configures an outbound Channel Adapter that writes Message payloads to a File.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="directory" type="xsd:string" use="required"/>
<xsd:attribute name="filename-generator" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.file.FileNameGenerator"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="delete-source-files" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
<xsd:complexContent>
<xsd:extension base="outboundFileBaseType">
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="outbound-gateway">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Configures an outbound Gateway that writes request Message payloads to a File and
then generates a reply Message containing the newly written File as its payload.
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="outboundFileBaseType">
<xsd:attribute name="request-channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="outboundFileBaseType">
<xsd:sequence>
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="directory" type="xsd:string" use="required"/>
<xsd:attribute name="filename-generator" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.file.FileNameGenerator"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="delete-source-files" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specify whether to delete source files after writing to the destination directory.
This will take effect if the Message payload is the actual source File instance
or if the original File instance (or its path) is available in the header value
associated with the FileHeaders.ORIGINAL_FILE constant. The default value is false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:element name="file-to-string-transformer">
<xsd:complexType>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/integration/file"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file-1.0.xsd">
<si:channel id="copyInput" />
<si:channel id="moveInput" />
<si:channel id="output">
<si:queue capacity="1" />
</si:channel>
<outbound-gateway id="copier"
request-channel="copyInput"
reply-channel="output"
directory="${java.io.tmpdir}/anyDir" />
<outbound-gateway id="mover"
request-channel="moveInput"
reply-channel="output"
directory="${java.io.tmpdir}/anyDir"
delete-source-files="true"/>
<context:property-placeholder />
</beans:beans>

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2002-2009 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 static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.io.FileOutputStream;
import java.util.List;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.file.FileHeaders;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.FileCopyUtils;
/**
* @author Alex Peters
* @author Mark Fisher
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class FileOutboundGatewayIntegrationTests {
FileWritingMessageHandler handler;
@Qualifier("copyInput")
@Autowired
MessageChannel copyInputChannel;
@Qualifier("moveInput")
@Autowired
MessageChannel moveInputChannel;
@Qualifier("output")
@Autowired
QueueChannel outputChannel;
@Autowired
BeanFactory beanFactory;
static final String DEFAULT_ENCODING = "UTF-8";
static final String SAMPLE_CONTENT = "HelloWorld\näöüß";
Message<File> message;
File sourceFile;
static File workDir;
@BeforeClass
public static void setupClass() {
workDir = new File(System.getProperty("java.io.tmpdir") + "/anyDir");
workDir.mkdir();
workDir.deleteOnExit();
}
@AfterClass
public static void cleanUp() {
if (workDir != null && workDir.exists()) {
for (File file : workDir.listFiles()) {
file.delete();
}
}
workDir.delete();
}
@Before
public void setUp() throws Exception {
sourceFile = File.createTempFile("anyFile", ".txt");
sourceFile.deleteOnExit();
FileCopyUtils.copy(SAMPLE_CONTENT.getBytes(DEFAULT_ENCODING),
new FileOutputStream(sourceFile, false));
message = MessageBuilder.withPayload(sourceFile).build();
}
@After
public void tearDown() {
sourceFile.delete();
}
@Test
public void instancesCreated() throws Exception {
assertThat(beanFactory.getBean("copier"), is(notNullValue()));
assertThat(beanFactory.getBean("mover"), is(notNullValue()));
}
@Test
public void copy() throws Exception {
copyInputChannel.send(message);
List<Message<?>> result = outputChannel.clear();
assertThat(result.size(), is(1));
Message<?> resultMessage = result.get(0);
File payloadFile = (File) resultMessage.getPayload();
assertThat(payloadFile, is(not(sourceFile)));
assertThat(resultMessage.getHeaders().get(FileHeaders.ORIGINAL_FILE, File.class),
is(sourceFile));
assertThat(sourceFile.exists(), is(true));
assertThat(payloadFile.exists(), is(true));
}
@Test
public void move() throws Exception {
moveInputChannel.send(message);
List<Message<?>> result = outputChannel.clear();
assertThat(result.size(), is(1));
Message<?> resultMessage = result.get(0);
File payloadFile = (File) resultMessage.getPayload();
assertThat(payloadFile, is(not(sourceFile)));
assertThat(resultMessage.getHeaders().get(FileHeaders.ORIGINAL_FILE, File.class),
is(sourceFile));
assertThat(sourceFile.exists(), is(false));
assertThat(payloadFile.exists(), is(true));
}
}