You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2012/08/22 19:00:24 UTC

svn commit: r1376140 - in /camel/trunk: camel-core/src/main/java/org/apache/camel/component/file/ camel-core/src/test/java/org/apache/camel/component/file/ components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/ components/camel-ftp/...

Author: davsclaus
Date: Wed Aug 22 17:00:24 2012
New Revision: 1376140

URL: http://svn.apache.org/viewvc?rev=1376140&view=rev
Log:
CAMEL-5521: Added fileExists=Move to file/ftp producer.

Added:
    camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerMoveExistingTest.java   (with props)
    camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpProducerMoveExistingTest.java   (with props)
    camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/sftp/SftpProducerMoveExistingTest.java   (with props)
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/component/file/GenericFileEndpoint.java
    camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/GenericFileExist.java
    camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java
    camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java
    camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.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=1376140&r1=1376139&r2=1376140&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 Aug 22 17:00:24 2012
@@ -98,6 +98,13 @@ public class FileEndpoint extends Generi
             throw new IllegalArgumentException("You cannot set both fileExist=Append and tempPrefix options");
         }
 
+        // ensure fileExist and moveExisting is configured correctly if in use
+        if (getFileExist() == GenericFileExist.Move && getMoveExisting() == null) {
+            throw new IllegalArgumentException("You must configure moveExisting option when fileExist=Move");
+        } else if (getMoveExisting() != null && getFileExist() != GenericFileExist.Move) {
+            throw new IllegalArgumentException("You must configure fileExist=Move when moveExisting has been set");
+        }
+
         return new GenericFileProducer<File>(this, operations);
     }
 

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=1376140&r1=1376139&r2=1376140&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 Aug 22 17:00:24 2012
@@ -169,7 +169,10 @@ public class FileOperations implements G
                 return true;
             } else if (endpoint.getFileExist() == GenericFileExist.Fail) {
                 throw new GenericFileOperationFailedException("File already exist: " + file + ". Cannot write new file.");
-            } 
+            } else if (endpoint.getFileExist() == GenericFileExist.Move) {
+                // move any existing file first
+                doMoveExistingFile(fileName);
+            }
         }
         
         // Do an explicit test for a null body and decide what to do
@@ -262,6 +265,56 @@ public class FileOperations implements G
         }
     }
 
+    /**
+     * Moves any existing file due fileExists=Move is in use.
+     */
+    private void doMoveExistingFile(String fileName) throws GenericFileOperationFailedException {
+        // need to evaluate using a dummy and simulate the file first, to have access to all the file attributes
+        // create a dummy exchange as Exchange is needed for expression evaluation
+        // we support only the following 3 tokens.
+        Exchange dummy = endpoint.createExchange();
+        String parent = FileUtil.onlyPath(fileName);
+        String onlyName = FileUtil.stripPath(fileName);
+        dummy.getIn().setHeader(Exchange.FILE_NAME, fileName);
+        dummy.getIn().setHeader(Exchange.FILE_NAME_ONLY, onlyName);
+        dummy.getIn().setHeader(Exchange.FILE_PARENT, parent);
+
+        String to = endpoint.getMoveExisting().evaluate(dummy, String.class);
+        // we must normalize it (to avoid having both \ and / in the name which confuses java.io.File)
+        to = FileUtil.normalizePath(to);
+        if (ObjectHelper.isEmpty(to)) {
+            throw new GenericFileOperationFailedException("moveExisting evaluated as empty String, cannot move existing file: " + fileName);
+        }
+
+        // ensure any paths is created before we rename as the renamed file may be in a different path (which may be non exiting)
+        // use java.io.File to compute the file path
+        File toFile = new File(to);
+        String directory = toFile.getParent();
+        boolean absolute = FileUtil.isAbsolute(toFile);
+        if (directory != null) {
+            if (!buildDirectory(directory, absolute)) {
+                LOG.debug("Cannot build directory [{}] (could be because of denied permissions)", directory);
+            }
+        }
+
+        // deal if there already exists a file
+        if (existsFile(to)) {
+            if (endpoint.isEagerDeleteTargetFile()) {
+                LOG.trace("Deleting existing file: {}", to);
+                if (!deleteFile(to)) {
+                    throw new GenericFileOperationFailedException("Cannot delete file: " + to);
+                }
+            } else {
+                throw new GenericFileOperationFailedException("Cannot moved existing file from: " + fileName + " to: " + to + " as there already exists a file: " + to);
+            }
+        }
+
+        LOG.trace("Moving existing file: {} to: {}", fileName, to);
+        if (!renameFile(fileName, to)) {
+            throw new GenericFileOperationFailedException("Cannot rename file from: " + fileName + " to: " + to);
+        }
+    }
+
     private void keepLastModified(Exchange exchange, File file) {
         if (endpoint.isKeepLastModified()) {
             Long last;

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java?rev=1376140&r1=1376139&r2=1376140&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java Wed Aug 22 17:00:24 2012
@@ -84,6 +84,7 @@ public abstract class GenericFileEndpoin
     protected Expression move;
     protected Expression moveFailed;
     protected Expression preMove;
+    protected Expression moveExisting;
     protected Boolean idempotent;
     protected IdempotentRepository<String> idempotentRepository;
     protected GenericFileFilter<T> filter;
@@ -344,6 +345,23 @@ public abstract class GenericFileEndpoin
         this.preMove = createFileLanguageExpression(expression);
     }
 
+    public Expression getMoveExisting() {
+        return moveExisting;
+    }
+
+    public void setMoveExisting(Expression moveExisting) {
+        this.moveExisting = moveExisting;
+    }
+
+    /**
+     * Sets the move existing expression based on
+     * {@link org.apache.camel.language.simple.SimpleLanguage}
+     */
+    public void setMoveExisting(String fileLanguageExpression) {
+        String expression = configureMoveOrPreMoveExpression(fileLanguageExpression);
+        this.moveExisting = createFileLanguageExpression(expression);
+    }
+
     public Expression getFileName() {
         return fileName;
     }
@@ -661,8 +679,6 @@ public abstract class GenericFileEndpoin
 
     /**
      * Set up the exchange properties with the options of the file endpoint
-     *
-     * @param exchange
      */
     public void configureExchange(Exchange exchange) {
         // Now we just set the charset property here
@@ -672,8 +688,7 @@ public abstract class GenericFileEndpoin
     }
 
     /**
-     * Strategy to configure the move or premove option based on a String input.
-     * <p/>
+     * Strategy to configure the move, preMove, or moveExisting option based on a String input.
      *
      * @param expression the original string input
      * @return configured string or the original if no modifications is needed

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/GenericFileExist.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/GenericFileExist.java?rev=1376140&r1=1376139&r2=1376140&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/GenericFileExist.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/file/GenericFileExist.java Wed Aug 22 17:00:24 2012
@@ -23,5 +23,5 @@ package org.apache.camel.component.file;
  */
 public enum GenericFileExist {
 
-    Override, Append, Fail, Ignore
+    Override, Append, Fail, Ignore, Move
 }

Added: camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerMoveExistingTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerMoveExistingTest.java?rev=1376140&view=auto
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerMoveExistingTest.java (added)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerMoveExistingTest.java Wed Aug 22 17:00:24 2012
@@ -0,0 +1,114 @@
+/**
+ * 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.CamelExecutionException;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+
+/**
+ *
+ */
+public class FileProducerMoveExistingTest extends ContextTestSupport {
+
+    @Override
+    protected void setUp() throws Exception {
+        deleteDirectory("target/file");
+        super.setUp();
+    }
+
+    public void testExistingFileDoesNotExists() throws Exception {
+        template.sendBodyAndHeader("file://target/file?fileExist=Move&moveExisting=${file:parent}/renamed-${file:onlyname}",
+                "Hello World", Exchange.FILE_NAME, "hello.txt");
+
+        assertFileExists("target/file/hello.txt");
+        assertFileNotExists("target/file/renamed-hello.txt");
+    }
+
+    public void testExistingFileExists() throws Exception {
+        template.sendBodyAndHeader("file://target/file?fileExist=Move&moveExisting=${file:parent}/renamed-${file:onlyname}",
+                "Hello World", Exchange.FILE_NAME, "hello.txt");
+        template.sendBodyAndHeader("file://target/file?fileExist=Move&moveExisting=${file:parent}/renamed-${file:onlyname}",
+                "Bye World", Exchange.FILE_NAME, "hello.txt");
+
+        assertFileExists("target/file/hello.txt");
+        assertEquals("Bye World", context.getTypeConverter().convertTo(String.class, new File("target/file/hello.txt")));
+
+        assertFileExists("target/file/renamed-hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File("target/file/renamed-hello.txt")));
+    }
+
+    public void testExistingFileExistsMoveSubDir() throws Exception {
+        template.sendBodyAndHeader("file://target/file?fileExist=Move&moveExisting=backup", "Hello World", Exchange.FILE_NAME, "hello.txt");
+        template.sendBodyAndHeader("file://target/file?fileExist=Move&moveExisting=backup", "Bye World", Exchange.FILE_NAME, "hello.txt");
+
+        assertFileExists("target/file/hello.txt");
+        assertEquals("Bye World", context.getTypeConverter().convertTo(String.class, new File("target/file/hello.txt")));
+
+        // would move into sub directory and keep existing name as is
+        assertFileExists("target/file/backup/hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File("target/file/backup/hello.txt")));
+    }
+
+    public void testFailOnMoveExistingFileExistsEagerDeleteTrue() throws Exception {
+        template.sendBodyAndHeader("file://target/file", "Old file", Exchange.FILE_NAME, "renamed-hello.txt");
+
+        template.sendBodyAndHeader("file://target/file?fileExist=Move&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=true",
+                "Hello World", Exchange.FILE_NAME, "hello.txt");
+        // we should be okay as we will just delete any existing file
+        template.sendBodyAndHeader("file://target/file?fileExist=Move&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=true",
+                "Bye World", Exchange.FILE_NAME, "hello.txt");
+
+        // we could write the new file so the old context should be there
+        assertFileExists("target/file/hello.txt");
+        assertEquals("Bye World", context.getTypeConverter().convertTo(String.class, new File("target/file/hello.txt")));
+
+        // and the renamed file should be overridden
+        assertFileExists("target/file/renamed-hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File("target/file/renamed-hello.txt")));
+    }
+
+    public void testFailOnMoveExistingFileExistsEagerDeleteFalse() throws Exception {
+        template.sendBodyAndHeader("file://target/file", "Old file", Exchange.FILE_NAME, "renamed-hello.txt");
+
+        template.sendBodyAndHeader("file://target/file?fileExist=Move&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=false",
+                "Hello World", Exchange.FILE_NAME, "hello.txt");
+        try {
+            template.sendBodyAndHeader("file://target/file?fileExist=Move&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=false",
+                    "Bye World", Exchange.FILE_NAME, "hello.txt");
+            fail("Should have thrown an exception");
+        } catch (CamelExecutionException e) {
+            GenericFileOperationFailedException cause = assertIsInstanceOf(GenericFileOperationFailedException.class, e.getCause());
+            assertTrue(cause.getMessage().startsWith("Cannot moved existing file"));
+        }
+
+        // we could not write the new file so the previous context should be there
+        assertFileExists("target/file/hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File("target/file/hello.txt")));
+
+        // and the renamed file should be untouched
+        assertFileExists("target/file/renamed-hello.txt");
+        assertEquals("Old file", context.getTypeConverter().convertTo(String.class, new File("target/file/renamed-hello.txt")));
+    }
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+}

Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerMoveExistingTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/component/file/FileProducerMoveExistingTest.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java?rev=1376140&r1=1376139&r2=1376140&view=diff
==============================================================================
--- camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java (original)
+++ camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java Wed Aug 22 17:00:24 2012
@@ -34,6 +34,7 @@ import org.apache.camel.component.file.F
 import org.apache.camel.component.file.GenericFile;
 import org.apache.camel.component.file.GenericFileEndpoint;
 import org.apache.camel.component.file.GenericFileExist;
+import org.apache.camel.component.file.GenericFileMessage;
 import org.apache.camel.component.file.GenericFileOperationFailedException;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.IOHelper;
@@ -138,7 +139,7 @@ public class FtpOperations implements Re
 
         // must set soTimeout after connect
         if (endpoint instanceof FtpEndpoint) {
-            FtpEndpoint<?> ftpEndpoint = (FtpEndpoint<?>) endpoint;
+            FtpEndpoint ftpEndpoint = (FtpEndpoint) endpoint;
             if (ftpEndpoint.getSoTimeout() > 0) {
                 log.trace("Using SoTimeout=" + ftpEndpoint.getSoTimeout());
                 try {
@@ -487,7 +488,9 @@ public class FtpOperations implements Re
         log.trace("doStoreFile({})", targetName);
 
         // if an existing file already exists what should we do?
-        if (endpoint.getFileExist() == GenericFileExist.Ignore || endpoint.getFileExist() == GenericFileExist.Fail) {
+        if (endpoint.getFileExist() == GenericFileExist.Ignore
+                || endpoint.getFileExist() == GenericFileExist.Fail
+                || endpoint.getFileExist() == GenericFileExist.Move) {
             boolean existFile = existsFile(targetName);
             if (existFile && endpoint.getFileExist() == GenericFileExist.Ignore) {
                 // ignore but indicate that the file was written
@@ -495,6 +498,9 @@ public class FtpOperations implements Re
                 return true;
             } else if (existFile && endpoint.getFileExist() == GenericFileExist.Fail) {
                 throw new GenericFileOperationFailedException("File already exist: " + name + ". Cannot write new file.");
+            } else if (existFile && endpoint.getFileExist() == GenericFileExist.Move) {
+                // move any existing file first
+                doMoveExistingFile(name, targetName);
             }
         }
 
@@ -529,6 +535,61 @@ public class FtpOperations implements Re
         }
     }
 
+    /**
+     * Moves any existing file due fileExists=Move is in use.
+     */
+    private void doMoveExistingFile(String name, String targetName) throws GenericFileOperationFailedException {
+        // need to evaluate using a dummy and simulate the file first, to have access to all the file attributes
+        // create a dummy exchange as Exchange is needed for expression evaluation
+        // we support only the following 3 tokens.
+        Exchange dummy = endpoint.createExchange();
+        // we only support relative paths for the ftp component, so dont provide any parent
+        String parent = null;
+        String onlyName = FileUtil.stripPath(targetName);
+        dummy.getIn().setHeader(Exchange.FILE_NAME, targetName);
+        dummy.getIn().setHeader(Exchange.FILE_NAME_ONLY, onlyName);
+        dummy.getIn().setHeader(Exchange.FILE_PARENT, parent);
+
+        String to = endpoint.getMoveExisting().evaluate(dummy, String.class);
+        // we only support relative paths for the ftp component, so strip any leading paths
+        to = FileUtil.stripLeadingSeparator(to);
+        // normalize accordingly to configuration
+        to = endpoint.getConfiguration().normalizePath(to);
+        if (ObjectHelper.isEmpty(to)) {
+            throw new GenericFileOperationFailedException("moveExisting evaluated as empty String, cannot move existing file: " + name);
+        }
+
+        // do we have a sub directory
+        String dir = FileUtil.onlyPath(to);
+        if (dir != null) {
+            // ensure directory exists
+            buildDirectory(dir, false);
+        }
+
+        // deal if there already exists a file
+        if (existsFile(to)) {
+            if (endpoint.isEagerDeleteTargetFile()) {
+                log.trace("Deleting existing file: {}", to);
+                boolean result;
+                try {
+                    result = client.deleteFile(to);
+                    if (!result) {
+                        throw new GenericFileOperationFailedException("Cannot delete file: " + to);
+                    }
+                } catch (IOException e) {
+                    throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), "Cannot delete file: " + to, e);
+                }
+            } else {
+                throw new GenericFileOperationFailedException("Cannot moved existing file from: " + name + " to: " + to + " as there already exists a file: " + to);
+            }
+        }
+
+        log.trace("Moving existing file: {} to: {}", name, to);
+        if (!renameFile(targetName, to)) {
+            throw new GenericFileOperationFailedException("Cannot rename file from: " + name + " to: " + to);
+        }
+    }
+
     public boolean existsFile(String name) throws GenericFileOperationFailedException {
         log.trace("existsFile({})", name);
         if (endpoint.isFastExistsCheck()) {

Modified: camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java?rev=1376140&r1=1376139&r2=1376140&view=diff
==============================================================================
--- camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java (original)
+++ camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java Wed Aug 22 17:00:24 2012
@@ -22,6 +22,7 @@ import org.apache.camel.Exchange;
 import org.apache.camel.Processor;
 import org.apache.camel.component.file.GenericFile;
 import org.apache.camel.component.file.GenericFileEndpoint;
+import org.apache.camel.component.file.GenericFileExist;
 import org.apache.camel.component.file.GenericFileProducer;
 import org.apache.camel.impl.DefaultExchange;
 import org.apache.camel.processor.idempotent.MemoryIdempotentRepository;
@@ -69,6 +70,14 @@ public abstract class RemoteFileEndpoint
     @Override
     public GenericFileProducer<T> createProducer() throws Exception {
         afterPropertiesSet();
+
+        // ensure fileExist and moveExisting is configured correctly if in use
+        if (getFileExist() == GenericFileExist.Move && getMoveExisting() == null) {
+            throw new IllegalArgumentException("You must configure moveExisting option when fileExist=Move");
+        } else if (getMoveExisting() != null && getFileExist() != GenericFileExist.Move) {
+            throw new IllegalArgumentException("You must configure fileExist=Move when moveExisting has been set");
+        }
+
         return buildProducer();
     }
 

Modified: camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java?rev=1376140&r1=1376139&r2=1376140&view=diff
==============================================================================
--- camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java (original)
+++ camel/trunk/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java Wed Aug 22 17:00:24 2012
@@ -661,7 +661,9 @@ public class SftpOperations implements R
         LOG.trace("doStoreFile({})", targetName);
 
         // if an existing file already exists what should we do?
-        if (endpoint.getFileExist() == GenericFileExist.Ignore || endpoint.getFileExist() == GenericFileExist.Fail) {
+        if (endpoint.getFileExist() == GenericFileExist.Ignore
+                || endpoint.getFileExist() == GenericFileExist.Fail
+                || endpoint.getFileExist() == GenericFileExist.Move) {
             boolean existFile = existsFile(targetName);
             if (existFile && endpoint.getFileExist() == GenericFileExist.Ignore) {
                 // ignore but indicate that the file was written
@@ -669,6 +671,9 @@ public class SftpOperations implements R
                 return true;
             } else if (existFile && endpoint.getFileExist() == GenericFileExist.Fail) {
                 throw new GenericFileOperationFailedException("File already exist: " + name + ". Cannot write new file.");
+            } else if (existFile && endpoint.getFileExist() == GenericFileExist.Move) {
+                // move any existing file first
+                doMoveExistingFile(name, targetName);
             }
         }
 
@@ -716,6 +721,53 @@ public class SftpOperations implements R
         }
     }
 
+    /**
+     * Moves any existing file due fileExists=Move is in use.
+     */
+    private void doMoveExistingFile(String name, String targetName) throws GenericFileOperationFailedException {
+        // need to evaluate using a dummy and simulate the file first, to have access to all the file attributes
+        // create a dummy exchange as Exchange is needed for expression evaluation
+        // we support only the following 3 tokens.
+        Exchange dummy = endpoint.createExchange();
+        // we only support relative paths for the ftp component, so dont provide any parent
+        String parent = null;
+        String onlyName = FileUtil.stripPath(targetName);
+        dummy.getIn().setHeader(Exchange.FILE_NAME, targetName);
+        dummy.getIn().setHeader(Exchange.FILE_NAME_ONLY, onlyName);
+        dummy.getIn().setHeader(Exchange.FILE_PARENT, parent);
+
+        String to = endpoint.getMoveExisting().evaluate(dummy, String.class);
+        // we only support relative paths for the ftp component, so strip any leading paths
+        to = FileUtil.stripLeadingSeparator(to);
+        // normalize accordingly to configuration
+        to = endpoint.getConfiguration().normalizePath(to);
+        if (ObjectHelper.isEmpty(to)) {
+            throw new GenericFileOperationFailedException("moveExisting evaluated as empty String, cannot move existing file: " + name);
+        }
+
+        // do we have a sub directory
+        String dir = FileUtil.onlyPath(to);
+        if (dir != null) {
+            // ensure directory exists
+            buildDirectory(dir, false);
+        }
+
+        // deal if there already exists a file
+        if (existsFile(to)) {
+            if (endpoint.isEagerDeleteTargetFile()) {
+                LOG.trace("Deleting existing file: {}", to);
+                deleteFile(to);
+            } else {
+                throw new GenericFileOperationFailedException("Cannot moved existing file from: " + name + " to: " + to + " as there already exists a file: " + to);
+            }
+        }
+
+        LOG.trace("Moving existing file: {} to: {}", name, to);
+        if (!renameFile(targetName, to)) {
+            throw new GenericFileOperationFailedException("Cannot rename file from: " + name + " to: " + to);
+        }
+    }
+
     public boolean existsFile(String name) throws GenericFileOperationFailedException {
         LOG.trace("existsFile({})", name);
         if (endpoint.isFastExistsCheck()) {

Added: camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpProducerMoveExistingTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpProducerMoveExistingTest.java?rev=1376140&view=auto
==============================================================================
--- camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpProducerMoveExistingTest.java (added)
+++ camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpProducerMoveExistingTest.java Wed Aug 22 17:00:24 2012
@@ -0,0 +1,111 @@
+/**
+ * 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.remote;
+
+import java.io.File;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.component.file.GenericFileOperationFailedException;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class FtpProducerMoveExistingTest extends FtpServerTestSupport {
+
+    private String getFtpUrl() {
+        return "ftp://admin@localhost:" + getPort() + "/move?password=admin&fileExist=Move";
+    }
+
+    @Test
+    public void testExistingFileDoesNotExists() throws Exception {
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}", "Hello World", Exchange.FILE_NAME, "hello.txt");
+
+        assertFileExists(FTP_ROOT_DIR + "/move/hello.txt");
+        assertFileNotExists(FTP_ROOT_DIR + "/move/renamed-hello.txt");
+    }
+
+    @Test
+    public void testExistingFileExists() throws Exception {
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}", "Hello World", Exchange.FILE_NAME, "hello.txt");
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}", "Bye World", Exchange.FILE_NAME, "hello.txt");
+
+        assertFileExists(FTP_ROOT_DIR + "/move/hello.txt");
+        assertEquals("Bye World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/hello.txt")));
+
+        assertFileExists(FTP_ROOT_DIR + "/move/renamed-hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/renamed-hello.txt")));
+    }
+
+    @Test
+    public void testExistingFileExistsMoveSubDir() throws Exception {
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=backup", "Hello World", Exchange.FILE_NAME, "hello.txt");
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=backup", "Bye World", Exchange.FILE_NAME, "hello.txt");
+
+        assertFileExists(FTP_ROOT_DIR + "/move/hello.txt");
+        assertEquals("Bye World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/hello.txt")));
+
+        // would move into sub directory and keep existing name as is
+        assertFileExists(FTP_ROOT_DIR + "/move/backup/hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/backup/hello.txt")));
+    }
+
+    @Test
+    public void testFailOnMoveExistingFileExistsEagerDeleteTrue() throws Exception {
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=true", "Old file", Exchange.FILE_NAME, "renamed-hello.txt");
+
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=true", "Hello World", Exchange.FILE_NAME, "hello.txt");
+        // we should be okay as we will just delete any existing file
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=true", "Bye World", Exchange.FILE_NAME, "hello.txt");
+
+        // we could write the new file so the old context should be there
+        assertFileExists(FTP_ROOT_DIR + "/move/hello.txt");
+        assertEquals("Bye World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/hello.txt")));
+
+        // and the renamed file should be overridden
+        assertFileExists(FTP_ROOT_DIR + "/move/renamed-hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/renamed-hello.txt")));
+    }
+
+    @Test
+    public void testFailOnMoveExistingFileExistsEagerDeleteFalse() throws Exception {
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=true", "Old file", Exchange.FILE_NAME, "renamed-hello.txt");
+
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=false", "Hello World", Exchange.FILE_NAME, "hello.txt");
+        try {
+            template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=false", "Bye World", Exchange.FILE_NAME, "hello.txt");
+            fail("Should have thrown an exception");
+        } catch (CamelExecutionException e) {
+            GenericFileOperationFailedException cause = assertIsInstanceOf(GenericFileOperationFailedException.class, e.getCause());
+            assertTrue(cause.getMessage().startsWith("Cannot moved existing file"));
+        }
+
+        // we could not write the new file so the previous context should be there
+        assertFileExists(FTP_ROOT_DIR + "/move/hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/hello.txt")));
+
+        // and the renamed file should be untouched
+        assertFileExists(FTP_ROOT_DIR + "/move/renamed-hello.txt");
+        assertEquals("Old file", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/renamed-hello.txt")));
+    }
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+}

Propchange: camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpProducerMoveExistingTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpProducerMoveExistingTest.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/sftp/SftpProducerMoveExistingTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/sftp/SftpProducerMoveExistingTest.java?rev=1376140&view=auto
==============================================================================
--- camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/sftp/SftpProducerMoveExistingTest.java (added)
+++ camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/sftp/SftpProducerMoveExistingTest.java Wed Aug 22 17:00:24 2012
@@ -0,0 +1,131 @@
+/**
+ * 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.remote.sftp;
+
+import java.io.File;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.component.file.GenericFileOperationFailedException;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class SftpProducerMoveExistingTest extends SftpServerTestSupport {
+
+    private String getFtpUrl() {
+        return "sftp://admin@localhost:" + getPort() + "/" + FTP_ROOT_DIR + "/move?password=admin&fileExist=Move";
+    }
+
+    @Test
+    public void testExistingFileDoesNotExists() throws Exception {
+        if (!canTest()) {
+            return;
+        }
+
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}", "Hello World", Exchange.FILE_NAME, "hello.txt");
+
+        assertFileExists(FTP_ROOT_DIR + "/move/hello.txt");
+        assertFileNotExists(FTP_ROOT_DIR + "/move/renamed-hello.txt");
+    }
+
+    @Test
+    public void testExistingFileExists() throws Exception {
+        if (!canTest()) {
+            return;
+        }
+
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}", "Hello World", Exchange.FILE_NAME, "hello.txt");
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}", "Bye World", Exchange.FILE_NAME, "hello.txt");
+
+        assertFileExists(FTP_ROOT_DIR + "/move/hello.txt");
+        assertEquals("Bye World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/hello.txt")));
+
+        assertFileExists(FTP_ROOT_DIR + "/move/renamed-hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/renamed-hello.txt")));
+    }
+
+    @Test
+    public void testExistingFileExistsMoveSubDir() throws Exception {
+        if (!canTest()) {
+            return;
+        }
+
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=backup", "Hello World", Exchange.FILE_NAME, "hello.txt");
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=backup", "Bye World", Exchange.FILE_NAME, "hello.txt");
+
+        assertFileExists(FTP_ROOT_DIR + "/move/hello.txt");
+        assertEquals("Bye World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/hello.txt")));
+
+        // would move into sub directory and keep existing name as is
+        assertFileExists(FTP_ROOT_DIR + "/move/backup/hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/backup/hello.txt")));
+    }
+
+    @Test
+    public void testFailOnMoveExistingFileExistsEagerDeleteTrue() throws Exception {
+        if (!canTest()) {
+            return;
+        }
+
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=true", "Old file", Exchange.FILE_NAME, "renamed-hello.txt");
+
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=true", "Hello World", Exchange.FILE_NAME, "hello.txt");
+        // we should be okay as we will just delete any existing file
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=true", "Bye World", Exchange.FILE_NAME, "hello.txt");
+
+        // we could write the new file so the old context should be there
+        assertFileExists(FTP_ROOT_DIR + "/move/hello.txt");
+        assertEquals("Bye World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/hello.txt")));
+
+        // and the renamed file should be overridden
+        assertFileExists(FTP_ROOT_DIR + "/move/renamed-hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/renamed-hello.txt")));
+    }
+
+    @Test
+    public void testFailOnMoveExistingFileExistsEagerDeleteFalse() throws Exception {
+        if (!canTest()) {
+            return;
+        }
+
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=true", "Old file", Exchange.FILE_NAME, "renamed-hello.txt");
+
+        template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=false", "Hello World", Exchange.FILE_NAME, "hello.txt");
+        try {
+            template.sendBodyAndHeader(getFtpUrl() + "&moveExisting=${file:parent}/renamed-${file:onlyname}&eagerDeleteTargetFile=false", "Bye World", Exchange.FILE_NAME, "hello.txt");
+            fail("Should have thrown an exception");
+        } catch (CamelExecutionException e) {
+            GenericFileOperationFailedException cause = assertIsInstanceOf(GenericFileOperationFailedException.class, e.getCause());
+            assertTrue(cause.getMessage().startsWith("Cannot moved existing file"));
+        }
+
+        // we could not write the new file so the previous context should be there
+        assertFileExists(FTP_ROOT_DIR + "/move/hello.txt");
+        assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/hello.txt")));
+
+        // and the renamed file should be untouched
+        assertFileExists(FTP_ROOT_DIR + "/move/renamed-hello.txt");
+        assertEquals("Old file", context.getTypeConverter().convertTo(String.class, new File(FTP_ROOT_DIR + "/move/renamed-hello.txt")));
+    }
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+}

Propchange: camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/sftp/SftpProducerMoveExistingTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/sftp/SftpProducerMoveExistingTest.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date