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");
+            }
+        };
+    }
+}


Reply via email to