Repository: mina-sshd Updated Branches: refs/heads/master 4039a11ae -> 2cd0ebbaf
[SSHD-428] Add ScpTransferEventListener Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/2cd0ebba Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/2cd0ebba Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/2cd0ebba Branch: refs/heads/master Commit: 2cd0ebbafabe232a483b244dcefb82477ec67580 Parents: 4039a11 Author: Guillaume Nodet <[email protected]> Authored: Tue Mar 17 18:51:19 2015 +0100 Committer: Guillaume Nodet <[email protected]> Committed: Tue Mar 17 18:56:14 2015 +0100 ---------------------------------------------------------------------- .../java/org/apache/sshd/ClientSession.java | 28 + .../sshd/client/scp/DefaultScpClient.java | 22 +- .../sshd/client/session/ClientSessionImpl.java | 23 +- .../org/apache/sshd/common/scp/ScpHelper.java | 249 ++++--- .../common/scp/ScpTransferEventListener.java | 103 +++ .../org/apache/sshd/common/util/IoUtils.java | 44 +- .../apache/sshd/server/command/ScpCommand.java | 11 +- .../sshd/server/command/ScpCommandFactory.java | 59 +- .../apache/sshd/server/sftp/SftpSubsystem.java | 4 +- .../src/test/java/org/apache/sshd/ScpTest.java | 703 ++++++++++--------- 10 files changed, 796 insertions(+), 450 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2cd0ebba/sshd-core/src/main/java/org/apache/sshd/ClientSession.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/ClientSession.java index 867dadb..a84534e 100644 --- a/sshd-core/src/main/java/org/apache/sshd/ClientSession.java +++ b/sshd-core/src/main/java/org/apache/sshd/ClientSession.java @@ -36,6 +36,7 @@ import org.apache.sshd.common.Session; import org.apache.sshd.common.SshdSocketAddress; import org.apache.sshd.common.future.CloseFuture; import org.apache.sshd.common.future.SshFuture; +import org.apache.sshd.common.scp.ScpTransferEventListener; /** * An authenticated session to a given SSH server @@ -153,10 +154,37 @@ public interface ClientSession extends Session { /** * Create an SCP client from this session. + * @return An {@link ScpClient} instance. <B>Note:</B> uses the currently + * registered {@link ScpTransferEventListener} if any + * @see #setScpTransferEventListener(ScpTransferEventListener) */ ScpClient createScpClient(); /** + * Create an SCP client from this session. + * @param listener A {@link ScpTransferEventListener} that can be used + * to receive information about the SCP operations - may be {@code null} + * to indicate no more events are required. <B>Note:</B> this listener + * is used <U>instead</U> of any listener set via {@link #setScpTransferEventListener(ScpTransferEventListener)} + * @return An {@link ScpClient} instance + */ + ScpClient createScpClient(ScpTransferEventListener listener); + + /** + * @return The last {@link ScpTransferEventListener} set via + * {@link #setScpTransferEventListener(ScpTransferEventListener)} + */ + ScpTransferEventListener getScpTransferEventListener(); + + /** + * @param listener A default {@link ScpTransferEventListener} that can be used + * to receive information about the SCP operations - may be {@code null} + * to indicate no more events are required + * @see #createScpClient(ScpTransferEventListener) + */ + void setScpTransferEventListener(ScpTransferEventListener listener); + + /** * Create an SFTP client from this session. */ SftpClient createSftpClient() throws IOException; http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2cd0ebba/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java index c0b23a0..bb5019b 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java @@ -23,10 +23,11 @@ import java.io.InterruptedIOException; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.EnumSet; import java.util.List; +import java.util.Set; import org.apache.sshd.ClientSession; import org.apache.sshd.client.ScpClient; @@ -34,6 +35,7 @@ import org.apache.sshd.client.channel.ChannelExec; import org.apache.sshd.common.SshException; import org.apache.sshd.common.file.FileSystemFactory; import org.apache.sshd.common.scp.ScpHelper; +import org.apache.sshd.common.scp.ScpTransferEventListener; /** * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> @@ -41,9 +43,15 @@ import org.apache.sshd.common.scp.ScpHelper; public class DefaultScpClient implements ScpClient { private final ClientSession clientSession; + private final ScpTransferEventListener listener; public DefaultScpClient(ClientSession clientSession) { + this(clientSession, ScpTransferEventListener.EMPTY); + } + + public DefaultScpClient(ClientSession clientSession, ScpTransferEventListener eventListener) { this.clientSession = clientSession; + this.listener = (eventListener == null) ? ScpTransferEventListener.EMPTY : eventListener; } public void download(String remote, String local, Option... options) throws IOException { @@ -55,7 +63,7 @@ public class DefaultScpClient implements ScpClient { public void download(String[] remote, String local, Option... options) throws IOException { local = checkNotNullAndNotEmpty(local, "Invalid argument local: {}"); remote = checkNotNullAndNotEmpty(remote, "Invalid argument remote: {}"); - List<Option> opts = options(options); + Set<Option> opts = options(options); if (remote.length > 1) { opts.add(Option.TargetIsDirectory); } @@ -100,7 +108,7 @@ public class DefaultScpClient implements ScpClient { throw (IOException) new InterruptedIOException().initCause(e); } - ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), fs); + ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), fs, listener); helper.receive(target, options.contains(Option.Recursive), @@ -127,7 +135,7 @@ public class DefaultScpClient implements ScpClient { public void upload(String[] local, String remote, Option... options) throws IOException { local = checkNotNullAndNotEmpty(local, "Invalid argument local: {}"); remote = checkNotNullAndNotEmpty(remote, "Invalid argument remote: {}"); - List<Option> opts = options(options); + Set<Option> opts = options(options); if (local.length > 1) { opts.add(Option.TargetIsDirectory); } @@ -161,7 +169,7 @@ public class DefaultScpClient implements ScpClient { FileSystemFactory factory = clientSession.getFactoryManager().getFileSystemFactory(); FileSystem fs = factory.createFileSystem(clientSession); try { - ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), fs); + ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), fs, listener); helper.send(Arrays.asList(local), options.contains(Option.Recursive), options.contains(Option.PreserveAttributes), @@ -176,8 +184,8 @@ public class DefaultScpClient implements ScpClient { channel.close(false); } - private List<Option> options(Option... options) { - List<Option> opts = new ArrayList<>(); + private Set<Option> options(Option... options) { + Set<Option> opts = EnumSet.noneOf(Option.class); if (options != null) { opts.addAll(Arrays.asList(options)); } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2cd0ebba/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java index a1872b8..492f5f4 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java @@ -23,7 +23,6 @@ import java.net.SocketAddress; import java.nio.file.FileSystem; import java.security.KeyPair; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -61,6 +60,7 @@ import org.apache.sshd.common.cipher.CipherNone; import org.apache.sshd.common.future.DefaultSshFuture; import org.apache.sshd.common.future.SshFuture; import org.apache.sshd.common.io.IoSession; +import org.apache.sshd.common.scp.ScpTransferEventListener; import org.apache.sshd.common.session.AbstractConnectionService; import org.apache.sshd.common.session.AbstractSession; import org.apache.sshd.common.session.ConnectionService; @@ -85,6 +85,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession private ServiceFactory nextServiceFactory; private final List<Object> identities = new ArrayList<Object>(); private UserInteraction userInteraction; + private ScpTransferEventListener scpListener; protected AuthFuture authFuture; @@ -164,7 +165,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession public AuthFuture authInteractive(String user, String password) throws IOException { return tryAuth(user, new UserAuthKeyboardInteractive(this, nextServiceName(), password)); - } + } public AuthFuture authPublicKey(String user, KeyPair key) throws IOException { return tryAuth(user, new UserAuthPublicKey(this, nextServiceName(), key)); @@ -273,8 +274,20 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession return getService(ConnectionService.class); } + public ScpTransferEventListener getScpTransferEventListener() { + return scpListener; + } + + public void setScpTransferEventListener(ScpTransferEventListener listener) { + scpListener = listener; + } + public ScpClient createScpClient() { - return new DefaultScpClient(this); + return createScpClient(getScpTransferEventListener()); + } + + public ScpClient createScpClient(ScpTransferEventListener listener) { + return new DefaultScpClient(this, listener); } public SftpClient createSftpClient() throws IOException { @@ -428,7 +441,7 @@ public class ClientSessionImpl extends AbstractSession implements ClientSession } public Map<Object, Object> getMetadataMap() { - return metadataMap; - } + return metadataMap; + } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2cd0ebba/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java index c5f6ae6..0583928 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java @@ -33,12 +33,14 @@ import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.nio.file.attribute.PosixFilePermission; +import java.util.Collection; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.sshd.common.SshException; +import org.apache.sshd.common.scp.ScpTransferEventListener.FileOperation; import org.apache.sshd.common.util.DirectoryScanner; import org.apache.sshd.common.util.IoUtils; import org.slf4j.Logger; @@ -82,11 +84,13 @@ public class ScpHelper { protected final FileSystem fileSystem; protected final InputStream in; protected final OutputStream out; + protected final ScpTransferEventListener listener; - public ScpHelper(InputStream in, OutputStream out, FileSystem fileSystem) { + public ScpHelper(InputStream in, OutputStream out, FileSystem fileSystem, ScpTransferEventListener eventListener) { this.in = in; this.out = out; this.fileSystem = fileSystem; + this.listener = (eventListener == null) ? ScpTransferEventListener.EMPTY : eventListener; } public void receive(Path path, boolean recursive, boolean shouldBeDir, boolean preserve, int bufferSize) throws IOException { @@ -153,7 +157,7 @@ public class ScpHelper { throw new IOException("Expected a D message but got '" + header + "'"); } - String perms = header.substring(1, 5); + Set<PosixFilePermission> perms = parseOctalPerms(header.substring(1, 5)); int length = Integer.parseInt(header.substring(6, header.indexOf(' ', 6))); String name = header.substring(header.indexOf(' ', 6) + 1); @@ -174,7 +178,7 @@ public class ScpHelper { } if (preserve) { - setOctalPerms(file, perms); + IoUtils.setPermissions(path, perms); if (time != null) { Files.getFileAttributeView(file, BasicFileAttributeView.class) .setTimes(FileTime.from(time[0], TimeUnit.SECONDS), @@ -186,24 +190,32 @@ public class ScpHelper { ack(); time = null; - for (;;) { - header = readLine(); - log.debug("Received header: " + header); - if (header.startsWith("C")) { - receiveFile(header, file, time, preserve, bufferSize); - time = null; - } else if (header.startsWith("D")) { - receiveDir(header, file, time, preserve, bufferSize); - time = null; - } else if (header.equals("E")) { - ack(); - break; - } else if (header.startsWith("T")) { - time = parseTime(header); - ack(); - } else { - throw new IOException("Unexpected message: '" + header + "'"); + try { + listener.startFolderEvent(FileOperation.RECEIVE, path, perms); + for (; ; ) { + header = readLine(); + if (log.isDebugEnabled()) { + log.debug("Received header: " + header); + } + if (header.startsWith("C")) { + receiveFile(header, file, time, preserve, bufferSize); + time = null; + } else if (header.startsWith("D")) { + receiveDir(header, file, time, preserve, bufferSize); + time = null; + } else if (header.equals("E")) { + ack(); + break; + } else if (header.startsWith("T")) { + time = parseTime(header); + ack(); + } else { + throw new IOException("Unexpected message: '" + header + "'"); + } } + } catch (IOException | RuntimeException e) { + listener.endFolderEvent(FileOperation.RECEIVE, path, perms, e); + throw e; } } @@ -219,7 +231,7 @@ public class ScpHelper { throw new IOException("receiveFile(" + path + ") buffer size (" + bufferSize + ") below minimum (" + MIN_RECEIVE_BUFFER_SIZE + ")"); } - String perms = header.substring(1, 5); + Set<PosixFilePermission> perms = parseOctalPerms(header.substring(1, 5)); final long length = Long.parseLong(header.substring(6, header.indexOf(' ', 6))); String name = header.substring(header.indexOf(' ', 6) + 1); if (length < 0L) { // TODO consider throwing an exception... @@ -234,7 +246,7 @@ public class ScpHelper { } bufSize = MIN_RECEIVE_BUFFER_SIZE; } else { - bufSize= (int) Math.min(length, bufferSize); + bufSize = (int) Math.min(length, bufferSize); } if (bufSize < 0) { // TODO consider throwing an exception @@ -258,15 +270,25 @@ public class ScpHelper { } else if (Files.exists(file) && !Files.isWritable(file)) { throw new IOException("Can not write to file: " + file); } - - try (InputStream is = new LimitInputStream(this.in, length); - OutputStream os = Files.newOutputStream(file)) { + + try ( + InputStream is = new LimitInputStream(this.in, length); + OutputStream os = Files.newOutputStream(file) + ) { ack(); - IoUtils.copy(is, os, bufSize); + + try { + listener.startFileEvent(FileOperation.RECEIVE, file, length, perms); + IoUtils.copy(is, os, bufSize); + listener.endFileEvent(FileOperation.RECEIVE, file, length, perms, null); + } catch (IOException | RuntimeException e) { + listener.endFileEvent(FileOperation.RECEIVE, file, length, perms, e); + throw e; + } } if (preserve) { - setOctalPerms(file, perms); + IoUtils.setPermissions(file, perms); if (time != null) { Files.getFileAttributeView(file, BasicFileAttributeView.class) .setTimes(FileTime.from(time[0], TimeUnit.SECONDS), @@ -285,13 +307,13 @@ public class ScpHelper { public String readLine(boolean canEof) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (;;) { + for (; ; ) { int c = in.read(); if (c == '\n') { return baos.toString(); } else if (c == -1) { if (!canEof) { - throw new EOFException(); + throw new EOFException("EOF while await end of line"); } return null; } else { @@ -370,7 +392,7 @@ public class ScpHelper { BasicFileAttributes basic = Files.getFileAttributeView(path, BasicFileAttributeView.class).readAttributes(); if (preserve) { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); buf.append("T"); buf.append(basic.lastModifiedTime().to(TimeUnit.SECONDS)); buf.append(" "); @@ -385,9 +407,10 @@ public class ScpHelper { readAck(false); } - StringBuffer buf = new StringBuffer(); + Set<PosixFilePermission> perms = IoUtils.getPermissions(path); + StringBuilder buf = new StringBuilder(); buf.append("C"); - buf.append(preserve ? getOctalPerms(path) : "0644"); + buf.append(preserve ? getOctalPerms(perms) : "0644"); buf.append(" "); buf.append(basic.size()); // length buf.append(" "); @@ -418,9 +441,15 @@ public class ScpHelper { bufSize = MIN_SEND_BUFFER_SIZE; } - // TODO: use bufSize try (InputStream in = Files.newInputStream(path)) { - IoUtils.copy(in, out, bufSize); + try { + listener.startFileEvent(FileOperation.SEND, path, fileSize, perms); + IoUtils.copy(in, out, bufSize); + listener.endFileEvent(FileOperation.SEND, path, fileSize, perms, null); + } catch (IOException | RuntimeException e) { + listener.endFileEvent(FileOperation.SEND, path, fileSize, perms, e); + throw e; + } } ack(); readAck(false); @@ -432,7 +461,7 @@ public class ScpHelper { } BasicFileAttributes basic = Files.getFileAttributeView(path, BasicFileAttributeView.class).readAttributes(); if (preserve) { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); buf.append("T"); buf.append(basic.lastModifiedTime().to(TimeUnit.SECONDS)); buf.append(" "); @@ -447,9 +476,10 @@ public class ScpHelper { readAck(false); } - StringBuffer buf = new StringBuffer(); + Set<PosixFilePermission> perms = IoUtils.getPermissions(path); + StringBuilder buf = new StringBuilder(); buf.append("D"); - buf.append(preserve ? getOctalPerms(path) : "0755"); + buf.append(preserve ? getOctalPerms(perms) : "0755"); buf.append(" "); buf.append("0"); // length buf.append(" "); @@ -460,12 +490,21 @@ public class ScpHelper { readAck(false); try (DirectoryStream<Path> children = Files.newDirectoryStream(path)) { - for (Path child : children) { - if (Files.isRegularFile(child)) { - sendFile(child, preserve, bufferSize); - } else if (Files.isDirectory(child)) { - sendDir(child, preserve, bufferSize); + listener.startFolderEvent(FileOperation.SEND, path, perms); + + try { + for (Path child : children) { + if (Files.isRegularFile(child)) { + sendFile(child, preserve, bufferSize); + } else if (Files.isDirectory(child)) { + sendDir(child, preserve, bufferSize); + } } + + listener.endFolderEvent(FileOperation.SEND, path, perms, null); + } catch (IOException | RuntimeException e) { + listener.endFolderEvent(FileOperation.SEND, path, perms, e); + throw e; } } @@ -476,61 +515,60 @@ public class ScpHelper { private long[] parseTime(String line) { String[] numbers = line.substring(1).split(" "); - return new long[] { Long.parseLong(numbers[0]), Long.parseLong(numbers[2]) }; + return new long[]{Long.parseLong(numbers[0]), Long.parseLong(numbers[2])}; } public static String getOctalPerms(Path path) throws IOException { + return getOctalPerms(IoUtils.getPermissions(path)); + } + + public static String getOctalPerms(Collection<PosixFilePermission> perms) { int pf = 0; - if (path.getFileSystem().supportedFileAttributeViews().contains("posix")) { - Set<PosixFilePermission> perms = Files.getPosixFilePermissions(path); - for (PosixFilePermission p : perms) { - switch (p) { - case OWNER_READ: - pf |= S_IRUSR; - break; - case OWNER_WRITE: - pf |= S_IWUSR; - break; - case OWNER_EXECUTE: - pf |= S_IXUSR; - break; - case GROUP_READ: - pf |= S_IRGRP; - break; - case GROUP_WRITE: - pf |= S_IWGRP; - break; - case GROUP_EXECUTE: - pf |= S_IXGRP; - break; - case OTHERS_READ: - pf |= S_IROTH; - break; - case OTHERS_WRITE: - pf |= S_IWOTH; - break; - case OTHERS_EXECUTE: - pf |= S_IXOTH; - break; - } - } - } else { - if (Files.isReadable(path)) { - pf |= S_IRUSR | S_IRGRP | S_IROTH; - } - if (Files.isWritable(path)) { - pf |= S_IWUSR | S_IWGRP | S_IWOTH; - } - if (Files.isExecutable(path)) { - pf |= S_IXUSR | S_IXGRP | S_IXOTH; + + for (PosixFilePermission p : perms) { + switch (p) { + case OWNER_READ: + pf |= S_IRUSR; + break; + case OWNER_WRITE: + pf |= S_IWUSR; + break; + case OWNER_EXECUTE: + pf |= S_IXUSR; + break; + case GROUP_READ: + pf |= S_IRGRP; + break; + case GROUP_WRITE: + pf |= S_IWGRP; + break; + case GROUP_EXECUTE: + pf |= S_IXGRP; + break; + case OTHERS_READ: + pf |= S_IROTH; + break; + case OTHERS_WRITE: + pf |= S_IWOTH; + break; + case OTHERS_EXECUTE: + pf |= S_IXOTH; + break; } } + return String.format("%04o", pf); } - public static void setOctalPerms(Path path, String str) throws IOException { + public static Set<PosixFilePermission> setOctalPerms(Path path, String str) throws IOException { + Set<PosixFilePermission> perms = parseOctalPerms(str); + IoUtils.setPermissions(path, perms); + return perms; + } + + public static Set<PosixFilePermission> parseOctalPerms(String str) { int perms = Integer.parseInt(str, 8); - EnumSet<PosixFilePermission> p = EnumSet.noneOf(PosixFilePermission.class); + Set<PosixFilePermission> p = EnumSet.noneOf(PosixFilePermission.class); if ((perms & S_IRUSR) != 0) { p.add(PosixFilePermission.OWNER_READ); } @@ -558,11 +596,8 @@ public class ScpHelper { if ((perms & S_IXOTH) != 0) { p.add(PosixFilePermission.OTHERS_EXECUTE); } - if (path.getFileSystem().supportedFileAttributeViews().contains("posix")) { - Files.setPosixFilePermissions(path, p); - } else { - log.warn("Unable to set file permissions because the underlying file system does not support posix permissions"); - } + + return p; } public void ack() throws IOException { @@ -573,20 +608,20 @@ public class ScpHelper { public int readAck(boolean canEof) throws IOException { int c = in.read(); switch (c) { - case -1: - if (!canEof) { - throw new EOFException(); - } - break; - case OK: - break; - case WARNING: - log.warn("Received warning: " + readLine()); - break; - case ERROR: - throw new IOException("Received nack: " + readLine()); - default: - break; + case -1: + if (!canEof) { + throw new EOFException("readAck - EOF before ACK"); + } + break; + case OK: + break; + case WARNING: + log.warn("Received warning: " + readLine()); + break; + case ERROR: + throw new IOException("Received nack: " + readLine()); + default: + break; } return c; } @@ -605,7 +640,7 @@ public class ScpHelper { if (remaining > 0) { remaining--; return super.read(); - } else{ + } else { return -1; } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2cd0ebba/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpTransferEventListener.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpTransferEventListener.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpTransferEventListener.java new file mode 100644 index 0000000..fb82707 --- /dev/null +++ b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpTransferEventListener.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.sshd.common.scp; + +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.util.EventListener; +import java.util.Set; + +/** + * Can be registered in order to receive events about SCP transfers + * + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +public interface ScpTransferEventListener extends EventListener { + + enum FileOperation { + SEND, + RECEIVE + } + + /** + * @param op The {@link FileOperation} + * @param file The <U>local</U> referenced file {@link Path} + * @param length Size (in bytes) of transfered data + * @param perms A {@link Set} of {@link PosixFilePermission}s to be applied + * once transfer is complete + */ + void startFileEvent(FileOperation op, Path file, long length, Set<PosixFilePermission> perms); + + /** + * @param op The {@link FileOperation} + * @param file The <U>local</U> referenced file {@link Path} + * @param length Size (in bytes) of transfered data + * @param perms A {@link Set} of {@link PosixFilePermission}s to be applied + * once transfer is complete + * @param thrown The result of the operation attempt - if {@code null} then + * reception was successful + */ + void endFileEvent(FileOperation op, Path file, long length, Set<PosixFilePermission> perms, Throwable thrown); + + /** + * @param op The {@link FileOperation} + * @param file The <U>local</U> referenced folder {@link Path} + * @param perms A {@link Set} of {@link PosixFilePermission}s to be applied + * once transfer is complete + */ + void startFolderEvent(FileOperation op, Path file, Set<PosixFilePermission> perms); + + /** + * @param op The {@link FileOperation} + * @param file The <U>local</U> referenced file {@link Path} + * @param perms A {@link Set} of {@link PosixFilePermission}s to be applied + * once transfer is complete + * @param thrown The result of the operation attempt - if {@code null} then + * reception was successful + */ + void endFolderEvent(FileOperation op, Path file, Set<PosixFilePermission> perms, Throwable thrown); + + /** + * An "empty" implementation to be used instead of {@code null}s + */ + ScpTransferEventListener EMPTY = new ScpTransferEventListener() { + // TODO in JDK 8.0 implement all methods as default with empty body in the interface itself + + @Override + public void startFileEvent(FileOperation op, Path file, long length, Set<PosixFilePermission> perms) { + // ignored + } + + @Override + public void endFileEvent(FileOperation op, Path file, long length, Set<PosixFilePermission> perms, Throwable thrown) { + // ignored + } + + @Override + public void startFolderEvent(FileOperation op, Path file, Set<PosixFilePermission> perms) { + // ignored + } + + @Override + public void endFolderEvent(FileOperation op, Path file, Set<PosixFilePermission> perms, Throwable thrown) { + // ignored + } + }; +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2cd0ebba/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java index cb21985..e62b231 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/util/IoUtils.java @@ -23,7 +23,10 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.FileSystem; +import java.nio.file.Files; import java.nio.file.LinkOption; +import java.nio.file.Path; import java.nio.file.attribute.PosixFilePermission; import java.util.Arrays; import java.util.Collection; @@ -42,7 +45,7 @@ public class IoUtils { public static final LinkOption[] EMPTY_OPTIONS = new LinkOption[0]; private static final LinkOption[] NO_FOLLOW_OPTIONS = new LinkOption[] { LinkOption.NOFOLLOW_LINKS }; - + public static LinkOption[] getLinkOptions(boolean followLinks) { if (followLinks) { return EMPTY_OPTIONS; @@ -97,6 +100,25 @@ public class IoUtils { } /** + * If the "posix" view is supported, then it returns + * {@link Files#getPosixFilePermissions(Path, LinkOption...)}, otherwise + * uses the {@link #getPermissionsFromFile(File)} method + * @param path The {@link Path} + * @return A {@link Set} of {@link PosixFilePermission} + * @throws IOException If failed to access the file system in order to + * retrieve the permissions + */ + public static Set<PosixFilePermission> getPermissions(Path path) throws IOException { + FileSystem fs = path.getFileSystem(); + Collection<String> views = fs.supportedFileAttributeViews(); + if (views.contains("posix")) { + return Files.getPosixFilePermissions(path, getLinkOptions(false)); + } else { + return getPermissionsFromFile(path.toFile()); + } + } + + /** * @param f The {@link File} to be checked * @return A {@link Set} of {@link PosixFilePermission}s based on whether * the file is readable/writable/executable. If so, then <U>all</U> the @@ -124,7 +146,25 @@ public class IoUtils { return perms; } - + + /** + * If the "posix" view is supported, then it invokes + * {@link Files#setPosixFilePermissions(Path, Set)}, otherwise + * uses the {@link #setPermissionsToFile(File, Collection)} method + * @param path The {@link Path} + * @param perms The {@link Set} of {@link PosixFilePermission}s + * @throws IOException If failed to access the file system + */ + public static void setPermissions(Path path, Set<PosixFilePermission> perms) throws IOException { + FileSystem fs = path.getFileSystem(); + Collection<String> views = fs.supportedFileAttributeViews(); + if (views.contains("posix")) { + Files.setPosixFilePermissions(path, perms); + } else { + setPermissionsToFile(path.toFile(), perms); + } + } + /** * @param f The {@link File} * @param perms A {@link Collection} of {@link PosixFilePermission}s to set on it. http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2cd0ebba/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java index 381773d..ade3866 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java @@ -21,14 +21,15 @@ package org.apache.sshd.server.command; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.FileSystem; import java.util.Collection; import java.util.Collections; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import java.nio.file.FileSystem; import org.apache.sshd.common.file.FileSystemAware; import org.apache.sshd.common.scp.ScpHelper; +import org.apache.sshd.common.scp.ScpTransferEventListener; import org.apache.sshd.common.util.ThreadUtils; import org.apache.sshd.server.Command; import org.apache.sshd.server.Environment; @@ -65,6 +66,7 @@ public class ScpCommand implements Command, Runnable, FileSystemAware { protected Future<?> pendingFuture; protected int sendBufferSize; protected int receiveBufferSize; + protected ScpTransferEventListener listener; /** * @param command The command to be executed @@ -76,11 +78,12 @@ public class ScpCommand implements Command, Runnable, FileSystemAware { * service, which will be shutdown regardless * @param sendSize Size (in bytes) of buffer to use when sending files * @param receiveSize Size (in bytes) of buffer to use when receiving files + * @param eventListener An {@link ScpTransferEventListener} - may be {@code null} * @see ThreadUtils#newSingleThreadExecutor(String) * @see ScpHelper#MIN_SEND_BUFFER_SIZE * @see ScpHelper#MIN_RECEIVE_BUFFER_SIZE */ - public ScpCommand(String command, ExecutorService executorService, boolean shutdownOnExit, int sendSize, int receiveSize) { + public ScpCommand(String command, ExecutorService executorService, boolean shutdownOnExit, int sendSize, int receiveSize, ScpTransferEventListener eventListener) { name = command; if ((executors = executorService) == null) { @@ -99,6 +102,8 @@ public class ScpCommand implements Command, Runnable, FileSystemAware { throw new IllegalArgumentException("<ScpCommmand>(" + command + ") receive buffer size (" + sendSize + ") below minimum required (" + ScpHelper.MIN_RECEIVE_BUFFER_SIZE + ")"); } + listener = (eventListener == null) ? ScpTransferEventListener.EMPTY : eventListener; + log.debug("Executing command {}", command); String[] args = command.split(" "); for (int i = 1; i < args.length; i++) { @@ -204,7 +209,7 @@ public class ScpCommand implements Command, Runnable, FileSystemAware { public void run() { int exitValue = ScpHelper.OK; String exitMessage = null; - ScpHelper helper = new ScpHelper(in, out, fileSystem); + ScpHelper helper = new ScpHelper(in, out, fileSystem, listener); try { if (optT) { helper.receive(fileSystem.getPath(path), optR, optD, optP, receiveBufferSize); http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2cd0ebba/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommandFactory.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommandFactory.java b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommandFactory.java index 2daf2d8..ed3db39 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommandFactory.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommandFactory.java @@ -18,9 +18,13 @@ */ package org.apache.sshd.server.command; +import java.util.Collection; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutorService; import org.apache.sshd.common.scp.ScpHelper; +import org.apache.sshd.common.scp.ScpTransferEventListener; +import org.apache.sshd.common.util.EventListenerUtils; import org.apache.sshd.common.util.ObjectBuilder; import org.apache.sshd.server.Command; import org.apache.sshd.server.CommandFactory; @@ -44,7 +48,6 @@ public class ScpCommandFactory implements CommandFactory, Cloneable { * A useful {@link ObjectBuilder} for {@link ScpCommandFactory} */ public static class Builder implements ObjectBuilder<ScpCommandFactory> { - private final ScpCommandFactory factory = new ScpCommandFactory(); public Builder() { @@ -76,8 +79,17 @@ public class ScpCommandFactory implements CommandFactory, Cloneable { return this; } + public Builder addEventListener(ScpTransferEventListener listener) { + factory.addEventListener(listener); + return this; + } + + public Builder removeEventListener(ScpTransferEventListener listener) { + factory.removeEventListener(listener); + return this; + } + public ScpCommandFactory build() { - // return a clone so that each invocation returns a different instance - avoid shared instances return factory.clone(); } } @@ -91,9 +103,11 @@ public class ScpCommandFactory implements CommandFactory, Cloneable { private boolean shutdownExecutor; private int sendBufferSize = ScpHelper.MIN_SEND_BUFFER_SIZE; private int receiveBufferSize = ScpHelper.MIN_RECEIVE_BUFFER_SIZE; + private Collection<ScpTransferEventListener> listeners = new CopyOnWriteArraySet<>(); + private ScpTransferEventListener listenerProxy; public ScpCommandFactory() { - super(); + listenerProxy = EventListenerUtils.proxyWrapper(ScpTransferEventListener.class, getClass().getClassLoader(), listeners); } public CommandFactory getDelegateCommandFactory() { @@ -167,6 +181,34 @@ public class ScpCommandFactory implements CommandFactory, Cloneable { } /** + * @param listener The {@link ScpTransferEventListener} to add + * @return {@code true} if this is a <U>new</U> listener instance, + * {@code false} if the listener is already registered + * @throws IllegalArgumentException if {@code null} listener + */ + public boolean addEventListener(ScpTransferEventListener listener) { + if (listener == null) { + throw new IllegalArgumentException("No listener instance"); + } + + return listeners.add(listener); + } + + /** + * @param listener The {@link ScpTransferEventListener} to remove + * @return {@code true} if the listener was registered and removed, + * {@code false} if the listener was not registered to begin with + * @throws IllegalArgumentException if {@code null} listener + */ + public boolean removeEventListener(ScpTransferEventListener listener) { + if (listener == null) { + throw new IllegalArgumentException("No listener instance"); + } + + return listeners.remove(listener); + } + + /** * Parses a command string and verifies that the basic syntax is * correct. If parsing fails the responsibility is delegated to * the configured {@link CommandFactory} instance; if one exist. @@ -179,7 +221,7 @@ public class ScpCommandFactory implements CommandFactory, Cloneable { */ public Command createCommand(String command) { if (command.startsWith(SCP_COMMAND_PREFIX)) { - return new ScpCommand(command, getExecutorService(), isShutdownOnExit(), getSendBufferSize(), getReceiveBufferSize()); + return new ScpCommand(command, getExecutorService(), isShutdownOnExit(), getSendBufferSize(), getReceiveBufferSize(), listenerProxy); } CommandFactory factory = getDelegateCommandFactory(); @@ -193,7 +235,14 @@ public class ScpCommandFactory implements CommandFactory, Cloneable { @Override public ScpCommandFactory clone() { try { - return getClass().cast(super.clone()); // shallow clone is good enough + ScpCommandFactory other = getClass().cast(super.clone()); + // clone the listeners set as well + other.listeners = this.listeners.isEmpty() + ? new CopyOnWriteArraySet<ScpTransferEventListener>() + : new CopyOnWriteArraySet<>(this.listeners) + ; + other.listenerProxy = EventListenerUtils.proxyWrapper(ScpTransferEventListener.class, getClass().getClassLoader(), other.listeners); + return other; } catch(CloneNotSupportedException e) { throw new RuntimeException(e); // un-expected... } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2cd0ebba/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java index 77f09b5..1e6d305 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java @@ -806,7 +806,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste Path p = resolveFile(path); if (!Files.exists(p)) { sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString()); - } else if (Files.isDirectory(p, LinkOption.NOFOLLOW_LINKS)) { + } else if (Files.isDirectory(p, IoUtils.getLinkOptions(false))) { sendStatus(id, SSH_FX_NO_SUCH_FILE, p.toString()); } else { Files.delete(p); @@ -1521,7 +1521,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste } if (view != null && value != null) { try { - Files.setAttribute(file, view + ":" + attribute, value, LinkOption.NOFOLLOW_LINKS); + Files.setAttribute(file, view + ":" + attribute, value, IoUtils.getLinkOptions(false)); } catch (UnsupportedOperationException e) { unsupported.add(attribute); } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2cd0ebba/sshd-core/src/test/java/org/apache/sshd/ScpTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/ScpTest.java b/sshd-core/src/test/java/org/apache/sshd/ScpTest.java index a92c4c7..7b33ca1 100644 --- a/sshd-core/src/test/java/org/apache/sshd/ScpTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/ScpTest.java @@ -25,17 +25,21 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.channels.FileChannel; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Collection; import java.util.Properties; +import java.util.Set; import java.util.concurrent.TimeUnit; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.SCPClient; - import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; - import org.apache.sshd.client.ScpClient; +import org.apache.sshd.common.scp.ScpTransferEventListener; +import org.apache.sshd.common.util.OsUtils; import org.apache.sshd.server.command.ScpCommandFactory; import org.apache.sshd.util.BaseTest; import org.apache.sshd.util.BogusPasswordAuthenticator; @@ -44,9 +48,12 @@ import org.apache.sshd.util.JSchLogger; import org.apache.sshd.util.SimpleUserInfo; import org.apache.sshd.util.Utils; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -101,413 +108,432 @@ public class ScpTest extends BaseTest { @Test public void testUploadAbsoluteDriveLetter() throws Exception { - SshClient client = SshClient.setUpDefaultClient(); - client.start(); - ClientSession session = client.connect("test", "localhost", port).await().getSession(); - session.addPasswordIdentity("test"); - session.auth().verify(); + try (SshClient client = SshClient.setUpDefaultClient()) { + client.start(); - ScpClient scp = session.createScpClient(); + try { + try (ClientSession session = client.connect("test", "localhost", port).await().getSession()) { + session.addPasswordIdentity("test"); + session.auth().verify(); - String data = "0123456789\n"; + ScpClient scp = createScpClient(session); - File root = new File("target/scp"); - Utils.deleteRecursive(root); - root.mkdirs(); - new File(root, "local").mkdirs(); - assertTrue(root.exists()); + String data = "0123456789\n"; + File root = new File("target/scp"); + Utils.deleteRecursive(root); + assertHierarchyTargetFolderExists(new File(root, "local")); + assertTrue("Root folder not created", root.exists()); - writeFile(new File("target/scp/local/out.txt"), data); - new File(root, "remote").mkdirs(); - scp.upload(new File("target/scp/local/out.txt").getAbsolutePath(), "/" + new File("target/scp/remote/out.txt").getAbsolutePath().replace(File.separatorChar, '/')); - assertFileLength(new File("target/scp/remote/out.txt"), data.length(), 5000); - scp.upload(new File("target/scp/local/out.txt").getAbsolutePath(), new File("target/scp/remote/out2.txt").getAbsolutePath()); - assertFileLength(new File("target/scp/remote/out2.txt"), data.length(), 5000); + writeFile(new File("target/scp/local/out.txt"), data); + assertHierarchyTargetFolderExists(new File(root, "remote")); + scp.upload(new File("target/scp/local/out.txt").getAbsolutePath(), "/" + new File("target/scp/remote/out.txt").getAbsolutePath().replace(File.separatorChar, '/')); + assertFileLength(new File("target/scp/remote/out.txt"), data.length(), 5000); + scp.upload(new File("target/scp/local/out.txt").getAbsolutePath(), new File("target/scp/remote/out2.txt").getAbsolutePath()); + assertFileLength(new File("target/scp/remote/out2.txt"), data.length(), 5000); + } + } finally { + client.stop(); + } + } } @Test public void testScpUploadOverwrite() throws Exception { - SshClient client = SshClient.setUpDefaultClient(); - client.start(); - ClientSession session = client.connect("test", "localhost", port).await().getSession(); - session.addPasswordIdentity("test"); - session.auth().verify(); + try (SshClient client = SshClient.setUpDefaultClient()) { + client.start(); - ScpClient scp = session.createScpClient(); + try { + try (ClientSession session = client.connect("test", "localhost", port).await().getSession()) { + session.addPasswordIdentity("test"); + session.auth().verify(); - String data = "0123456789\n"; + ScpClient scp = createScpClient(session); - File root = new File("target/scp"); - Utils.deleteRecursive(root); - root.mkdirs(); - new File(root, "local").mkdirs(); - assertTrue(root.exists()); + String data = "0123456789\n"; - new File(root, "remote").mkdirs(); - writeFile(new File("target/scp/remote/out.txt"), data + data); + File root = new File("target/scp"); + Utils.deleteRecursive(root); + root.mkdirs(); + new File(root, "local").mkdirs(); + assertTrue(root.exists()); - writeFile(new File("target/scp/local/out.txt"), data); - scp.upload("target/scp/local/out.txt", "target/scp/remote/out.txt"); - assertFileLength(new File("target/scp/remote/out.txt"), data.length(), 5000); + new File(root, "remote").mkdirs(); + writeFile(new File("target/scp/remote/out.txt"), data + data); - session.close(false).await(); - client.stop(); + writeFile(new File("target/scp/local/out.txt"), data); + scp.upload("target/scp/local/out.txt", "target/scp/remote/out.txt"); + assertFileLength(new File("target/scp/remote/out.txt"), data.length(), 5000); + } + } finally { + client.stop(); + } + } } @Test public void testScpUploadZeroLengthFile() throws Exception { - File root=assertHierarchyTargetFolderExists(new File("target/scp")); - File local=assertHierarchyTargetFolderExists(new File(root, "local")); - File remote=assertHierarchyTargetFolderExists(new File(root, "remote")); - File zeroLocal=new File(local, getCurrentTestName()); + File root = assertHierarchyTargetFolderExists(new File("target/scp")); + File local = assertHierarchyTargetFolderExists(new File(root, "local")); + File remote = assertHierarchyTargetFolderExists(new File(root, "remote")); + File zeroLocal = new File(local, getCurrentTestName()); - FileOutputStream fout=new FileOutputStream(zeroLocal); - try { + try (FileOutputStream fout = new FileOutputStream(zeroLocal)) { if (zeroLocal.length() > 0L) { - FileChannel fch=fout.getChannel(); + FileChannel fch = fout.getChannel(); try { fch.truncate(0L); } finally { fch.close(); } } - } finally { - fout.close(); } assertEquals("Non-zero size for local file=" + zeroLocal.getAbsolutePath(), 0L, zeroLocal.length()); - File zeroRemote=new File(remote, zeroLocal.getName()); + File zeroRemote = new File(remote, zeroLocal.getName()); if (zeroRemote.exists()) { assertTrue("Failed to delete remote target " + zeroRemote.getAbsolutePath(), zeroRemote.delete()); } - SshClient client = SshClient.setUpDefaultClient(); - try { - client.start(); - - ClientSession session = client.connect("test", "localhost", port).await().getSession(); + try (SshClient client = SshClient.setUpDefaultClient()) { try { - session.addPasswordIdentity("test"); - session.auth().verify(); - - ScpClient scp = session.createScpClient(); - scp.upload("target/scp/local/" + zeroLocal.getName(), "target/scp/remote/" + zeroRemote.getName()); - assertFileLength(zeroRemote, 0L, TimeUnit.SECONDS.toMillis(5L)); + client.start(); + + try (ClientSession session = client.connect("test", "localhost", port).await().getSession()) { + session.addPasswordIdentity("test"); + session.auth().verify(); + + ScpClient scp = createScpClient(session); + scp.upload("target/scp/local/" + zeroLocal.getName(), "target/scp/remote/" + zeroRemote.getName()); + assertFileLength(zeroRemote, 0L, TimeUnit.SECONDS.toMillis(5L)); + } } finally { - session.close(false).await(); + client.stop(); } - } finally { - client.stop(); } } @Test public void testScpDownloadZeroLengthFile() throws Exception { - File root=assertHierarchyTargetFolderExists(new File("target/scp")); - File local=assertHierarchyTargetFolderExists(new File(root, "local")); - File remote=assertHierarchyTargetFolderExists(new File(root, "remote")); - File zeroLocal=new File(local, getCurrentTestName()); + File root = assertHierarchyTargetFolderExists(new File("target/scp")); + File local = assertHierarchyTargetFolderExists(new File(root, "local")); + File remote = assertHierarchyTargetFolderExists(new File(root, "remote")); + File zeroLocal = new File(local, getCurrentTestName()); if (zeroLocal.exists()) { assertTrue("Failed to delete local target " + zeroLocal.getAbsolutePath(), zeroLocal.delete()); } - File zeroRemote=new File(remote, zeroLocal.getName()); - FileOutputStream fout=new FileOutputStream(zeroRemote); - try { + File zeroRemote = new File(remote, zeroLocal.getName()); + try (FileOutputStream fout = new FileOutputStream(zeroRemote)) { if (zeroRemote.length() > 0L) { - FileChannel fch=fout.getChannel(); + FileChannel fch = fout.getChannel(); try { fch.truncate(0L); } finally { fch.close(); } } - } finally { - fout.close(); } assertEquals("Non-zero size for remote file=" + zeroRemote.getAbsolutePath(), 0L, zeroRemote.length()); - SshClient client = SshClient.setUpDefaultClient(); - try { - client.start(); - - ClientSession session = client.connect("test", "localhost", port).await().getSession(); + try (SshClient client = SshClient.setUpDefaultClient()) { try { - session.addPasswordIdentity("test"); - session.auth().verify(); - - ScpClient scp = session.createScpClient(); - scp.download("target/scp/remote/" + zeroRemote.getName(), "target/scp/local/" + zeroLocal.getName()); - assertFileLength(zeroLocal, 0L, TimeUnit.SECONDS.toMillis(5L)); + client.start(); + + try (ClientSession session = client.connect("test", "localhost", port).await().getSession()) { + session.addPasswordIdentity("test"); + session.auth().verify(); + + ScpClient scp = createScpClient(session); + scp.download("target/scp/remote/" + zeroRemote.getName(), "target/scp/local/" + zeroLocal.getName()); + assertFileLength(zeroLocal, 0L, TimeUnit.SECONDS.toMillis(5L)); + } } finally { - session.close(false).await(); + client.stop(); } - } finally { - client.stop(); } } @Test public void testScpNativeOnSingleFile() throws Exception { - SshClient client = SshClient.setUpDefaultClient(); - client.start(); - ClientSession session = client.connect("test", "localhost", port).await().getSession(); - session.addPasswordIdentity("test"); - session.auth().verify(); - - ScpClient scp = session.createScpClient(); - - String data = "0123456789\n"; - - File root = new File("target/scp"); - Utils.deleteRecursive(root); - root.mkdirs(); - new File(root, "local").mkdirs(); - assertTrue(root.exists()); - - - writeFile(new File("target/scp/local/out.txt"), data); - try { - scp.upload("target/scp/local/out.txt", "target/scp/remote/out.txt"); - fail("Expected IOException"); - } catch (IOException e) { - // ok + try (SshClient client = SshClient.setUpDefaultClient()) { + client.start(); + try { + try (ClientSession session = client.connect("test", "localhost", port).await().getSession()) { + session.addPasswordIdentity("test"); + session.auth().verify(); + + ScpClient scp = createScpClient(session); + + String data = "0123456789\n"; + + File root = new File("target/scp"); + Utils.deleteRecursive(root); + root.mkdirs(); + new File(root, "local").mkdirs(); + assertTrue(root.exists()); + + + writeFile(new File("target/scp/local/out.txt"), data); + try { + scp.upload("target/scp/local/out.txt", "target/scp/remote/out.txt"); + fail("Expected IOException"); + } catch (IOException e) { + // ok + } + new File(root, "remote").mkdirs(); + scp.upload("target/scp/local/out.txt", "target/scp/remote/out.txt"); + assertFileLength(new File("target/scp/remote/out.txt"), data.length(), 5000); + + scp.download("target/scp/remote/out.txt", "target/scp/local/out2.txt"); + assertFileLength(new File("target/scp/local/out2.txt"), data.length(), 5000); + } + } finally { + client.stop(); + } } - new File(root, "remote").mkdirs(); - scp.upload("target/scp/local/out.txt", "target/scp/remote/out.txt"); - assertFileLength(new File("target/scp/remote/out.txt"), data.length(), 5000); - - scp.download("target/scp/remote/out.txt", "target/scp/local/out2.txt"); - assertFileLength(new File("target/scp/local/out2.txt"), data.length(), 5000); - - session.close(false).await(); - client.stop(); } @Test public void testScpNativeOnMultipleFiles() throws Exception { - SshClient client = SshClient.setUpDefaultClient(); - client.start(); - ClientSession session = client.connect("test", "localhost", port).await().getSession(); - session.addPasswordIdentity("test"); - session.auth().verify(); - - ScpClient scp = session.createScpClient(); - - String data = "0123456789\n"; - - File root = new File("target/scp"); - Utils.deleteRecursive(root); - root.mkdirs(); - new File(root, "local").mkdirs(); - new File(root, "remote").mkdirs(); - assertTrue(root.exists()); - - - writeFile(new File("target/scp/local/out1.txt"), data); - writeFile(new File("target/scp/local/out2.txt"), data); - try { - scp.upload(new String[] { "target/scp/local/out1.txt", "target/scp/local/out2.txt" }, "target/scp/remote/out.txt"); - fail("Expected IOException"); - } catch (IOException e) { - // Ok - } - writeFile(new File("target/scp/remote/out.txt"), data); - try { - scp.upload(new String[] { "target/scp/local/out1.txt", "target/scp/local/out2.txt" }, "target/scp/remote/out.txt"); - fail("Expected IOException"); - } catch (IOException e) { - // Ok - } - new File(root, "remote/dir").mkdirs(); - scp.upload(new String[] { "target/scp/local/out1.txt", "target/scp/local/out2.txt" }, "target/scp/remote/dir"); - assertFileLength(new File("target/scp/remote/dir/out1.txt"), data.length(), 5000); - assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000); + try (SshClient client = SshClient.setUpDefaultClient()) { + client.start(); - try { - scp.download(new String[] { "target/scp/remote/dir/out1.txt", "target/scp/remote/dir/out2.txt" }, "target/scp/local/out1.txt"); - fail("Expected IOException"); - } catch (IOException e) { - // Ok - } - try { - scp.download(new String[] { "target/scp/remote/dir/out1.txt", "target/scp/remote/dir/out2.txt" }, "target/scp/local/dir"); - fail("Expected IOException"); - } catch (IOException e) { - // Ok + try { + try (ClientSession session = client.connect("test", "localhost", port).await().getSession()) { + session.addPasswordIdentity("test"); + session.auth().verify(); + + ScpClient scp = createScpClient(session); + + String data = "0123456789\n"; + + File root = new File("target/scp"); + Utils.deleteRecursive(root); + root.mkdirs(); + new File(root, "local").mkdirs(); + new File(root, "remote").mkdirs(); + assertTrue(root.exists()); + + writeFile(new File("target/scp/local/out1.txt"), data); + writeFile(new File("target/scp/local/out2.txt"), data); + try { + scp.upload(new String[]{"target/scp/local/out1.txt", "target/scp/local/out2.txt"}, "target/scp/remote/out.txt"); + fail("Expected IOException"); + } catch (IOException e) { + // Ok + } + writeFile(new File("target/scp/remote/out.txt"), data); + try { + scp.upload(new String[]{"target/scp/local/out1.txt", "target/scp/local/out2.txt"}, "target/scp/remote/out.txt"); + fail("Expected IOException"); + } catch (IOException e) { + // Ok + } + new File(root, "remote/dir").mkdirs(); + scp.upload(new String[]{"target/scp/local/out1.txt", "target/scp/local/out2.txt"}, "target/scp/remote/dir"); + assertFileLength(new File("target/scp/remote/dir/out1.txt"), data.length(), 5000); + assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000); + + try { + scp.download(new String[]{"target/scp/remote/dir/out1.txt", "target/scp/remote/dir/out2.txt"}, "target/scp/local/out1.txt"); + fail("Expected IOException"); + } catch (IOException e) { + // Ok + } + try { + scp.download(new String[]{"target/scp/remote/dir/out1.txt", "target/scp/remote/dir/out2.txt"}, "target/scp/local/dir"); + fail("Expected IOException"); + } catch (IOException e) { + // Ok + } + new File(root, "local/dir").mkdirs(); + scp.download(new String[]{"target/scp/remote/dir/out1.txt", "target/scp/remote/dir/out2.txt"}, "target/scp/local/dir"); + assertFileLength(new File("target/scp/local/dir/out1.txt"), data.length(), 5000); + assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000); + } + } finally { + client.stop(); + } } - new File(root, "local/dir").mkdirs(); - scp.download(new String[] { "target/scp/remote/dir/out1.txt", "target/scp/remote/dir/out2.txt" }, "target/scp/local/dir"); - assertFileLength(new File("target/scp/local/dir/out1.txt"), data.length(), 5000); - assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000); - - session.close(false).await(); - client.stop(); } @Test public void testScpNativeOnRecursiveDirs() throws Exception { - SshClient client = SshClient.setUpDefaultClient(); - client.start(); - ClientSession session = client.connect("test", "localhost", port).await().getSession(); - session.addPasswordIdentity("test"); - session.auth().verify(); - - ScpClient scp = session.createScpClient(); - - String data = "0123456789\n"; - - File root = new File("target/scp"); - Utils.deleteRecursive(root); - root.mkdirs(); - new File(root, "local").mkdirs(); - new File(root, "remote").mkdirs(); - assertTrue(root.exists()); - - new File("target/scp/local/dir").mkdirs(); - writeFile(new File("target/scp/local/dir/out1.txt"), data); - writeFile(new File("target/scp/local/dir/out2.txt"), data); - scp.upload("target/scp/local/dir", "target/scp/remote/", ScpClient.Option.Recursive); - assertFileLength(new File("target/scp/remote/dir/out1.txt"), data.length(), 5000); - assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000); - - Utils.deleteRecursive(new File("target/scp/local/dir")); - scp.download("target/scp/remote/dir", "target/scp/local", ScpClient.Option.Recursive); - assertFileLength(new File("target/scp/local/dir/out1.txt"), data.length(), 5000); - assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000); + try (SshClient client = SshClient.setUpDefaultClient()) { + client.start(); - session.close(false).await(); - client.stop(); + try { + try (ClientSession session = client.connect("test", "localhost", port).await().getSession()) { + session.addPasswordIdentity("test"); + session.auth().verify(); + + ScpClient scp = createScpClient(session); + + String data = "0123456789\n"; + + File root = new File("target/scp"); + Utils.deleteRecursive(root); + root.mkdirs(); + new File(root, "local").mkdirs(); + new File(root, "remote").mkdirs(); + assertTrue(root.exists()); + + new File("target/scp/local/dir").mkdirs(); + writeFile(new File("target/scp/local/dir/out1.txt"), data); + writeFile(new File("target/scp/local/dir/out2.txt"), data); + scp.upload("target/scp/local/dir", "target/scp/remote/", ScpClient.Option.Recursive); + assertFileLength(new File("target/scp/remote/dir/out1.txt"), data.length(), 5000); + assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000); + + Utils.deleteRecursive(new File("target/scp/local/dir")); + scp.download("target/scp/remote/dir", "target/scp/local", ScpClient.Option.Recursive); + assertFileLength(new File("target/scp/local/dir/out1.txt"), data.length(), 5000); + assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000); + } + } finally { + client.stop(); + } + } } @Test public void testScpNativeOnDirWithPattern() throws Exception { - SshClient client = SshClient.setUpDefaultClient(); - client.start(); - ClientSession session = client.connect("test", "localhost", port).await().getSession(); - session.addPasswordIdentity("test"); - session.auth().verify(); - - ScpClient scp = session.createScpClient(); - - String data = "0123456789\n"; - - File root = new File("target/scp"); - Utils.deleteRecursive(root); - root.mkdirs(); - new File(root, "local").mkdirs(); - new File(root, "remote").mkdirs(); - assertTrue(root.exists()); - - writeFile(new File("target/scp/local/out1.txt"), data); - writeFile(new File("target/scp/local/out2.txt"), data); - scp.upload("target/scp/local/*", "target/scp/remote/"); - assertFileLength(new File("target/scp/remote/out1.txt"), data.length(), 5000); - assertFileLength(new File("target/scp/remote/out2.txt"), data.length(), 5000); - - new File("target/scp/local/out1.txt").delete(); - new File("target/scp/local/out2.txt").delete(); - scp.download("target/scp/remote/*", "target/scp/local"); - assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000); - assertFileLength(new File("target/scp/local/out2.txt"), data.length(), 5000); + try (SshClient client = SshClient.setUpDefaultClient()) { + client.start(); - session.close(false).await(); - client.stop(); + try { + try (ClientSession session = client.connect("test", "localhost", port).await().getSession()) { + session.addPasswordIdentity("test"); + session.auth().verify(); + + ScpClient scp = createScpClient(session); + + String data = "0123456789\n"; + + File root = new File("target/scp"); + Utils.deleteRecursive(root); + root.mkdirs(); + new File(root, "local").mkdirs(); + new File(root, "remote").mkdirs(); + assertTrue(root.exists()); + + writeFile(new File("target/scp/local/out1.txt"), data); + writeFile(new File("target/scp/local/out2.txt"), data); + scp.upload("target/scp/local/*", "target/scp/remote/"); + assertFileLength(new File("target/scp/remote/out1.txt"), data.length(), 5000); + assertFileLength(new File("target/scp/remote/out2.txt"), data.length(), 5000); + + new File("target/scp/local/out1.txt").delete(); + new File("target/scp/local/out2.txt").delete(); + scp.download("target/scp/remote/*", "target/scp/local"); + assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000); + assertFileLength(new File("target/scp/local/out2.txt"), data.length(), 5000); + } + } finally { + client.stop(); + } + } } @Test public void testScpNativeOnMixedDirAndFiles() throws Exception { - SshClient client = SshClient.setUpDefaultClient(); - client.start(); - ClientSession session = client.connect("test", "localhost", port).await().getSession(); - session.addPasswordIdentity("test"); - session.auth().verify(); - - ScpClient scp = session.createScpClient(); - - String data = "0123456789\n"; - - File root = new File("target/scp"); - Utils.deleteRecursive(root); - root.mkdirs(); - new File(root, "local").mkdirs(); - new File(root, "remote").mkdirs(); - assertTrue(root.exists()); + try (SshClient client = SshClient.setUpDefaultClient()) { + client.start(); - new File("target/scp/local/dir").mkdirs(); - writeFile(new File("target/scp/local/out1.txt"), data); - writeFile(new File("target/scp/local/dir/out2.txt"), data); - scp.upload("target/scp/local/*", "target/scp/remote/", ScpClient.Option.Recursive); - assertFileLength(new File("target/scp/remote/out1.txt"), data.length(), 5000); - assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000); - - Utils.deleteRecursive(new File("target/scp/local/out1.txt")); - Utils.deleteRecursive(new File("target/scp/local/dir")); - scp.download("target/scp/remote/*", "target/scp/local"); - assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000); - assertFalse(new File("target/scp/local/dir/out2.txt").exists()); - - Utils.deleteRecursive(new File("target/scp/local/out1.txt")); - scp.download("target/scp/remote/*", "target/scp/local", ScpClient.Option.Recursive); - assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000); - assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000); - - session.close(false).await(); - client.stop(); + try { + try (ClientSession session = client.connect("test", "localhost", port).await().getSession()) { + session.addPasswordIdentity("test"); + session.auth().verify(); + + ScpClient scp = createScpClient(session); + + String data = "0123456789\n"; + + File root = new File("target/scp"); + Utils.deleteRecursive(root); + root.mkdirs(); + new File(root, "local").mkdirs(); + new File(root, "remote").mkdirs(); + assertTrue(root.exists()); + + new File("target/scp/local/dir").mkdirs(); + writeFile(new File("target/scp/local/out1.txt"), data); + writeFile(new File("target/scp/local/dir/out2.txt"), data); + scp.upload("target/scp/local/*", "target/scp/remote/", ScpClient.Option.Recursive); + assertFileLength(new File("target/scp/remote/out1.txt"), data.length(), 5000); + assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000); + + Utils.deleteRecursive(new File("target/scp/local/out1.txt")); + Utils.deleteRecursive(new File("target/scp/local/dir")); + scp.download("target/scp/remote/*", "target/scp/local"); + assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000); + assertFalse(new File("target/scp/local/dir/out2.txt").exists()); + + Utils.deleteRecursive(new File("target/scp/local/out1.txt")); + scp.download("target/scp/remote/*", "target/scp/local", ScpClient.Option.Recursive); + assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000); + assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000); + } + } finally { + client.stop(); + } + } } @Test public void testScpNativePreserveAttributes() throws Exception { // Ignore this test if running a Windows system - if (System.getProperty("os.name").toLowerCase().contains("win")) { - return; - } - - SshClient client = SshClient.setUpDefaultClient(); - client.start(); - ClientSession session = client.connect("test", "localhost", port).await().getSession(); - session.addPasswordIdentity("test"); - session.auth().verify(); + Assume.assumeFalse("Skip test for Windows", OsUtils.isWin32()); - ScpClient scp = session.createScpClient(); - - String data = "0123456789\n"; - - File root = new File("target/scp"); - Utils.deleteRecursive(root); - root.mkdirs(); - new File(root, "local").mkdirs(); - new File(root, "remote").mkdirs(); - assertTrue(root.exists()); + try (SshClient client = SshClient.setUpDefaultClient()) { + client.start(); - new File("target/scp/local/dir").mkdirs(); - long lastMod = new File("target/scp/local/dir").lastModified() - TimeUnit.DAYS.toMillis(1); - - writeFile(new File("target/scp/local/out1.txt"), data); - writeFile(new File("target/scp/local/dir/out2.txt"), data); - new File("target/scp/local/out1.txt").setLastModified(lastMod); - new File("target/scp/local/out1.txt").setExecutable(true, true); - new File("target/scp/local/out1.txt").setWritable(false, false); - new File("target/scp/local/dir/out2.txt").setLastModified(lastMod); - scp.upload("target/scp/local/*", "target/scp/remote/", ScpClient.Option.Recursive, ScpClient.Option.PreserveAttributes); - assertFileLength(new File("target/scp/remote/out1.txt"), data.length(), 5000); - assertEquals(lastMod, new File("target/scp/remote/out1.txt").lastModified()); - assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000); - assertEquals(lastMod, new File("target/scp/remote/dir/out2.txt").lastModified()); - - Utils.deleteRecursive(new File("target/scp/local")); - new File("target/scp/local").mkdirs(); - scp.download("target/scp/remote/*", "target/scp/local", ScpClient.Option.Recursive, ScpClient.Option.PreserveAttributes); - assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000); - assertEquals(lastMod, new File("target/scp/local/out1.txt").lastModified()); - assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000); - assertEquals(lastMod, new File("target/scp/local/dir/out2.txt").lastModified()); - - session.close(false).await(); - client.stop(); + try { + try (ClientSession session = client.connect("test", "localhost", port).await().getSession()) { + session.addPasswordIdentity("test"); + session.auth().verify(); + + ScpClient scp = createScpClient(session); + + String data = "0123456789\n"; + + File root = new File("target/scp"); + Utils.deleteRecursive(root); + root.mkdirs(); + new File(root, "local").mkdirs(); + new File(root, "remote").mkdirs(); + assertTrue(root.exists()); + + new File("target/scp/local/dir").mkdirs(); + long lastMod = new File("target/scp/local/dir").lastModified() - TimeUnit.DAYS.toMillis(1); + + writeFile(new File("target/scp/local/out1.txt"), data); + writeFile(new File("target/scp/local/dir/out2.txt"), data); + new File("target/scp/local/out1.txt").setLastModified(lastMod); + new File("target/scp/local/out1.txt").setExecutable(true, true); + new File("target/scp/local/out1.txt").setWritable(false, false); + new File("target/scp/local/dir/out2.txt").setLastModified(lastMod); + scp.upload("target/scp/local/*", "target/scp/remote/", ScpClient.Option.Recursive, ScpClient.Option.PreserveAttributes); + assertFileLength(new File("target/scp/remote/out1.txt"), data.length(), 5000); + assertEquals(lastMod, new File("target/scp/remote/out1.txt").lastModified()); + assertFileLength(new File("target/scp/remote/dir/out2.txt"), data.length(), 5000); + assertEquals(lastMod, new File("target/scp/remote/dir/out2.txt").lastModified()); + + Utils.deleteRecursive(new File("target/scp/local")); + new File("target/scp/local").mkdirs(); + scp.download("target/scp/remote/*", "target/scp/local", ScpClient.Option.Recursive, ScpClient.Option.PreserveAttributes); + assertFileLength(new File("target/scp/local/out1.txt"), data.length(), 5000); + assertEquals(lastMod, new File("target/scp/local/out1.txt").lastModified()); + assertFileLength(new File("target/scp/local/dir/out2.txt"), data.length(), 5000); + assertEquals(lastMod, new File("target/scp/local/dir/out2.txt").lastModified()); + } + } finally { + client.stop(); + } + } } private void writeFile(File file, String data) throws IOException { @@ -598,7 +624,7 @@ public class ScpTest extends BaseTest { } } - protected void assertFileLength(File file, long length, long timeout) throws Exception{ + protected void assertFileLength(File file, long length, long timeout) throws Exception { boolean ok = false; while (timeout > 0) { if (file.exists() && file.length() == length) { @@ -694,7 +720,7 @@ public class ScpTest extends BaseTest { InputStream is = c.getInputStream(); c.connect(); assertEquals(0, is.read()); - os.write(("C7777 "+ data.length() + " " + name + "\n").getBytes()); + os.write(("C7777 " + data.length() + " " + name + "\n").getBytes()); os.flush(); assertEquals(0, is.read()); os.write(data.getBytes()); @@ -713,7 +739,7 @@ public class ScpTest extends BaseTest { c.setCommand("scp -t " + path); c.connect(); assertEquals(0, is.read()); - os.write(("C7777 "+ data.length() + " " + name + "\n").getBytes()); + os.write(("C7777 " + data.length() + " " + name + "\n").getBytes()); os.flush(); assertEquals(2, is.read()); c.disconnect(); @@ -742,9 +768,9 @@ public class ScpTest extends BaseTest { assertEquals(0, is.read()); } - private String readLine(InputStream in) throws IOException { + private static String readLine(InputStream in) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (;;) { + for (; ; ) { int c = in.read(); if (c == '\n') { return baos.toString(); @@ -756,4 +782,43 @@ public class ScpTest extends BaseTest { } } + private ScpClient createScpClient(ClientSession session) { + final Logger logger = LoggerFactory.getLogger(getClass().getName() + "[" + getCurrentTestName() + "]"); + return session.createScpClient(new ScpTransferEventListener() { + @Override + public void startFolderEvent(FileOperation op, Path file, Set<PosixFilePermission> perms) { + logEvent("starFolderEvent", op, file, false, -1L, perms, null); + } + + @Override + public void startFileEvent(FileOperation op, Path file, long length, Set<PosixFilePermission> perms) { + logEvent("startFileEvent", op, file, true, length, perms, null); + + } + + @Override + public void endFolderEvent(FileOperation op, Path file, Set<PosixFilePermission> perms, Throwable thrown) { + logEvent("endFolderEvent", op, file, false, -1L, perms, thrown); + } + + @Override + public void endFileEvent(FileOperation op, Path file, long length, Set<PosixFilePermission> perms, Throwable thrown) { + logEvent("endFileEvent", op, file, true, length, perms, thrown); + } + + private void logEvent(String type, FileOperation op, Path path, boolean isFile, long length, Collection<PosixFilePermission> perms, Throwable t) { + StringBuilder sb = new StringBuilder(Byte.MAX_VALUE); + sb.append('\t').append(type) + .append('[').append(op).append(']') + .append(' ').append(isFile ? "File" : "Directory").append('=').append(path) + .append(' ').append("length=").append(length) + .append(' ').append("perms=").append(perms) + ; + if (t != null) { + sb.append(' ').append("ERROR=").append(t.getClass().getSimpleName()).append(": ").append(t.getMessage()); + } + logger.info(sb.toString()); + } + }); + } }
