This commit is contained in:
Mark Fisher
2009-07-03 23:17:50 +00:00
parent 4e3f8510d0
commit 604b7e9075

View File

@@ -6,54 +6,53 @@
<section id="file-intro">
<title>Introduction</title>
<para>
Spring Integration File extends the Spring Integration Core with
dedicated
vocabulary to deal with reading, writing and transforming
files.
There is a namespace that enables elements that define channel
adapters dedicated to files and support for transformers that
transform files into strings or byte arrays.
Spring Integration's File support extends the Spring Integration Core with
a dedicated vocabulary to deal with reading, writing, and transforming files.
It provides a namespace that enables elements defining Channel Adapters dedicated
to files and support for Transformers that can read file contents into strings or
byte arrays.
</para>
<para>
This section will explain the workings of <classname>FileReadingMessageSource</classname>, <classname>FileWritingMessageHandler</classname> and how to configure them as <emphasis>beans</emphasis>. Also the support for dealing with files through file specific implementations of <interfacename>Transformer</interfacename> will be discussed. Finally the file specific namespace will be explained.
This section will explain the workings of <classname>FileReadingMessageSource</classname>
and <classname>FileWritingMessageHandler</classname> and how to configure them as
<emphasis>beans</emphasis>. Also the support for dealing with files through file specific
implementations of <interfacename>Transformer</interfacename> will be discussed. Finally the
file specific namespace will be explained.
</para>
</section>
<section id="file-reading">
<title>Reading Files</title>
<para>
A <classname>FileReadingMessageSource</classname> can be used to consume files from the filesystem. This is an implementation of <interfacename>MessageSource</interfacename> that creates messages from a file system directory.
<programlisting language="xml"><![CDATA[<bean id="pollableFileSource"
A <classname>FileReadingMessageSource</classname> can be used to consume files from the filesystem.
This is an implementation of <interfacename>MessageSource</interfacename> that creates messages from
a file system directory. <programlisting language="xml"><![CDATA[<bean id="pollableFileSource"
class="org.springframework.integration.file.FileReadingMessageSource"
p:inputDirectory="file:${input.directory.property}"/>]]></programlisting>
p:inputDirectory="file:${input.directory}"/>]]></programlisting>
</para>
<para>
To prevent creating messages for certain files, you may supply a
<interfacename>FileListFilter</interfacename>. By default, an
<classname>AcceptOnceFileListFilter</classname>
is used. This filter ensures files are picked up only once from the
directory.
<classname>AcceptOnceFileListFilter</classname> is used. This filter
ensures files are picked up only once from the directory.
<programlisting language="xml"><![CDATA[<bean id="pollableFileSource"
class="org.springframework.integration.file.FileReadingMessageSource"
p:inputDirectory="file:${input.directory.property}"
p:inputDirectory="file:${input.directory}"
p:filter-ref="customFilterBean"/>]]></programlisting>
</para>
<para>
A common problem with reading files is that a file may be detected
before it is ready. The default
<classname>AcceptOnceFileListFilter
</classname>
does
not prevent this. In most cases, this can be prevented if the
A common problem with reading files is that a file may be detected before
it is ready. The default <classname>AcceptOnceFileListFilter</classname>
does not prevent this. In most cases, this can be prevented if the
file-writing process renames each file as soon as it is ready for
reading. A pattern-matching filter that accepts only files that are
ready (e.g. based on a known suffix), composed with the default
<classname>AcceptOnceFileListFilter
</classname>
allows for this.
<classname>AcceptOnceFileListFilter</classname> allows for this.
The <classname>CompositeFileListFilter</classname> enables the
composition.
<programlisting language="xml"><![CDATA[<bean id="pollableFileSource"
class="org.springframework.integration.file.FileReadingMessageSource"
p:inputDirectory="file:${input.directory.property}"
p:inputDirectory="file:${input.directory}"
p:filter-ref="compositeFilter"/>
<bean id="compositeFilter" class="org.springframework.integration.file.CompositeFileListFilter">
<constructor-arg>
@@ -67,8 +66,8 @@
</bean>]]></programlisting>
</para>
<para>
The configuration can be simplified using the file specific
namespace. To do this use the following template.
The configuration can be simplified using the file specific namespace. To do
this use the following template.
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -82,121 +81,116 @@
http://www.springframework.org/schema/integration/file/spring-integration-file-1.0.xsd">
</beans>]]></programlisting>
Within this namespace you can reduce the FileReadingMessageSource and wrap
it in an InboundChannelAdapter like this:
<programlisting language="xml"><![CDATA[<file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory.property}"/>
<file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory.property}"
filter="customFilterBean" />
<file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory.property}"
filename-pattern="^test.*$" /> ]]></programlisting>
Where the first channel adapter is relying on the default filter that just prevents
duplication. The second is using a custom filter and the third is using the
<emphasis>filename-pattern</emphasis>
attribute to add a
<classname>Pattern</classname>
based filter to the
<classname>FileReadingMessageSource</classname>
it in an inbound Channel Adapter like this:
<programlisting language="xml"><![CDATA[ <file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory}"/>
<file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory}"
filter="customFilterBean" />
<file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory}"
filename-pattern="^test.*$" /> ]]></programlisting>
The first channel adapter is relying on the default filter that just prevents
duplication, the second is using a custom filter, and the third is using the
<emphasis>filename-pattern</emphasis> attribute to add a <classname>Pattern</classname>
based filter to the <classname>FileReadingMessageSource</classname>.
</para>
</section>
<section id="file-writing">
<title>Writing files</title>
<para>
To write messages to the file system you can use a
<classname>FileWritingMessageHandler
</classname>
. This class can deal with File or byte[] payloads and otherwise
invokes the toString() method on the payload to establish the contents
of the File. In its simplest form the
<classname>FileWritingMessageHandler
</classname>
just needs a parent directory for the files.
<classname>FileWritingMessageHandler</classname>. This class can deal with
File, String, or byte array payloads. In its simplest form the
<classname>FileWritingMessageHandler </classname> only requires a
destination directory for writing the files. The name of the file to be
written is determined by the handler's <classname>FileNameGenerator</classname>.
The default implementation looks for a Message header whose key matches
the constant defined as <code>FileHeaders.FILENAME</code>.
</para>
<para>
Additionally, you can
configure the encoding and the charset that
will
be used in case of a
fallback on the toString() method.
Additionally, you can configure the encoding and the charset that
will be used in case of a String payload.
</para>
<para>
To make things easier you can configure the
FileWritingMessageHandler as part of an outbound channel adapter
using the namespace.
<programlisting language="xml"><![CDATA[<file:outbound-channel-adapter id="filesOut" directory="file:${input.directory.property}"/>]]></programlisting>
To make things easier you can configure the FileWritingMessageHandler as
part of an outbound channel adapter using the namespace.
<programlisting language="xml"><![CDATA[ <file:outbound-channel-adapter id="filesOut" directory="file:${input.directory.property}"/>]]></programlisting>
</para>
<para>
Namespace based configuration also supports <code>delete-source-files</code> attribute which if set to <code>true</code>
will trigger deletion of the original source files after writing to a destination.
The default value for that flag is <code>false</code>
<programlisting language="xml"><![CDATA[<file:outbound-channel-adapter id="filesOut" directory="file:${input.directory.property}"
delete-source-files="true"/>]]></programlisting>
The namespace based configuration also supports a <code>delete-source-files</code> attribute.
If set to <code>true</code>, it will trigger deletion of the original source files after writing
to a destination. The default value for that flag is <code>false</code>.
<programlisting language="xml"><![CDATA[ <file:outbound-channel-adapter id="filesOut"
directory="file:${output.directory}"
delete-source-files="true"/>]]></programlisting>
<note>
<para>
The <code>delete-source-files</code> attribute will only have an effect if the inbound Message has a File payload or
<classname>FileHeaders.ORIGINAL_FILE</classname> header value containing either a File instance or a String representing the original file path.
The <code>delete-source-files</code> attribute will only have an effect if the inbound
Message has a File payload or if the <classname>FileHeaders.ORIGINAL_FILE</classname> header
value contains either the source File instance or a String representing the original file path.
</para>
</note>
</para>
<para>
In cases where you want to continue processing messages based on the written File you can use <code>outbound-gateway</code> which plays a very similar role
as the <code>outbound-channel-adapter</code>. However instead of writing a file It will send it over to the reply channel
<programlisting language="xml"><![CDATA[<file:outbound-gateway id="mover" request-channel="moveInput"
reply-channel="output"
directory="${java.io.tmpdir}/anyDir"
delete-source-files="true"/>]]></programlisting>
In cases where you want to continue processing messages based on the written File you can use
the <code>outbound-gateway</code> instead. It plays a very similar role as the
<code>outbound-channel-adapter</code>. However after writing the File, it will also send it
to the reply channel as the payload of a Message.
<programlisting language="xml"><![CDATA[ <file:outbound-gateway id="mover" request-channel="moveInput"
reply-channel="output"
directory="${output.directory}"
delete-source-files="true"/>]]></programlisting>
</para>
<note>
The 'outbound-gateway' works well in cases where you want to first move a File and then send it
through a processing pipeline. In such cases, you may connect the file namespace's
'inbound-channel-adapter' element to the 'outbound-gateway' and then connect that gateway's
reply-channel to the beginning of the pipeline.
</note>
<para>
If you have more elaborate requirements to the payload to file
conversion you could extend the FileWritingMessageHandler, but a
much better option is to rely on a
<classname>Transformer</classname>
If you have more elaborate requirements or need to support additional payload types as input
to be converted to file content you could extend the FileWritingMessageHandler, but a much
better option is to rely on a <classname>Transformer</classname>.
</para>
</section>
<section id="file-transforming">
<title>File Transformers</title>
<para>
<!-- Introduction -->
To transform data read from the file system to objects and the other
way around you need to do some work. Contrary to
<classname>FileReadingMessageSource</classname>
and to a lesser extent
<classname>FileWritingMessageHandler
</classname>
it is very likely that you will need your own mechanism to get the
job done. For this you can implement the
<interfacename>Transformer</interfacename>
interface. Or extend the
<classname>AbstractFilePayloadTransformer
</classname>
for inbound messages. Some obvious implementations have been
provided.
To transform data read from the file system to objects and the other way around you need
to do some work. Contrary to <classname>FileReadingMessageSource</classname> and to a
lesser extent <classname>FileWritingMessageHandler</classname>, it is very likely that you
will need your own mechanism to get the job done. For this you can implement the
<interfacename>Transformer</interfacename> interface. Or extend the
<classname>AbstractFilePayloadTransformer</classname> for inbound messages. Some obvious
implementations have been provided.
</para>
<para>
<classname>FileToByteArrayTransformer
</classname>
transforms Files into byte[]s using
<classname>FileCopyUtils</classname>
. It is often better to use a sequence of transformers than to put
all transformations in a single class, in that case the File to
<classname>FileToByteArrayTransformer</classname> transforms Files into byte[]s using
Spring's <classname>FileCopyUtils</classname>. It is often better to use a sequence of
transformers than to put all transformations in a single class. In that case the File to
byte[] conversion might be a logical first step.
</para>
<para>
<classname>FileToStringTransformer</classname>
will convert Files to Strings as the name suggests. This is mainly useful for debugging.
<classname>FileToStringTransformer</classname> will convert Files to Strings as the name
suggests. If nothing else, this can be useful for debugging (consider using with a Wire Tap).
</para>
<para>
To configure File specific transformers you can use the appropriate
elements from the file namespace.
<programlisting language="xml"><![CDATA[<file-to-bytes-transformer input-channel="input" output-channel="output" delete-files="true"/>]]></programlisting>
The
<emphasis>delete-files</emphasis>
option signals the transformer to delete the File after the
transformation is done. This is in no way a replacement for using the
<classname>AcceptOnceFileListFilter
</classname>
with the PollableFileSource in a multi-threaded environment (e.g. Spring Integration in general).
To configure File specific transformers you can use the appropriate elements from the file namespace.
<programlisting language="xml"><![CDATA[ <file-to-bytes-transformer input-channel="input" output-channel="output"
delete-files="true"/>
<file:file-to-string-transformer input-channel="input" output-channel="output
delete-files="true" charset="UTF-8"/>]]></programlisting>
The <emphasis>delete-files</emphasis> option signals to the transformer that it should delete
the inbound File after the transformation is complete. This is in no way a replacement for using the
<classname>AcceptOnceFileListFilter</classname> when the FileReadingMessageSource is being used in a
multi-threaded environment (e.g. Spring Integration in general).
</para>
</section>
</chapter>