I am trying to route a file to an SFTP server using The Camel Java DSL as follows:
.recipientList(simple("sftp://{{hostname}}:{{port}}/" + exchangeProperty(destinationDir) + "?username={{username}}&preferredAuthentications=publickey&privateKeyFile={{pkfilelocation}}&privateKeyPassphrase={{pkPassphrase}}"))
When the message gets to this endpoint however, Camel is throwing the following exception:
org.apache.camel.component.file.GenericFileOperationFailedException: Cannot change directory to: exchangeProperty{destinationDir}
at org.apache.camel.component.file.remote.SftpOperations.doChangeDirectory(SftpOperations.java:596)
at org.apache.camel.component.file.remote.SftpOperations.changeCurrentDirectory(SftpOperations.java:584)
at org.apache.camel.component.file.remote.SftpOperations.storeFile(SftpOperations.java:830)
at org.apache.camel.component.file.GenericFileProducer.writeFile(GenericFileProducer.java:277)
at org.apache.camel.component.file.GenericFileProducer.processExchange(GenericFileProducer.java:165)
at org.apache.camel.component.file.remote.RemoteFileProducer.process(RemoteFileProducer.java:58)
at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:110)
at org.apache.camel.builder.NoErrorHandlerBuilder$1.process(NoErrorHandlerBuilder.java:40)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
at org.apache.camel.processor.MulticastProcessor.doProcessSequential(MulticastProcessor.java:695)
at org.apache.camel.processor.MulticastProcessor.doProcessSequential(MulticastProcessor.java:623)
at org.apache.camel.processor.MulticastProcessor.process(MulticastProcessor.java:247)
at org.apache.camel.processor.RecipientList.sendToRecipientList(RecipientList.java:172)
at org.apache.camel.processor.RecipientList.process(RecipientList.java:132)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:120)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:83)
at org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:77)
at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:110)
at org.apache.camel.builder.NoErrorHandlerBuilder$1.process(NoErrorHandlerBuilder.java:40)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:120)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:83)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:62)
at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:145)
at org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:77)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:541)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:541)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
at org.apache.camel.processor.MulticastProcessor.doProcessSequential(MulticastProcessor.java:695)
at org.apache.camel.processor.MulticastProcessor.doProcessSequential(MulticastProcessor.java:623)
at org.apache.camel.processor.MulticastProcessor.process(MulticastProcessor.java:247)
at org.apache.camel.processor.Splitter.process(Splitter.java:114)
at org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:77)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:541)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:120)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:83)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:97)
at org.apache.camel.component.jms.EndpointMessageListener.onMessage(EndpointMessageListener.java:112)
at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:721)
at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:681)
at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:651)
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:317)
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:255)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1166)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1158)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1055)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
Caused by: 2: No such file
at com.jcraft.jsch.ChannelSftp.throwStatusError(ChannelSftp.java:2873)
at com.jcraft.jsch.ChannelSftp._realpath(ChannelSftp.java:2367)
at com.jcraft.jsch.ChannelSftp.cd(ChannelSftp.java:342)
at org.apache.camel.component.file.remote.SftpOperations.doChangeDirectory(SftpOperations.java:594)
... 53 more
I can see that the destinationDir property is set on the exchange printed in the stacktrace. If I replace
- exchangeProperty(destinationDir) +
in the route with the actual destination directory (tmp/destination/dir1/), it works fine. The problem is, I need the destination directory to be dynamic. I have tried using ${exchangeProperty.destinationDir}
in the route (> 2.16.0) and ${property.destinationDir}
in the route instead both to no avail. The "non-exchange" property placeholders all resolve fine as well.
When debugging the Camel SFTP library, I can see that at SftpOperations.class:594, path is set to exchangeProperty{destinationDir}
.
This is not getting replaced and this is why it's failing.
Note: I have the / after {{port}} (and not in the destinationDir property) because otherwise, the Camel SFTP component throws an exception saying host isn't defined.
Does anyone have any suggestions how to get property placeholders and exchange properties to work dynamically with SFTP? I've tried toD("..") with no luck as well.
Camelv 2.19.0
I have tried many variations of .toD, .inOnly, .to, .recipientList, etc with no luck. I did find an easy workaround for this however for anyone who's suffering this same frustration:
.process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
exchange.setProperty("myHackProperty", String.format("sftp://{{hostname}}:{{port}}/%s)", exchange.getProperty("destinationDir"))
}
}
.recipientList(exchangeProperty("myHackProperty"))
Simple manual dynamic string replacement. Works like a charm. Sometimes the best way to code camel is to avoid camel :)