Author: davsclaus Date: Wed Mar 27 10:57:50 2013 New Revision: 1461507 URL: http://svn.apache.org/r1461507 Log: CAMEL-6205: Added forceWrites option to file producer. Thanks to Christian Tytgat for the patch.
Added: camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerNoForcedWritesTest.java Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/FileEndpoint.java camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java camel/trunk/camel-core/src/main/java/org/apache/camel/converter/IOConverter.java camel/trunk/camel-core/src/main/java/org/apache/camel/util/IOHelper.java Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/FileEndpoint.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/FileEndpoint.java?rev=1461507&r1=1461506&r2=1461507&view=diff ============================================================================== --- camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/FileEndpoint.java (original) +++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/FileEndpoint.java Wed Mar 27 10:57:50 2013 @@ -34,6 +34,7 @@ public class FileEndpoint extends Generi private FileOperations operations = new FileOperations(this); private File file; private boolean copyAndDeleteOnRenameFail = true; + private boolean forceWrites = true; public FileEndpoint() { // use marker file as default exclusive read locks @@ -165,4 +166,12 @@ public class FileEndpoint extends Generi public void setCopyAndDeleteOnRenameFail(boolean copyAndDeleteOnRenameFail) { this.copyAndDeleteOnRenameFail = copyAndDeleteOnRenameFail; } + + public boolean isForceWrites() { + return forceWrites; + } + + public void setForceWrites(boolean forceWrites) { + this.forceWrites = forceWrites; + } } Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java?rev=1461507&r1=1461506&r2=1461507&view=diff ============================================================================== --- camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java (original) +++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java Wed Mar 27 10:57:50 2013 @@ -359,9 +359,7 @@ public class FileOperations implements G } } finally { IOHelper.close(in, source.getName(), LOG); - // force updates to be written, and then close afterwards - IOHelper.force(out, target.getName(), LOG); - IOHelper.close(out, target.getName(), LOG); + IOHelper.close(out, target.getName(), LOG, endpoint.isForceWrites()); } } @@ -383,25 +381,24 @@ public class FileOperations implements G } } finally { IOHelper.close(in, target.getName(), LOG); - // force updates to be written, and then close afterwards - IOHelper.force(out, target.getName(), LOG); - IOHelper.close(out, target.getName(), LOG); + IOHelper.close(out, target.getName(), LOG, endpoint.isForceWrites()); } } private void writeFileByReaderWithCharset(Reader in, File target, String charset) throws IOException { boolean append = endpoint.getFileExist() == GenericFileExist.Append; - Writer out = IOConverter.toWriter(target, append, charset); + FileOutputStream os = new FileOutputStream(target, append); + Writer out = IOConverter.toWriter(os, charset); try { LOG.debug("Using Reader to write file: {} with charset: {}", target, charset); int size = endpoint.getBufferSize(); IOHelper.copy(in, out, size); } finally { IOHelper.close(in, target.getName(), LOG); - IOHelper.close(out, target.getName(), LOG); + IOHelper.close(out, os, target.getName(), LOG, endpoint.isForceWrites()); } } - + /** * Creates a new file if the file doesn't exist. * If the endpoint's existing file logic is set to 'Override' then the target file will be truncated @@ -416,8 +413,7 @@ public class FileOperations implements G try { out.truncate(0); } finally { - IOHelper.force(out, target.getName(), LOG); - IOHelper.close(out, target.getName(), LOG); + IOHelper.close(out, target.getName(), LOG, endpoint.isForceWrites()); } } } Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/converter/IOConverter.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/converter/IOConverter.java?rev=1461507&r1=1461506&r2=1461507&view=diff ============================================================================== --- camel/trunk/camel-core/src/main/java/org/apache/camel/converter/IOConverter.java (original) +++ camel/trunk/camel-core/src/main/java/org/apache/camel/converter/IOConverter.java Wed Mar 27 10:57:50 2013 @@ -130,16 +130,21 @@ public final class IOConverter { */ @Deprecated public static BufferedWriter toWriter(File file) throws IOException { - return toWriter(file, false, IOHelper.getCharsetName(null, true)); + FileOutputStream os = new FileOutputStream(file, false); + return toWriter(os, IOHelper.getCharsetName(null, true)); } @Converter public static BufferedWriter toWriter(File file, Exchange exchange) throws IOException { - return toWriter(file, false, IOHelper.getCharsetName(exchange)); + FileOutputStream os = new FileOutputStream(file, false); + return toWriter(os, IOHelper.getCharsetName(exchange)); } public static BufferedWriter toWriter(File file, boolean append, String charset) throws IOException { - FileOutputStream os = new FileOutputStream(file, append); + return toWriter(new FileOutputStream(file, append), charset); + } + + public static BufferedWriter toWriter(FileOutputStream os, String charset) throws IOException { return IOHelper.buffered(new EncodingFileWriter(os, charset)); } Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/util/IOHelper.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/util/IOHelper.java?rev=1461507&r1=1461506&r2=1461507&view=diff ============================================================================== --- camel/trunk/camel-core/src/main/java/org/apache/camel/util/IOHelper.java (original) +++ camel/trunk/camel-core/src/main/java/org/apache/camel/util/IOHelper.java Wed Mar 27 10:57:50 2013 @@ -21,6 +21,7 @@ import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.Closeable; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -230,6 +231,62 @@ public final class IOHelper { } /** + * Forces any updates to a FileOutputStream be written to the storage device that contains it. + * + * @param os the file output stream + * @param name the name of the resource + * @param log the log to use when reporting warnings, will use this class's own {@link Logger} if <tt>log == null</tt> + */ + public static void force(FileOutputStream os, String name, Logger log) { + try { + if (os != null) { + os.getFD().sync(); + } + } catch (Exception e) { + if (log == null) { + // then fallback to use the own Logger + log = LOG; + } + if (name != null) { + log.warn("Cannot sync FileDescriptor: " + name + ". Reason: " + e.getMessage(), e); + } else { + log.warn("Cannot sync FileDescriptor. Reason: " + e.getMessage(), e); + } + } + } + + /** + * Closes the given writer, logging any closing exceptions to the given log. + * An associated FileOutputStream can optionally be forced to disk. + * + * @param writer the writer to close + * @param os an underlying FileOutputStream that will to be forced to disk according to the the force parameter + * @param name the name of the resource + * @param log the log to use when reporting warnings, will use this class's own {@link Logger} if <tt>log == null</tt> + * @param force forces the FileOutputStream to disk + */ + public static void close(Writer writer, FileOutputStream os, String name, Logger log, boolean force) { + if (writer != null && force) { + // flush the writer prior to syncing the FD + try { + writer.flush(); + } catch (Exception e) { + if (log == null) { + // then fallback to use the own Logger + log = LOG; + } + if (name != null) { + log.warn("Cannot flush Writer: " + name + ". Reason: " + e.getMessage(), e); + } else { + log.warn("Cannot flush Writer. Reason: " + e.getMessage(), e); + } + } + force(os, name, log); + } + close(writer, name, log); + } + + /** * Closes the given resource if it is available, logging any closing exceptions to the given log. * * @param closeable the object to close @@ -255,6 +312,22 @@ public final class IOHelper { } /** + * Closes the given channel if it is available, logging any closing exceptions to the given log. + * The file's channel can optionally be forced to disk. + * + * @param channel the file channel + * @param name the name of the resource + * @param log the log to use when reporting warnings, will use this class's own {@link Logger} if <tt>log == null</tt> + * @param force forces the file channel to disk + */ + public static void close(FileChannel channel, String name, Logger log, boolean force) { + if (force) { + force(channel, name, log); + } + close(channel, name, log); + } + + /** * Closes the given resource if it is available. * * @param closeable the object to close Added: camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerNoForcedWritesTest.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerNoForcedWritesTest.java?rev=1461507&view=auto ============================================================================== --- camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerNoForcedWritesTest.java (added) +++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerNoForcedWritesTest.java Wed Mar 27 10:57:50 2013 @@ -0,0 +1,58 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.file; + +import java.io.File; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; + +public class FileProducerNoForcedWritesTest extends ContextTestSupport { + + @Override + protected void setUp() throws Exception { + deleteDirectory("target/file"); + super.setUp(); + template.sendBodyAndHeader("file://target/file", "Hello World", Exchange.FILE_NAME, "hello.txt"); + } + + public void testNoForcedWrites() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedBodiesReceived("Hello World"); + + assertMockEndpointsSatisfied(); + + assertFileExists("target/file/output.txt"); + assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File("target/file/output.txt"))); + + assertFileExists("target/file/output2.txt"); + assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File("target/file/output2.txt"))); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("file:target/file?noop=true").multicast().to("file:target/file/?fileName=output.txt&forceWrites=false", + "file:target/file/?fileName=output2.txt&charset=iso-8859-1&forceWrites=false").to("mock:result"); + } + }; + } +}