INT-2390 backported INT-2350 and INT-2351 to 2.0.6
This commit is contained in:
@@ -97,7 +97,7 @@ public class RemoteFileOutboundChannelAdapterParser extends AbstractOutboundChan
|
||||
}
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "charset");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "remote-file-separator");
|
||||
handlerBuilder.addPropertyValue("remoteFileSeparator", element.getAttribute("remote-file-separator"));
|
||||
return handlerBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,8 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.Message;
|
||||
@@ -74,7 +76,7 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler {
|
||||
}
|
||||
|
||||
public void setRemoteFileSeparator(String remoteFileSeparator) {
|
||||
Assert.hasText(remoteFileSeparator, "'remoteFileSeparator' must not be empty");
|
||||
Assert.notNull(remoteFileSeparator, "'remoteFileSeparator' must not be null");
|
||||
this.remoteFileSeparator = remoteFileSeparator;
|
||||
}
|
||||
|
||||
@@ -106,6 +108,9 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler {
|
||||
|
||||
protected void onInit() throws Exception {
|
||||
Assert.notNull(this.directoryExpressionProcessor, "remoteDirectoryExpression is required");
|
||||
if (this.autoCreateDirectory){
|
||||
Assert.hasText(this.remoteFileSeparator, "'remoteFileSeparator' must not be empty when 'autoCreateDirectory' is set to 'true'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -194,7 +199,13 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler {
|
||||
// write remote file first with .writing extension
|
||||
String tempFilePath = remoteFilePath + this.temporaryFileSuffix;
|
||||
if (this.autoCreateDirectory) {
|
||||
session.mkdir(remoteDirectory);
|
||||
try {
|
||||
this.makeDirectories(remoteDirectory, session);
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
// Revert to old FTP behavior if recursive mkdir fails, for backwards compatibility
|
||||
session.mkdir(remoteDirectory);
|
||||
}
|
||||
}
|
||||
try {
|
||||
session.write(fileInputStream, tempFilePath);
|
||||
@@ -209,4 +220,35 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void makeDirectories(String path, Session session) throws IOException {
|
||||
if (!session.exists(path)){
|
||||
|
||||
int nextSeparatorIndex = path.lastIndexOf(remoteFileSeparator);
|
||||
|
||||
if (nextSeparatorIndex > -1){
|
||||
List<String> pathsToCreate = new LinkedList<String>();
|
||||
while (nextSeparatorIndex > -1){
|
||||
String pathSegment = path.substring(0, nextSeparatorIndex);
|
||||
if (session.exists(pathSegment)){
|
||||
// no more paths to create
|
||||
break;
|
||||
}
|
||||
else {
|
||||
pathsToCreate.add(0, pathSegment);
|
||||
nextSeparatorIndex = pathSegment.lastIndexOf(remoteFileSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
for (String pathToCreate : pathsToCreate) {
|
||||
if (logger.isDebugEnabled()){
|
||||
logger.debug("Creating '" + pathToCreate + "'");
|
||||
}
|
||||
session.mkdir(pathToCreate);
|
||||
}
|
||||
}
|
||||
else {
|
||||
session.mkdir(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -152,6 +152,10 @@ public class CachingSessionFactory implements SessionFactory, DisposableBean {
|
||||
public void mkdir(String directory) throws IOException {
|
||||
this.targetSession.mkdir(directory);
|
||||
}
|
||||
|
||||
public boolean exists(String path) throws IOException {
|
||||
return this.targetSession.exists(path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,4 +46,6 @@ public interface Session {
|
||||
void close();
|
||||
|
||||
boolean isOpen();
|
||||
|
||||
boolean exists(String path) throws IOException;
|
||||
}
|
||||
|
||||
@@ -136,4 +136,24 @@ class FtpSession implements Session {
|
||||
this.client.makeDirectory(directory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean exists(String path) throws IOException {
|
||||
Assert.hasText(path, "'path' must not be empty");
|
||||
|
||||
String currentWorkingPath = this.client.printWorkingDirectory();
|
||||
Assert.state(currentWorkingPath != null, "working directory cannot be determined, therefore exists check can not be completed");
|
||||
boolean exists = false;
|
||||
|
||||
try {
|
||||
if (this.client.changeWorkingDirectory(path)){
|
||||
exists = true;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.client.changeWorkingDirectory(currentWorkingPath);
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
cache-sessions="false"
|
||||
remote-directory="foo/bar"
|
||||
charset="UTF-8"
|
||||
remote-file-separator="."
|
||||
auto-create-directory="false"
|
||||
remote-file-separator=""
|
||||
temporary-file-suffix=".foo"
|
||||
remote-filename-generator="fileNameGenerator"
|
||||
order="23"/>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-ftp="http://www.springframework.org/schema/integration/ftp"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
|
||||
http://www.springframework.org/schema/integration/ftp http://www.springframework.org/schema/integration/ftp/spring-integration-ftp-2.0.xsd">
|
||||
|
||||
<bean id="ftpSessionFactory" class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="port" value="22"/>
|
||||
<property name="username" value="oleg"/>
|
||||
<property name="password" value="password"/>
|
||||
<property name="clientMode" value="0"/>
|
||||
<property name="fileType" value="2"/>
|
||||
</bean>
|
||||
|
||||
<int-ftp:outbound-channel-adapter id="ftpOutbound"
|
||||
channel="ftpChannel"
|
||||
session-factory="ftpSessionFactory"
|
||||
cache-sessions="false"
|
||||
remote-directory="foo/bar"
|
||||
charset="UTF-8"
|
||||
auto-create-directory="true"
|
||||
remote-file-separator=""
|
||||
temporary-file-suffix=".foo"
|
||||
remote-filename-generator="fileNameGenerator"
|
||||
order="23"/>
|
||||
|
||||
|
||||
<int:publish-subscribe-channel id="ftpChannel"/>
|
||||
|
||||
<bean id="fileNameGenerator" class="org.mockito.Mockito" factory-method="mock">
|
||||
<constructor-arg value="org.springframework.integration.file.FileNameGenerator"/>
|
||||
</bean>
|
||||
</beans>
|
||||
@@ -16,15 +16,12 @@
|
||||
|
||||
package org.springframework.integration.ftp.config;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.channel.PublishSubscribeChannel;
|
||||
@@ -35,6 +32,12 @@ import org.springframework.integration.file.remote.session.CachingSessionFactory
|
||||
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
@@ -55,7 +58,7 @@ public class FtpOutboundChannelAdapterParserTests {
|
||||
String remoteFileSeparator = (String) TestUtils.getPropertyValue(handler, "remoteFileSeparator");
|
||||
assertNotNull(remoteFileSeparator);
|
||||
assertEquals(".foo", TestUtils.getPropertyValue(handler, "temporaryFileSuffix", String.class));
|
||||
assertEquals(".", remoteFileSeparator);
|
||||
assertEquals("", remoteFileSeparator);
|
||||
assertEquals(ac.getBean("fileNameGenerator"), TestUtils.getPropertyValue(handler, "fileNameGenerator"));
|
||||
assertEquals("UTF-8", TestUtils.getPropertyValue(handler, "charset"));
|
||||
assertNotNull(TestUtils.getPropertyValue(handler, "temporaryDirectory"));
|
||||
@@ -86,5 +89,10 @@ public class FtpOutboundChannelAdapterParserTests {
|
||||
Object innerSfProperty = TestUtils.getPropertyValue(sfProperty, "sessionFactory");
|
||||
assertEquals(DefaultFtpSessionFactory.class, innerSfProperty.getClass());
|
||||
}
|
||||
|
||||
@Test(expected=BeanCreationException.class)
|
||||
public void testFailWithEmptyRfsAndAcdTrue() throws Exception{
|
||||
new ClassPathXmlApplicationContext("FtpOutboundChannelAdapterParserTests-fail.xml", this.getClass());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,11 +25,9 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.NestedIOException;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
@@ -156,7 +154,7 @@ class SftpSession implements Session {
|
||||
|
||||
public void mkdir(String remoteDirectory) throws IOException {
|
||||
try {
|
||||
this.mkdirRecursively(remoteDirectory, remoteDirectory);
|
||||
this.channel.mkdir(remoteDirectory);
|
||||
}
|
||||
catch (SftpException e) {
|
||||
throw new NestedIOException("failed to create remote directory '" + remoteDirectory + "'.", e);
|
||||
@@ -178,46 +176,7 @@ class SftpSession implements Session {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the underlying SFTP API does not give us a clean method to create directories recursively,
|
||||
* we need to create them one at the time starting from the path that we know actually exists.
|
||||
* To determine the existing path we need to iterate through each delimited segment starting from
|
||||
* the full directory path moving backward until we find it. Once found we need to start creating
|
||||
* individual directories for each segment; so in this method on the initial call the two parameters
|
||||
* will be the same, but for each recursive call the 'currentPath' is the directory with one less
|
||||
* segment from the previous 'currentPath'. For example, if you had '/foo/bar/baz', in the next
|
||||
* iteration it would be '/foo/bar/', and then just '/foo' and so on.
|
||||
*/
|
||||
private void mkdirRecursively(String currentPath, String fullPath) throws SftpException {
|
||||
String remoteFileSeparator = "/";
|
||||
if (this.exists(currentPath)) {
|
||||
String missingDirectoryPath = fullPath.substring(currentPath.length());
|
||||
String[] directories = StringUtils.tokenizeToStringArray(missingDirectoryPath, remoteFileSeparator);
|
||||
String directory = currentPath + remoteFileSeparator;
|
||||
for (String directorySegment : directories) {
|
||||
directory += directorySegment + remoteFileSeparator;
|
||||
if (logger.isDebugEnabled()){
|
||||
logger.debug("Creating '" + directory + "'");
|
||||
}
|
||||
this.channel.mkdir(directory);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Directory '" + currentPath + "' does not exist. Will attempt to auto-create it");
|
||||
}
|
||||
int nextSeparatorIndex = currentPath.lastIndexOf(remoteFileSeparator);
|
||||
if (nextSeparatorIndex <= 0) {
|
||||
throw new MessagingException("Failed to auto-create directory '" + fullPath + "'");
|
||||
}
|
||||
else {
|
||||
currentPath = currentPath.substring(0, nextSeparatorIndex);
|
||||
this.mkdirRecursively(currentPath, fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean exists(String path) {
|
||||
public boolean exists(String path) {
|
||||
try {
|
||||
this.channel.lstat(path);
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user