title: Moving processed files in remote S(ftp) using Java DSL
body: <p>I'm trying to move files on remote SFTP once the batch has successfully processed the files using Spring integration and Java DSL.</p>
  
  <p>What would be the best way to achieve that? </p>
  
  <ol>
  <li>Adding a step in batch to move remote files ?</li>
  <li>Or using FTP Outbound Gateway and provide the MV command ?</li>
  </ol>
  
  <p>I tend to prefer the second solution and let the batch focus on the logic only, but I've hard times trying to implement it with java dsl. </p>
  
  <p>I've read <a href="http://docs.spring.io/spring-integration/reference/html/ftp.html#ftp-outbound-gateway" rel="nofollow">http://docs.spring.io/spring-integration/reference/html/ftp.html#ftp-outbound-gateway</a> and tried to implement like that :</p>
  
  <pre><code>@Bean
  public MessageHandler ftpOutboundGateway() {
      return Sftp.outboundGateway(SftpSessionFactory(), 
              AbstractRemoteFileOutboundGateway.Command.MV, "payload")
              .localDirectory(new File("/home/blabla/"))
              .get();
  
  }
  
  @Bean
  public IntegrationFlow ftpInboundFlow() {
      return IntegrationFlows
              .from(
                  Sftp.inboundAdapter(SftpSessionFactory())
                  .regexFilter(".*\\.xml.mini$")
                  ...             
                 , 
                  e -&gt; e.id("sftpInboundAdapter")
                  .poller(
                          Pollers.fixedRate(intCfg.getSftpPollerInMinutes(), TimeUnit.MINUTES)
                          .maxMessagesPerPoll(-1)
                          .advice(retryAdvice())
                          )
              )
              .enrichHeaders( h -&gt; h
                      .header(FileHeaders.REMOTE_DIRECTORY,"/home/filedrop/")
                      .header(FileHeaders.REMOTE_FILE, "/home/filedrop/OFFERS.xml.mini")
                      .header(FileHeaders.RENAME_TO, "/home/filedrop/done/OFFERS.xml.mini")
              )
              .transform(fileToJobLaunchRequestTransformer())         
              .handle(jobLaunchingGw()))
              .transform(jobExecutionToFileStringTransformer())
              .handle(ftpOutboundGateway())
              .handle(logger())
              .get();
  }
  </code></pre>
  
  <p>I know my headers should be dynamic but I don't know how to do it so for now I use the name of an existing file. I get this error message (he's trying to delete the file in the destination directory!):</p>
  
  <pre><code>Caused by: org.springframework.messaging.MessagingException: Failed to execute on session; nested exception is org.springframework.core.NestedIOException: Failed to delete file /home/filedrop/done/OFFERS.xml.mini; nested exception is org.springframework.core.NestedIOException: Failed to remove file: 2: No such file     at org.springframework.integration.file.remote.RemoteFileTemplate.execute(RemoteFileTemplate.java:343)
      at org.springframework.integration.file.remote.RemoteFileTemplate.rename(RemoteFileTemplate.java:290)
      at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.doMv(AbstractRemoteFileOutboundGateway.java:482)
      at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.handleRequestMessage(AbstractRemoteFileOutboundGateway.java:400)
      at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:99)
      at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
      at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
      ... 94 more
   Caused by: org.springframework.core.NestedIOException: Failed to delete file /home/filedrop/done/OFFERS.xml.mini; nested exception is org.springframework.core.NestedIOException: Failed to remove file: 2: No such file
      at org.springframework.integration.sftp.session.SftpSession.rename(SftpSession.java:211)
      at org.springframework.integration.file.remote.RemoteFileTemplate$3.doInSessionWithoutResult(RemoteFileTemplate.java:300)
      at org.springframework.integration.file.remote.SessionCallbackWithoutResult.doInSession(SessionCallbackWithoutResult.java:34)
      at org.springframework.integration.file.remote.RemoteFileTemplate.execute(RemoteFileTemplate.java:334)
      ... 100 more
   Caused by: org.springframework.core.NestedIOException: Failed to remove file: 2: No such file
      at org.springframework.integration.sftp.session.SftpSession.remove(SftpSession.java:83)
      at org.springframework.integration.sftp.session.SftpSession.rename(SftpSession.java:205)
      ... 103 more
  </code></pre>
  
  <p>Thanks for any help!</p>
  
  <p><strong>EDIT</strong>
  working flow, I've then simplified it a lot but here the solution of myprevious problem:</p>
  
  <pre><code>@Bean
  public IntegrationFlow ftpInboundFlow() {
      return IntegrationFlows
              .from(
                  Sftp.inboundAdapter(SftpSessionFactory())
                  .regexFilter(".*\\.xml$")
                  ...
                  , 
                  e -&gt; e.id("sftpInboundAdapter")
                  .poller(Pollers.fixedRate(intCfg.getSftpPollerInMinutes(), TimeUnit.MINUTES)
                          .maxMessagesPerPoll(-1)
                          )
              )
              .enrichHeaders( h -&gt; h
                      // headers necessary for moving remote files (ftpOutboundGateway)
                      .headerExpression(FileHeaders.RENAME_TO, "'/home/blabla/done/' + payload.getName()")
                      .headerExpression(FileHeaders.REMOTE_FILE, "payload.getName()")
                      .header(FileHeaders.REMOTE_DIRECTORY,"/home/blabla/")
                      // headers necessary for moving local files (fileOutboundGateway_MoveToProcessedDirectory)
                      .headerExpression(FileHeaders.ORIGINAL_FILE,  "payload.getAbsolutePath()" )
                      .headerExpression(FileHeaders.FILENAME,  "payload.getName()")
              )
              .transform(fileToJobLaunchRequestTransformer())         
              .handle(jobLaunchingGw(), e-&gt; e.advice(retryAdvice()))
  
              .&lt;JobExecution, Boolean&gt;route(p -&gt; BatchStatus.COMPLETED.equals(p.getStatus()),
                                              mapping -&gt; mapping
                                              .subFlowMapping("true", sf -&gt; sf
  
  
                                                  .handle(org.springframework.batch.core.JobExecution.class,
                                                           (p, h) -&gt; myServiceActivator.jobExecutionToString(p, 
                                                                   (String) h.get(FileHeaders.REMOTE_DIRECTORY),
                                                                   (String) h.get(FileHeaders.REMOTE_FILE)))
                                                  .handle(ftpOutboundGateway())
                                                  .handle(Boolean.class,
                                                           (p, h) -&gt; myServiceActivator.BooleanToString(p, 
                                                                   (String) h.get(FileHeaders.FILENAME)))
                                                  .handle(fileOutboundGateway_MoveToProcessedDirectory())
  
                                                                                      )
  
  
                                          .subFlowMapping("false", sf -&gt; sf
                                              .channel("nullChannel")     
  
                                              )
              )
  
              .handle(logger())
              .get();
  }
  
  @Bean(name = PollerMetadata.DEFAULT_POLLER)
  public PollerMetadata poller() {
      return Pollers.fixedRate(500).get();
  }
  
  
  @Bean
  public MessageHandler ftpOutboundGateway() {
      return Sftp
              .outboundGateway(SftpSessionFactory(),
                      AbstractRemoteFileOutboundGateway.Command.MV,
                      "payload")
              .renameExpression("headers['file_renameTo']").get();
  }
  </code></pre>