INTSAMPLES-134: SFTP: Use Apache SSHD SshServer
JIRA: https://jira.spring.io/browse/INTSAMPLES-134 INTSAMPLES-134: `sun.misc.BASE64Decoder` -> `org.apache.sshd.common.util.Base64` INTSAMPLES-134: PR comments and fix for Windows INTSAMPLES-134 Test Samples with Remote Server - change 'user' property to 'username' to avoid conflict with environment variable. - create temporary files using the remote file template in 'si.sftp.sample'. - clean up after test completion. - use a caching session factory.
This commit is contained in:
committed by
Gary Russell
parent
35e5156bdb
commit
90d5ee9282
3
basic/sftp/.gitignore
vendored
3
basic/sftp/.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
/local-dir
|
||||
/target
|
||||
/src/test/resources/META-INF/keys/sftp_rsa
|
||||
hostkey.ser
|
||||
si.sftp.sample
|
||||
|
||||
@@ -6,11 +6,18 @@ This example demonstrates the following aspects of the SFTP support available wi
|
||||
1. SFTP Inbound Channel Adapter (transfers files from remote to local directory)
|
||||
2. SFTP Outbound Channel Adapter (transfers files from local to the remote directory)
|
||||
|
||||
In order to run this sample you need to:
|
||||
In order to run this sample for the 'real' SFTP Server you need to:
|
||||
|
||||
1. generate private/public keys. Below is simple directions what needs to be done
|
||||
2. update user.properties file with appropriate values
|
||||
3. run the sample
|
||||
|
||||
By default this sample uses an [Apache MINA](http://mina.apache.org/sshd-project) embedded `SshServer` with predefined
|
||||
private and public keys.
|
||||
Note, the embedded Server is started only when the `port` property remains as `-1`. In this case the target port
|
||||
for the Embedded Server is selected randomly. For a real SFTP server you should specify correct `host/port` properties.
|
||||
|
||||
NOTE: The test cases will create/delete a directory `si.sftp.sample`.
|
||||
|
||||
## INBOUND CHANNEL ADAPTER
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
A
|
||||
@@ -1 +0,0 @@
|
||||
B
|
||||
@@ -1 +0,0 @@
|
||||
C
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2014 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.
|
||||
* 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.springframework.integration.samples.sftp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.sshd.SshServer;
|
||||
import org.apache.sshd.common.NamedFactory;
|
||||
import org.apache.sshd.common.file.FileSystemView;
|
||||
import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory;
|
||||
import org.apache.sshd.common.file.nativefs.NativeFileSystemView;
|
||||
import org.apache.sshd.common.util.Base64;
|
||||
import org.apache.sshd.server.Command;
|
||||
import org.apache.sshd.server.PublickeyAuthenticator;
|
||||
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
|
||||
import org.apache.sshd.server.session.ServerSession;
|
||||
import org.apache.sshd.server.sftp.SftpSubsystem;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.util.SocketUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class EmbeddedSftpServer implements InitializingBean, SmartLifecycle {
|
||||
|
||||
public static final int PORT = SocketUtils.findAvailableTcpPort();
|
||||
|
||||
private final SshServer server = SshServer.setUpDefaultServer();
|
||||
|
||||
private volatile int port;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
final PublicKey allowedKey = decodePublicKey();
|
||||
this.server.setPublickeyAuthenticator(new PublickeyAuthenticator() {
|
||||
|
||||
@Override
|
||||
public boolean authenticate(String username, PublicKey key, ServerSession session) {
|
||||
return key.equals(allowedKey);
|
||||
}
|
||||
|
||||
});
|
||||
this.server.setPort(this.port);
|
||||
this.server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("hostkey.ser"));
|
||||
this.server.setSubsystemFactories(Collections.<NamedFactory<Command>>singletonList(new SftpSubsystem.Factory()));
|
||||
final String virtualDir = new FileSystemResource("").getFile().getAbsolutePath();
|
||||
this.server.setFileSystemFactory(new NativeFileSystemFactory() {
|
||||
|
||||
@Override
|
||||
public FileSystemView createFileSystemView(org.apache.sshd.common.Session session) {
|
||||
return new NativeFileSystemView(session.getUsername(), false) {
|
||||
|
||||
@Override
|
||||
public String getVirtualUserDir() {
|
||||
return virtualDir;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private PublicKey decodePublicKey() throws Exception {
|
||||
InputStream stream = new ClassPathResource("META-INF/keys/sftp_rsa.pub").getInputStream();
|
||||
byte[] decodeBuffer = Base64.decodeBase64(StreamUtils.copyToByteArray(stream));
|
||||
ByteBuffer bb = ByteBuffer.wrap(decodeBuffer);
|
||||
int len = bb.getInt();
|
||||
byte[] type = new byte[len];
|
||||
bb.get(type);
|
||||
if ("ssh-rsa".equals(new String(type))) {
|
||||
BigInteger e = decodeBigInt(bb);
|
||||
BigInteger m = decodeBigInt(bb);
|
||||
RSAPublicKeySpec spec = new RSAPublicKeySpec(m, e);
|
||||
return KeyFactory.getInstance("RSA").generatePublic(spec);
|
||||
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Only supports RSA");
|
||||
}
|
||||
}
|
||||
|
||||
private BigInteger decodeBigInt(ByteBuffer bb) {
|
||||
int len = bb.getInt();
|
||||
byte[] bytes = new byte[len];
|
||||
bb.get(bytes);
|
||||
return new BigInteger(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return PORT == this.port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
try {
|
||||
server.start();
|
||||
this.running = true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
stop();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (this.running) {
|
||||
try {
|
||||
server.stop(true);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
finally {
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,24 +15,68 @@
|
||||
*/
|
||||
package org.springframework.integration.samples.sftp;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public class SftpInboundReceiveSample {
|
||||
|
||||
@Test
|
||||
public void runDemo(){
|
||||
ApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/integration/SftpInboundReceiveSample-context.xml", this.getClass());
|
||||
PollableChannel localFileChannel = context.getBean("receiveChannel", PollableChannel.class);
|
||||
System.out.println("Received first file message: " + localFileChannel.receive());
|
||||
System.out.println("Received second file message: " + localFileChannel.receive());
|
||||
System.out.println("No third file was received " + localFileChannel.receive(1000));
|
||||
ConfigurableApplicationContext context =
|
||||
new ClassPathXmlApplicationContext("/META-INF/spring/integration/SftpInboundReceiveSample-context.xml", this.getClass());
|
||||
RemoteFileTemplate<LsEntry> template = null;
|
||||
String file1 = "a.txt";
|
||||
String file2 = "b.txt";
|
||||
String file3 = "c.bar";
|
||||
new File("local-dir", file1).delete();
|
||||
new File("local-dir", file2).delete();
|
||||
try {
|
||||
PollableChannel localFileChannel = context.getBean("receiveChannel", PollableChannel.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
SessionFactory<LsEntry> sessionFactory = context.getBean(CachingSessionFactory.class);
|
||||
template = new RemoteFileTemplate<LsEntry>(sessionFactory);
|
||||
SftpTestUtils.createTestFiles(template, file1, file2, file3);
|
||||
|
||||
SourcePollingChannelAdapter adapter = context.getBean(SourcePollingChannelAdapter.class);
|
||||
adapter.start();
|
||||
|
||||
Message<?> received = localFileChannel.receive();
|
||||
assertNotNull("Expected file", received);
|
||||
System.out.println("Received first file message: " + received);
|
||||
received = localFileChannel.receive();
|
||||
assertNotNull("Expected file", received);
|
||||
System.out.println("Received second file message: " + received);
|
||||
received = localFileChannel.receive(1000);
|
||||
assertNull("Expected null", received);
|
||||
System.out.println("No third file was received as expected");
|
||||
}
|
||||
finally {
|
||||
SftpTestUtils.cleanUp(template, file1, file2, file3);
|
||||
context.close();
|
||||
assertTrue("Could note delete retrieved file", new File("local-dir", file1).delete());
|
||||
assertTrue("Could note delete retrieved file", new File("local-dir", file2).delete());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,11 +20,16 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
* Demonstrates use of the outbound gateway to use ls, get and rm.
|
||||
@@ -40,24 +45,23 @@ public class SftpOutboundGatewaySample {
|
||||
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
|
||||
"classpath:/META-INF/spring/integration/SftpOutboundGatewaySample-context.xml");
|
||||
ToSftpFlowGateway toFtpFlow = ctx.getBean(ToSftpFlowGateway.class);
|
||||
RemoteFileTemplate<LsEntry> template = null;
|
||||
String file1 = "1.ftptest";
|
||||
String file2 = "2.ftptest";
|
||||
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
|
||||
|
||||
try {
|
||||
String tmpDir = System.getProperty("java.io.tmpdir");
|
||||
|
||||
// remove the previous output files if necessary
|
||||
new File(new File(tmpDir), "1.ftptest").delete();
|
||||
new File(new File(tmpDir), "2.ftptest").delete();
|
||||
|
||||
// create a couple of files in a temp dir
|
||||
File dir = new File(tmpDir + "/" + new Random().nextInt());
|
||||
dir.mkdir();
|
||||
File f1 = new File(dir, "1.ftptest");
|
||||
f1.createNewFile();
|
||||
File f2 = new File(dir, "2.ftptest");
|
||||
f2.createNewFile();
|
||||
new File(tmpDir, file1).delete();
|
||||
new File(tmpDir, file2).delete();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
SessionFactory<LsEntry> sessionFactory = ctx.getBean(CachingSessionFactory.class);
|
||||
template = new RemoteFileTemplate<LsEntry>(sessionFactory);
|
||||
SftpTestUtils.createTestFiles(template, file1, file2);
|
||||
|
||||
// execute the flow (ls, get, rm, aggregate results)
|
||||
List<Boolean> rmResults = toFtpFlow.lsGetAndRmFiles(dir.getAbsolutePath());
|
||||
List<Boolean> rmResults = toFtpFlow.lsGetAndRmFiles("si.sftp.sample");
|
||||
|
||||
|
||||
//Check everything went as expected, and clean up
|
||||
@@ -65,13 +69,16 @@ public class SftpOutboundGatewaySample {
|
||||
for (Boolean result : rmResults) {
|
||||
assertTrue(result);
|
||||
}
|
||||
assertTrue("Expected remote dir to be empty", dir.delete());
|
||||
assertTrue("Could note delete retrieved file", new File(new File(tmpDir), "1.ftptest").delete());
|
||||
assertTrue("Could note delete retrieved file", new File(new File(tmpDir), "2.ftptest").delete());
|
||||
} finally {
|
||||
|
||||
}
|
||||
finally {
|
||||
SftpTestUtils.cleanUp(template, file1, file2);
|
||||
ctx.close();
|
||||
assertTrue("Could note delete retrieved file", new File(tmpDir, file1).delete());
|
||||
assertTrue("Could note delete retrieved file", new File(tmpDir, file2).delete());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2014 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,15 +20,21 @@ import java.io.File;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public class SftpOutboundTransferSample {
|
||||
@@ -38,29 +44,34 @@ public class SftpOutboundTransferSample {
|
||||
|
||||
final String sourceFileName = "README.md";
|
||||
final String destinationFileName = sourceFileName +"_foo";
|
||||
final String destinationFilePath = "remote-target-dir/" + destinationFileName;
|
||||
|
||||
final ClassPathXmlApplicationContext ac =
|
||||
new ClassPathXmlApplicationContext("/META-INF/spring/integration/SftpOutboundTransferSample-context.xml", SftpOutboundTransferSample.class);
|
||||
ac.start();
|
||||
@SuppressWarnings("unchecked")
|
||||
SessionFactory<LsEntry> sessionFactory = ac.getBean(CachingSessionFactory.class);
|
||||
RemoteFileTemplate<LsEntry> template = new RemoteFileTemplate<LsEntry>(sessionFactory);
|
||||
SftpTestUtils.createTestFiles(template); // Just the directory
|
||||
|
||||
final File file = new File(sourceFileName);
|
||||
try {
|
||||
final File file = new File(sourceFileName);
|
||||
|
||||
Assert.isTrue(file.exists(), String.format("File '%s' does not exist.", sourceFileName));
|
||||
Assert.isTrue(file.exists(), String.format("File '%s' does not exist.", sourceFileName));
|
||||
|
||||
final Message<File> message = MessageBuilder.withPayload(file).build();
|
||||
final MessageChannel inputChannel = ac.getBean("inputChannel", MessageChannel.class);
|
||||
final Message<File> message = MessageBuilder.withPayload(file).build();
|
||||
final MessageChannel inputChannel = ac.getBean("inputChannel", MessageChannel.class);
|
||||
|
||||
inputChannel.send(message);
|
||||
Thread.sleep(2000);
|
||||
inputChannel.send(message);
|
||||
Thread.sleep(2000);
|
||||
|
||||
Assert.isTrue(new File(destinationFilePath).exists(), String.format("File '%s' does not exist.", destinationFilePath));
|
||||
|
||||
System.out.println(String.format("Successfully transferred '%s' file to a " +
|
||||
"remote location under the name '%s'", sourceFileName, destinationFileName));
|
||||
|
||||
ac.stop();
|
||||
Assert.isTrue(SftpTestUtils.fileExists(template, destinationFileName));
|
||||
|
||||
System.out.println(String.format("Successfully transferred '%s' file to a " +
|
||||
"remote location under the name '%s'", sourceFileName, destinationFileName));
|
||||
}
|
||||
finally {
|
||||
SftpTestUtils.cleanUp(template, destinationFileName);
|
||||
ac.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2014 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.
|
||||
* 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.springframework.integration.samples.sftp;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.file.remote.SessionCallback;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 4.1
|
||||
*
|
||||
*/
|
||||
public class SftpTestUtils {
|
||||
|
||||
public static void createTestFiles(RemoteFileTemplate<LsEntry> template, final String... fileNames) {
|
||||
if (template != null) {
|
||||
final ByteArrayInputStream stream = new ByteArrayInputStream("foo".getBytes());
|
||||
template.execute(new SessionCallback<LsEntry, Void>() {
|
||||
|
||||
@Override
|
||||
public Void doInSession(Session<LsEntry> session) throws IOException {
|
||||
try {
|
||||
session.mkdir("si.sftp.sample");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e.getMessage(), containsString("failed to create"));
|
||||
}
|
||||
for (int i = 0; i < fileNames.length; i++) {
|
||||
stream.reset();
|
||||
session.write(stream, "si.sftp.sample/" + fileNames[i]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void cleanUp(RemoteFileTemplate<LsEntry> template, final String... fileNames) {
|
||||
if (template != null) {
|
||||
template.execute(new SessionCallback<LsEntry, Void>() {
|
||||
|
||||
@Override
|
||||
public Void doInSession(Session<LsEntry> session) throws IOException {
|
||||
// TODO: avoid DFAs with Spring 4.1 (INT-3412)
|
||||
ChannelSftp channel = (ChannelSftp) new DirectFieldAccessor(new DirectFieldAccessor(session)
|
||||
.getPropertyValue("targetSession")).getPropertyValue("channel");
|
||||
for (int i = 0; i < fileNames.length; i++) {
|
||||
try {
|
||||
session.remove("si.sftp.sample/" + fileNames[i]);
|
||||
}
|
||||
catch (IOException e) {}
|
||||
}
|
||||
try {
|
||||
// should be empty
|
||||
channel.rmdir("si.sftp.sample");
|
||||
}
|
||||
catch (SftpException e) {
|
||||
fail("Expected remote directory to be empty " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean fileExists(RemoteFileTemplate<LsEntry> template, final String... fileNames) {
|
||||
if (template != null) {
|
||||
return template.execute(new SessionCallback<LsEntry, Boolean>() {
|
||||
|
||||
@Override
|
||||
public Boolean doInSession(Session<LsEntry> session) throws IOException {
|
||||
// TODO: avoid DFAs with Spring 4.1 (INT-3412)
|
||||
ChannelSftp channel = (ChannelSftp) new DirectFieldAccessor(new DirectFieldAccessor(session)
|
||||
.getPropertyValue("targetSession")).getPropertyValue("channel");
|
||||
for (int i = 0; i < fileNames.length; i++) {
|
||||
try {
|
||||
SftpATTRS stat = channel.stat("si.sftp.sample/" + fileNames[i]);
|
||||
if (stat == null) {
|
||||
System.out.println("stat returned null for " + fileNames[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (SftpException e) {
|
||||
System.out.println("Remote file not present: " + e.getMessage() + ": " + fileNames[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-EDE3-CBC,89FEB82756CA8E56
|
||||
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
||||
1
basic/sftp/src/test/resources/META-INF/keys/sftp_rsa.pub
Normal file
1
basic/sftp/src/test/resources/META-INF/keys/sftp_rsa.pub
Normal file
@@ -0,0 +1 @@
|
||||
AAAAB3NzaC1yc2EAAAADAQABAAABAQDMQB/vTCY6QEF+/P73ZQZzgj5LBUG8gKPbAXdWcE/X0g3jgsPpxb7+NGiqNpP+oVIQsL9Q7TBsv1/X7xc6sd6SmM/UTP8LF0zlf0GSvEehC2BT98GCiDqI8BKpZjG6O8+pPEBY8M5iGOLtDUSzlhzyV7kPcADYLHl/z1fssU+x2UML7BoSmgh0XPc2u+Gv6+58SVk2ipAZOmaWZDyzWAc3sczz/DkQeR/cpj047HVc1Zb/M5a4ajYI18Ka+mVhKeiQnHc3NKwE6ZxcwlacbVK/feypfperHQOlj6Ee6f6huhnVx9x42V11A41Toh98WI2EVDequ4EcTxHHJntJhpbd
|
||||
@@ -3,29 +3,31 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/integration/sftp http://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
|
||||
|
||||
<context:property-placeholder location="classpath:user.properties"/>
|
||||
<import resource="SftpSampleCommon.xml"/>
|
||||
|
||||
<bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="privateKey" value="classpath:META-INF/keys/sftp_rsa"/>
|
||||
<bean id="sftpSessionFactory" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
|
||||
<constructor-arg ref="defaultSftpSessionFactory" />
|
||||
</bean>
|
||||
|
||||
<bean id="defaultSftpSessionFactory"
|
||||
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
|
||||
<property name="host" value="${host}"/>
|
||||
<property name="privateKey" value="${private.keyfile}"/>
|
||||
<property name="privateKeyPassphrase" value="${passphrase}"/>
|
||||
<property name="port" value="22"/>
|
||||
<property name="user" value="${user}"/>
|
||||
<property name="port" value="#{serverPort}"/>
|
||||
<property name="user" value="${username}"/>
|
||||
</bean>
|
||||
|
||||
<int-sftp:inbound-channel-adapter id="sftpInbondAdapter"
|
||||
auto-startup="false"
|
||||
channel="receiveChannel"
|
||||
session-factory="sftpSessionFactory"
|
||||
local-directory="file:local-dir"
|
||||
remote-directory="${remote.directory}"
|
||||
remote-directory="si.sftp.sample"
|
||||
auto-create-local-directory="true"
|
||||
delete-remote-files="false"
|
||||
filename-regex=".*\.txt$">
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
<?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:context="http://www.springframework.org/schema/context"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration/sftp http://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
http://www.springframework.org/schema/integration/sftp http://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
|
||||
|
||||
<context:property-placeholder location="classpath:user.properties"/>
|
||||
<import resource="SftpSampleCommon.xml"/>
|
||||
|
||||
<int:gateway id="gw" service-interface="org.springframework.integration.samples.sftp.ToSftpFlowGateway"
|
||||
default-request-channel="inbound"/>
|
||||
|
||||
<bean id="sftpSessionFactory"
|
||||
<bean id="sftpSessionFactory" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
|
||||
<constructor-arg ref="defaultSftpSessionFactory" />
|
||||
</bean>
|
||||
|
||||
<bean id="defaultSftpSessionFactory"
|
||||
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="host" value="${host}"/>
|
||||
<property name="privateKey" value="${private.keyfile}"/>
|
||||
<property name="privateKeyPassphrase" value="${passphrase}"/>
|
||||
<property name="port" value="22"/>
|
||||
<property name="user" value="${user}"/>
|
||||
<property name="port" value="#{serverPort}"/>
|
||||
<property name="user" value="${username}"/>
|
||||
</bean>
|
||||
|
||||
<int-sftp:outbound-gateway id="gatewayLS"
|
||||
@@ -40,11 +42,11 @@
|
||||
reply-channel="toRm"
|
||||
command="get"
|
||||
command-options="-P"
|
||||
expression="payload.remoteDirectory + '/' + payload.filename"/>
|
||||
expression="payload.remoteDirectory + payload.filename"/>
|
||||
|
||||
<int-sftp:outbound-gateway id="gatewayRM" reply-channel="aggregateResultsChannel"
|
||||
session-factory="sftpSessionFactory"
|
||||
expression="headers['file_remoteDirectory'] + '/' + headers['file_remoteFile']"
|
||||
expression="headers['file_remoteDirectory'] + headers['file_remoteFile']"
|
||||
request-channel="toRm"
|
||||
command="rm"/>
|
||||
|
||||
|
||||
@@ -3,20 +3,23 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/integration/sftp http://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
|
||||
|
||||
<context:property-placeholder location="classpath:user.properties"/>
|
||||
<import resource="SftpSampleCommon.xml"/>
|
||||
|
||||
<bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="privateKey" value="classpath:META-INF/keys/sftp_rsa"/>
|
||||
<bean id="sftpSessionFactory" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
|
||||
<constructor-arg ref="defaultSftpSessionFactory" />
|
||||
</bean>
|
||||
|
||||
<bean id="defaultSftpSessionFactory"
|
||||
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
|
||||
<property name="host" value="${host}"/>
|
||||
<property name="privateKey" value="${private.keyfile}"/>
|
||||
<property name="privateKeyPassphrase" value="${passphrase}"/>
|
||||
<property name="port" value="22"/>
|
||||
<property name="user" value="${user}"/>
|
||||
<property name="port" value="#{serverPort}"/>
|
||||
<property name="user" value="${username}"/>
|
||||
</bean>
|
||||
|
||||
<int:channel id="inputChannel"/>
|
||||
@@ -25,6 +28,6 @@
|
||||
session-factory="sftpSessionFactory"
|
||||
channel="inputChannel"
|
||||
remote-filename-generator-expression="payload.getName() + '_foo'"
|
||||
remote-directory="${remote.directory}"/>
|
||||
remote-directory="si.sftp.sample"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?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:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<context:property-placeholder location="classpath:user.properties"/>
|
||||
|
||||
<bean id="serverPort" class="java.lang.String">
|
||||
<constructor-arg value="#{'${port}' == '-1' ? T(org.springframework.integration.samples.sftp.EmbeddedSftpServer).PORT : '${port}'}"/>
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.integration.samples.sftp.EmbeddedSftpServer">
|
||||
<property name="port" value="#{serverPort}"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -1,5 +1,7 @@
|
||||
user=
|
||||
passphrase=
|
||||
host=localhost
|
||||
# -1 means the embedded Apache MINA SshServer. Change it to any real port, if you are going to test sample against real SFTP Server
|
||||
port=-1
|
||||
username=user
|
||||
passphrase=password
|
||||
#private.keyfile=file:/home/someuser/.ssh/id_rsa
|
||||
private.keyfile=classpath:META-INF/keys/sftp_rsa
|
||||
remote.directory=<<SPECIFY REMOTE DIRECTORY (e.g., /{workspace}/samples/basic/sftp/remote-target-dir)>>
|
||||
|
||||
@@ -150,6 +150,7 @@ subprojects { subproject ->
|
||||
|
||||
ext {
|
||||
activeMqVersion = '5.9.0'
|
||||
apacheSshdVersion = '0.10.1'
|
||||
aspectjVersion = '1.8.0'
|
||||
commonsDigesterVersion = '2.0'
|
||||
commonsDbcpVersion = '1.2.2'
|
||||
@@ -652,10 +653,7 @@ project('sftp') {
|
||||
|
||||
dependencies {
|
||||
compile "org.springframework.integration:spring-integration-sftp:$springIntegrationVersion"
|
||||
}
|
||||
|
||||
test {
|
||||
exclude '**/*Sample*'
|
||||
compile "org.apache.sshd:sshd-core:$apacheSshdVersion"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user