diff --git a/camel-core/src/main/java/org/apache/camel/component/file/GenericFileProducer.java b/camel-core/src/main/java/org/apache/camel/component/file/GenericFileProducer.java index bd0a24888fc94..47b87133d01ad 100644 --- a/camel-core/src/main/java/org/apache/camel/component/file/GenericFileProducer.java +++ b/camel-core/src/main/java/org/apache/camel/component/file/GenericFileProducer.java @@ -111,93 +111,95 @@ protected void processExchange(Exchange exchange, String target) throws Exceptio log.trace("Processing file: {} for exchange: {}", target, exchange); try { - preWriteCheck(); - - // should we write to a temporary name and then afterwards rename to real target - boolean writeAsTempAndRename = ObjectHelper.isNotEmpty(endpoint.getTempFileName()); - String tempTarget = null; - // remember if target exists to avoid checking twice - Boolean targetExists = null; - if (writeAsTempAndRename) { - // compute temporary name with the temp prefix - tempTarget = createTempFileName(exchange, target); - - log.trace("Writing using tempNameFile: {}", tempTarget); - - // cater for file exists option on the real target as - // the file operations code will work on the temp file - - // if an existing file already exists what should we do? - targetExists = operations.existsFile(target); - if (targetExists) { - if (endpoint.getFileExist() == GenericFileExist.Ignore) { - // ignore but indicate that the file was written - log.trace("An existing file already exists: {}. Ignore and do not override it.", target); - return; - } else if (endpoint.getFileExist() == GenericFileExist.Fail) { - throw new GenericFileOperationFailedException("File already exist: " + target + ". Cannot write new file."); - } else if (endpoint.isEagerDeleteTargetFile() && endpoint.getFileExist() == GenericFileExist.Override) { - // we override the target so we do this by deleting it so the temp file can be renamed later - // with success as the existing target file have been deleted - log.trace("Eagerly deleting existing file: {}", target); - if (!operations.deleteFile(target)) { - throw new GenericFileOperationFailedException("Cannot delete file: " + target); + preWriteCheck(exchange); + + if (isUploadFile()) { + // should we write to a temporary name and then afterwards rename to real target + boolean writeAsTempAndRename = ObjectHelper.isNotEmpty(endpoint.getTempFileName()); + String tempTarget = null; + // remember if target exists to avoid checking twice + Boolean targetExists = null; + if (writeAsTempAndRename) { + // compute temporary name with the temp prefix + tempTarget = createTempFileName(exchange, target); + + log.trace("Writing using tempNameFile: {}", tempTarget); + + // cater for file exists option on the real target as + // the file operations code will work on the temp file + + // if an existing file already exists what should we do? + targetExists = operations.existsFile(target); + if (targetExists) { + if (endpoint.getFileExist() == GenericFileExist.Ignore) { + // ignore but indicate that the file was written + log.trace("An existing file already exists: {}. Ignore and do not override it.", target); + return; + } else if (endpoint.getFileExist() == GenericFileExist.Fail) { + throw new GenericFileOperationFailedException("File already exist: " + target + ". Cannot write new file."); + } else if (endpoint.isEagerDeleteTargetFile() && endpoint.getFileExist() == GenericFileExist.Override) { + // we override the target so we do this by deleting it so the temp file can be renamed later + // with success as the existing target file have been deleted + log.trace("Eagerly deleting existing file: {}", target); + if (!operations.deleteFile(target)) { + throw new GenericFileOperationFailedException("Cannot delete file: " + target); + } } } - } - // delete any pre existing temp file - if (operations.existsFile(tempTarget)) { - log.trace("Deleting existing temp file: {}", tempTarget); - if (!operations.deleteFile(tempTarget)) { - throw new GenericFileOperationFailedException("Cannot delete file: " + tempTarget); + // delete any pre existing temp file + if (operations.existsFile(tempTarget)) { + log.trace("Deleting existing temp file: {}", tempTarget); + if (!operations.deleteFile(tempTarget)) { + throw new GenericFileOperationFailedException("Cannot delete file: " + tempTarget); + } } } - } - // write/upload the file - writeFile(exchange, tempTarget != null ? tempTarget : target); - - // if we did write to a temporary name then rename it to the real - // name after we have written the file - if (tempTarget != null) { - - // if we should not eager delete the target file then do it now just before renaming - if (!endpoint.isEagerDeleteTargetFile() && targetExists - && endpoint.getFileExist() == GenericFileExist.Override) { - // we override the target so we do this by deleting it so the temp file can be renamed later - // with success as the existing target file have been deleted - log.trace("Deleting existing file: {}", target); - if (!operations.deleteFile(target)) { - throw new GenericFileOperationFailedException("Cannot delete file: " + target); + // write/upload the file + writeFile(exchange, tempTarget != null ? tempTarget : target); + + // if we did write to a temporary name then rename it to the real + // name after we have written the file + if (tempTarget != null) { + + // if we should not eager delete the target file then do it now just before renaming + if (!endpoint.isEagerDeleteTargetFile() && targetExists + && endpoint.getFileExist() == GenericFileExist.Override) { + // we override the target so we do this by deleting it so the temp file can be renamed later + // with success as the existing target file have been deleted + log.trace("Deleting existing file: {}", target); + if (!operations.deleteFile(target)) { + throw new GenericFileOperationFailedException("Cannot delete file: " + target); + } } - } - // now we are ready to rename the temp file to the target file - log.trace("Renaming file: [{}] to: [{}]", tempTarget, target); - boolean renamed = operations.renameFile(tempTarget, target); - if (!renamed) { - throw new GenericFileOperationFailedException("Cannot rename file from: " + tempTarget + " to: " + target); + // now we are ready to rename the temp file to the target file + log.trace("Renaming file: [{}] to: [{}]", tempTarget, target); + boolean renamed = operations.renameFile(tempTarget, target); + if (!renamed) { + throw new GenericFileOperationFailedException("Cannot rename file from: " + tempTarget + " to: " + target); + } } - } - // any done file to write? - if (endpoint.getDoneFileName() != null) { - String doneFileName = endpoint.createDoneFileName(target); - ObjectHelper.notEmpty(doneFileName, "doneFileName", endpoint); + // any done file to write? + if (endpoint.getDoneFileName() != null) { + String doneFileName = endpoint.createDoneFileName(target); + ObjectHelper.notEmpty(doneFileName, "doneFileName", endpoint); - // create empty exchange with empty body to write as the done file - Exchange empty = new DefaultExchange(exchange); - empty.getIn().setBody(""); + // create empty exchange with empty body to write as the done file + Exchange empty = new DefaultExchange(exchange); + empty.getIn().setBody(""); - log.trace("Writing done file: [{}]", doneFileName); - // delete any existing done file - if (operations.existsFile(doneFileName)) { - if (!operations.deleteFile(doneFileName)) { - throw new GenericFileOperationFailedException("Cannot delete existing done file: " + doneFileName); + log.trace("Writing done file: [{}]", doneFileName); + // delete any existing done file + if (operations.existsFile(doneFileName)) { + if (!operations.deleteFile(doneFileName)) { + throw new GenericFileOperationFailedException("Cannot delete existing done file: " + doneFileName); + } } + writeFile(empty, doneFileName); } - writeFile(empty, doneFileName); } // let's store the name we really used in the header, so end-users @@ -210,6 +212,15 @@ protected void processExchange(Exchange exchange, String target) throws Exceptio postWriteCheck(); } + /** + * Override if required. Files are actually sent / returns true by default + * + * @return true to send files, false to skip sending files. + */ + protected boolean isUploadFile() { + return true; + } + /** * If we fail writing out a file, we will call this method. This hook is * provided to disconnect from servers or clean up files we created (if needed). @@ -221,7 +232,7 @@ public void handleFailedWrite(Exchange exchange, Exception exception) throws Exc /** * Perform any actions that need to occur before we write such as connecting to an FTP server etc. */ - public void preWriteCheck() throws Exception { + public void preWriteCheck(Exchange exchange) throws Exception { // nothing needed to check } diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java index 4cbc4ddb32390..0522aad803e36 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java @@ -167,8 +167,13 @@ public boolean connect(RemoteFileConfiguration configuration) throws GenericFile throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e); } - // site commands - if (endpoint.getConfiguration().getSiteCommand() != null) { + return true; + } + + public void sendSiteCommands(RemoteFileConfiguration configuration, Exchange exchange) throws GenericFileOperationFailedException { + if (configuration.getSiteCommand() != null) { + final boolean captureOutput = exchange != null && configuration.isSiteCommandCapture(); + final List siteOutput = new ArrayList(); // commands can be separated using new line Iterator it = ObjectHelper.createIterator(endpoint.getConfiguration().getSiteCommand(), "\n"); while (it.hasNext()) { @@ -177,14 +182,21 @@ public boolean connect(RemoteFileConfiguration configuration) throws GenericFile log.trace("Site command to send: {}", command); if (command != null) { boolean result = sendSiteCommand(command); + + if (captureOutput) { + String reply = getFtpClient().getReplyString(); + siteOutput.add( reply ); + } + if (!result) { throw new GenericFileOperationFailedException("Site command: " + command + " returned false"); } } } + if ( captureOutput ) { + exchange.getIn().setBody(siteOutput); + } } - - return true; } public boolean isConnected() throws GenericFileOperationFailedException { diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileConfiguration.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileConfiguration.java index e5325bacfa677..89f3e3c75c3b6 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileConfiguration.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileConfiguration.java @@ -47,6 +47,7 @@ public enum PathSeparator { UNIX, Windows, Auto }; private int soTimeout; private boolean throwExceptionOnConnectFailed; private String siteCommand; + private boolean siteCommandCapture = false; private boolean stepwise = true; private PathSeparator separator = PathSeparator.Auto; private boolean streamDownload; @@ -57,7 +58,7 @@ public RemoteFileConfiguration() { public RemoteFileConfiguration(URI uri) { configure(uri); } - + @Override public boolean needToNormalize() { return false; @@ -230,6 +231,25 @@ public void setSiteCommand(String siteCommand) { this.siteCommand = siteCommand; } + public boolean isSiteCommandCapture() { + return siteCommandCapture; + } + + /** + * Capture the output of the site commands. The output of the site commands will be returned + * in the exchange's body as a List with the output of each executed site command + * as a list element in order of execution. The order of execution is defined by the order + * of the site commands in the siteCommand option (each separated by a newline character). + * If false, the body remains untouched. + *

+ * The default is false. + * + * @param siteCommandCapture if true, capture site command output. + */ + public void setSiteCommandCapture(boolean siteCommandCapture) { + this.siteCommandCapture = siteCommandCapture; + } + public boolean isStepwise() { return stepwise; } @@ -261,7 +281,7 @@ public PathSeparator getSeparator() { public void setSeparator(PathSeparator separator) { this.separator = separator; } - + public boolean isStreamDownload() { return streamDownload; } @@ -271,7 +291,7 @@ public boolean isStreamDownload() { * the remote files are streamed to the route as they are read. When set to false, the remote files * are loaded into memory before being sent into the route. * - * @param streamDownload + * @param streamDownload */ public void setStreamDownload(boolean streamDownload) { this.streamDownload = streamDownload; diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileConsumer.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileConsumer.java index e347c7dcc1f68..0669ccccd2053 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileConsumer.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileConsumer.java @@ -55,6 +55,7 @@ protected boolean prePollCheck() throws Exception { } else { connectIfNecessary(); } + getOperations().sendSiteCommands( getEndpoint().getConfiguration(), null ); } catch (Exception e) { loggedIn = false; diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java index 1951143ad36bf..895573ef55238 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java @@ -38,6 +38,7 @@ public abstract class RemoteFileEndpoint extends GenericFileEndpoint { private boolean disconnect; private boolean fastExistsCheck; private boolean download = true; + private boolean upload = true; public RemoteFileEndpoint() { // no args constructor for spring bean endpoint configuration @@ -208,4 +209,12 @@ public boolean isDownload() { public void setDownload(boolean download) { this.download = download; } + + public boolean isUpload() { + return this.upload; + } + + public void setUpload(boolean upload) { + this.upload = upload; + } } diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileOperations.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileOperations.java index 55573cbe48232..116a402348a25 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileOperations.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileOperations.java @@ -16,6 +16,7 @@ */ package org.apache.camel.component.file.remote; +import org.apache.camel.Exchange; import org.apache.camel.component.file.GenericFileOperationFailedException; import org.apache.camel.component.file.GenericFileOperations; @@ -65,4 +66,17 @@ public interface RemoteFileOperations extends GenericFileOperations { */ boolean sendSiteCommand(String command) throws GenericFileOperationFailedException; + /** + * Sends the site commands to the remote server which are specified in the + * configuration option siteCommand. If option siteCommandCapture is true, + * the output of each site command will be captured and returned as a List + * in the body. + *

+ * Works with the producer only. + * + * @param configuration the configuration + * @param exchange the exchange; if null, output will not be captured + * @throws GenericFileOperationFailedException can be thrown + */ + void sendSiteCommands(RemoteFileConfiguration configuration, Exchange exchange) throws GenericFileOperationFailedException; } diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileProducer.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileProducer.java index 87882aad16949..00bca04b28837 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileProducer.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/RemoteFileProducer.java @@ -89,7 +89,7 @@ public void disconnect() throws GenericFileOperationFailedException { } @Override - public void preWriteCheck() throws Exception { + public void preWriteCheck(Exchange exchange) throws Exception { // before writing send a noop to see if the connection is alive and works boolean noop = false; if (loggedIn) { @@ -114,6 +114,7 @@ public void preWriteCheck() throws Exception { } else { connectIfNecessary(); } + getOperations().sendSiteCommands(getEndpoint().getConfiguration(), exchange); } catch (Exception e) { loggedIn = false; @@ -136,6 +137,11 @@ public void postWriteCheck() { } } + @Override + protected boolean isUploadFile() { + return getEndpoint().isUpload(); + } + @Override protected void doStart() throws Exception { log.debug("Starting"); diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java index 68da99e7a8da1..60460356404a4 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java @@ -867,4 +867,9 @@ public boolean sendSiteCommand(String command) throws GenericFileOperationFailed // is not implemented return true; } + + @Override + public void sendSiteCommands(RemoteFileConfiguration configuration, Exchange exchange) throws GenericFileOperationFailedException { + // is not implemented + } } diff --git a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpProducerSiteCommandTest.java b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpProducerSiteCommandTest.java index 064e2c2e8b793..be100881f1745 100644 --- a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpProducerSiteCommandTest.java +++ b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpProducerSiteCommandTest.java @@ -17,19 +17,61 @@ package org.apache.camel.component.file.remote; import java.io.File; +import java.util.List; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; import org.apache.camel.converter.IOConverter; import org.junit.Test; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.startsWith; + public class FtpProducerSiteCommandTest extends FtpServerTestSupport { + private String getFtpUrl() { + return "ftp://admin@localhost:" + getPort() + "/site?password=admin&siteCommand=help site"; + } + @Override - public boolean isUseRouteBuilder() { - return false; + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:callSiteCommandWithoutUpload") + .to("ftp://admin@localhost:" + getPort() + "/site?password=admin&siteCommand=STAT&upload=false"); + + from("direct:callSiteCommandAndCaptureOutput") + .inOut("ftp://admin@localhost:" + getPort() + "/site?password=admin&siteCommand=STAT&upload=false&siteCommandCapture=true"); + + from("direct:callTwoSiteCommands") + .inOut("ftp://admin@localhost:" + getPort() + "/site?password=admin&siteCommand=STAT%0AZONE&upload=false&siteCommandCapture=true"); + } + }; } - private String getFtpUrl() { - return "ftp://admin@localhost:" + getPort() + "/site?password=admin&siteCommand=help site"; + @Test + public void testSiteCommandWithoutUploadingFile() throws Exception { + template.sendBodyAndHeader("direct:callSiteCommandWithoutUpload", "Hello world", Exchange.FILE_NAME, "hello.txt"); + + File file = new File(FTP_ROOT_DIR + "/site/hello.txt"); + assertFalse("No file should be uploaded", file.exists()); + } + + @Test + public void testCaptureSiteCommandOutput() throws Exception { + List output = template.requestBody("direct:callSiteCommandAndCaptureOutput", null, List.class); + + assertThat("Body should contain one line of site command output", output.size(), is(1)); + assertThat("Site command output should start with response code 200", output.get(0), startsWith("200")); + } + + @Test + public void testCallsTwoSiteCommands() throws Exception { + List output = template.requestBody("direct:callTwoSiteCommands", null, List.class); + System.out.println(output.get(0)); + System.out.println(output.get(1)); + assertThat("Body should contain one line of site command output", output.size(), is(2)); } @Test @@ -37,8 +79,7 @@ public void testSiteCommand() throws Exception { sendFile(getFtpUrl(), "Hello World", "hello.txt"); File file = new File(FTP_ROOT_DIR + "/site/hello.txt"); - assertTrue("The uploaded file should exists", file.exists()); + assertTrue("The uploaded file should exist", file.exists()); assertEquals("Hello World", IOConverter.toString(file, null)); } - } \ No newline at end of file diff --git a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpServerTestSupport.java b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpServerTestSupport.java index 7df7a9296b1e1..7c695507f91df 100644 --- a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpServerTestSupport.java +++ b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpServerTestSupport.java @@ -38,7 +38,7 @@ public abstract class FtpServerTestSupport extends BaseServerTestSupport { protected static final String FTP_ROOT_DIR = "./target/res/home"; - protected static final File USERS_FILE = new File("./src/test/resources/users.properties"); + protected static final File USERS_FILE = new File(FtpServerTestSupport.class.getResource( "/users.properties" ).getFile()); protected static final String DEFAULT_LISTENER = "default"; protected FtpServer ftpServer; diff --git a/components/camel-jsch/src/main/java/org/apache/camel/component/jsch/ScpOperations.java b/components/camel-jsch/src/main/java/org/apache/camel/component/jsch/ScpOperations.java index 4bea7fb2988c2..32ae1fcd686b5 100644 --- a/components/camel-jsch/src/main/java/org/apache/camel/component/jsch/ScpOperations.java +++ b/components/camel-jsch/src/main/java/org/apache/camel/component/jsch/ScpOperations.java @@ -212,7 +212,12 @@ public boolean sendSiteCommand(String command) throws GenericFileOperationFailed // TODO: not really used, maybe implement at a later time return true; } - + + @Override + public void sendSiteCommands(RemoteFileConfiguration configuration, Exchange exchange) throws GenericFileOperationFailedException { + // TODO: not really used, maybe implement at a later time + } + private Session createSession(ScpConfiguration config) { ObjectHelper.notNull(config, "ScpConfiguration"); try {