You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2015/02/10 11:11:21 UTC
[1/2] mina-sshd git commit: [SSHD-395] Use an ExecutorService to run
ScpCommand(s)
Repository: mina-sshd
Updated Branches:
refs/heads/master faacb6c35 -> 6af9457d3
[SSHD-395] Use an ExecutorService to run ScpCommand(s)
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/d6ab2b83
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/d6ab2b83
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/d6ab2b83
Branch: refs/heads/master
Commit: d6ab2b83b419a3af3eeeb19e5950d7f9cedb660b
Parents: faacb6c
Author: Guillaume Nodet <gn...@apache.org>
Authored: Tue Feb 10 10:38:13 2015 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Tue Feb 10 10:38:13 2015 +0100
----------------------------------------------------------------------
.../apache/sshd/server/command/ScpCommand.java | 70 +++++++++++++--
.../sshd/server/command/ScpCommandFactory.java | 90 ++++++++++++++++++--
2 files changed, 144 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d6ab2b83/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 be5fc91..4355be4 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.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
-import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import org.apache.sshd.common.file.FileSystemAware;
import org.apache.sshd.common.file.FileSystemView;
import org.apache.sshd.common.scp.ScpHelper;
+import org.apache.sshd.common.util.ThreadUtils;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
@@ -59,9 +60,39 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
protected OutputStream err;
protected ExitCallback callback;
protected IOException error;
+ protected ExecutorService executors;
+ protected boolean shutdownExecutor;
+ protected Future<?> pendingFuture;
public ScpCommand(String command) {
- this.name = command;
+ this(command, null);
+ }
+
+ public ScpCommand(String command, ExecutorService executorService) {
+ this(command, executorService, false);
+ }
+
+ /**
+ * @param command The command to be executed
+ * @param executorService An {@link ExecutorService} to be used when
+ * {@link #start(Environment)}-ing execution. If {@code null} an ad-hoc
+ * single-threaded service is created and used.
+ * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
+ * will be called when command terminates - unless it is the ad-hoc
+ * service, which will be shutdown regardless
+ * @see ThreadUtils#newSingleThreadExecutor(String)
+ */
+ public ScpCommand(String command, ExecutorService executorService, boolean shutdownOnExit) {
+ name = command;
+
+ if ((executors = executorService) == null) {
+ String poolName = command.replace(' ', '_').replace('/', ':');
+ executors = ThreadUtils.newSingleThreadExecutor(poolName);
+ shutdownExecutor = true; // we always close the ad-hoc executor service
+ } else {
+ shutdownExecutor = shutdownOnExit;
+ }
+
log.debug("Executing command {}", command);
String[] args = command.split(" ");
for (int i = 1; i < args.length; i++) {
@@ -97,7 +128,7 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
}
}
if (!optF && !optT) {
- error = new IOException("Either -f or -t option should be set");
+ error = new IOException("Either -f or -t option should be set for " + command);
}
}
@@ -125,10 +156,35 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
if (error != null) {
throw error;
}
- new Thread(this, "ScpCommand: " + name).start();
+
+ try {
+ pendingFuture = executors.submit(this);
+ } catch (RuntimeException e) { // e.g., RejectedExecutionException
+ log.error("Failed (" + e.getClass().getSimpleName() + ") to start command=" + name + ": " + e.getMessage(), e);
+ throw new IOException(e);
+ }
}
public void destroy() {
+ // if thread has not completed, cancel it
+ if ((pendingFuture != null) && (!pendingFuture.isDone())) {
+ boolean result = pendingFuture.cancel(true);
+ // TODO consider waiting some reasonable (?) amount of time for cancellation
+ if (log.isDebugEnabled()) {
+ log.debug("destroy() - cancel pending future=" + result);
+ }
+ }
+
+ pendingFuture = null;
+
+ if ((executors != null) && shutdownExecutor) {
+ Collection<Runnable> runners = executors.shutdownNow();
+ if (log.isDebugEnabled()) {
+ log.debug("destroy() - shutdown executor service - runners count=" + ((runners == null) ? 0 : runners.size()));
+ }
+ }
+
+ executors = null;
}
public void run() {
@@ -154,7 +210,7 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
} catch (IOException e2) {
// Ignore
}
- log.info("Error in scp command", e);
+ log.info("Error in scp command=" + name, e);
} finally {
if (callback != null) {
callback.onExit(exitValue, exitMessage);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d6ab2b83/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 51eee6c..07ec477 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,8 +18,7 @@
*/
package org.apache.sshd.server.command;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.concurrent.ExecutorService;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.CommandFactory;
@@ -34,14 +33,85 @@ import org.apache.sshd.server.CommandFactory;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class ScpCommandFactory implements CommandFactory {
+ /**
+ * Command prefix used to identify SCP commands
+ */
+ public static final String SCP_COMMAND_PREFIX = "scp";
private CommandFactory delegate;
+ private ExecutorService executors;
+ private boolean shutdownExecutor;
public ScpCommandFactory() {
+ this(null, null);
}
- public ScpCommandFactory(CommandFactory delegate) {
- this.delegate = delegate;
+ /**
+ * @param executorService An {@link ExecutorService} to be used when
+ * starting {@link ScpCommand} execution. If {@code null} an ad-hoc
+ * single-threaded service is created and used.
+ */
+ public ScpCommandFactory(ExecutorService executorService) {
+ this(null, executorService);
+ }
+
+ /**
+ * @param delegateFactory A {@link CommandFactory} to be used if the
+ * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
+ * will be thrown when attempting to invoke {@link #createCommand(String)}
+ * with a non-SCP command
+ * @see #SCP_COMMAND_PREFIX
+ */
+ public ScpCommandFactory(CommandFactory delegateFactory) {
+ this(delegateFactory, null);
+ }
+
+ /**
+ * @param delegateFactory A {@link CommandFactory} to be used if the
+ * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
+ * will be thrown when attempting to invoke {@link #createCommand(String)}
+ * with a non-SCP command
+ * @param executorService An {@link ExecutorService} to be used when
+ * starting {@link ScpCommand} execution. If {@code null} then a single-threaded
+ * ad-hoc service is used. <B>Note:</B> the service will <U>not</U> be shutdown
+ * when the command is terminated - unless it is the ad-hoc service, which will be
+ * shutdown regardless
+ * @see #ScpCommandFactory(CommandFactory, ExecutorService, boolean)
+ */
+ public ScpCommandFactory(CommandFactory delegateFactory, ExecutorService executorService) {
+ this(delegateFactory, executorService, false);
+ }
+
+ /**
+ * @param delegateFactory A {@link CommandFactory} to be used if the
+ * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
+ * will be thrown when attempting to invoke {@link #createCommand(String)}
+ * with a non-SCP command
+ * @param executorService An {@link ExecutorService} to be used when
+ * starting {@link ScpCommand} execution. If {@code null} then a single-threaded
+ * ad-hoc service is used. <B>Note:</B> the service will <U>not</U> be shutdown
+ * when the command is terminated - unless it is the ad-hoc service, which will be
+ * shutdown regardless
+ * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
+ * will be called when command terminates - unless it is the ad-hoc
+ * service, which will be shutdown regardless
+ */
+ public ScpCommandFactory(CommandFactory delegateFactory, ExecutorService executorService, boolean shutdownOnExit) {
+ delegate = delegateFactory;
+ executors = executorService;
+ shutdownExecutor = shutdownOnExit;
+ }
+
+ public CommandFactory getDelegateCommandFactory() {
+ return delegate;
+ }
+
+ public ExecutorService getExecutorService() {
+ return executors;
+ }
+
+ public boolean isShutdownOnExit() {
+ return shutdownExecutor;
}
/**
@@ -51,14 +121,17 @@ public class ScpCommandFactory implements CommandFactory {
*
* @param command command to parse
* @return configured {@link Command} instance
- * @throws IllegalArgumentException
+ * @throws IllegalArgumentException if not an SCP command and no
+ * delegate command factory is available
+ * @see #SCP_COMMAND_PREFIX
*/
public Command createCommand(String command) {
try {
- if (!command.startsWith("scp")) {
- throw new IllegalArgumentException("Unknown command, does not begin with 'scp'");
+ if (!command.startsWith(SCP_COMMAND_PREFIX)) {
+ throw new IllegalArgumentException("Unknown command, does not begin with '" + SCP_COMMAND_PREFIX + "': " + command);
}
- return new ScpCommand(command);
+
+ return new ScpCommand(command, getExecutorService(), isShutdownOnExit());
} catch (IllegalArgumentException iae) {
if (delegate != null) {
return delegate.createCommand(command);
@@ -66,5 +139,4 @@ public class ScpCommandFactory implements CommandFactory {
throw iae;
}
}
-
}
[2/2] mina-sshd git commit: [SSHD-401] Allow user control over
ScpCommand send/receive buffer size
Posted by gn...@apache.org.
[SSHD-401] Allow user control over ScpCommand send/receive buffer size
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/6af9457d
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/6af9457d
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/6af9457d
Branch: refs/heads/master
Commit: 6af9457d3032080d49e6dbf41d2dad0d376c9b7f
Parents: d6ab2b8
Author: Guillaume Nodet <gn...@apache.org>
Authored: Tue Feb 10 11:11:14 2015 +0100
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Tue Feb 10 11:11:14 2015 +0100
----------------------------------------------------------------------
.../sshd/client/scp/DefaultScpClient.java | 6 +-
.../org/apache/sshd/common/scp/ScpHelper.java | 81 ++++++++---
.../apache/sshd/server/command/ScpCommand.java | 116 +++++++++++++--
.../sshd/server/command/ScpCommandFactory.java | 145 ++++++++++++++-----
4 files changed, 283 insertions(+), 65 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6af9457d/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 911eb5c..7527102 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
@@ -104,7 +104,8 @@ public class DefaultScpClient implements ScpClient {
helper.receive(target,
options.contains(Option.Recursive),
options.contains(Option.TargetIsDirectory),
- options.contains(Option.PreserveAttributes));
+ options.contains(Option.PreserveAttributes),
+ ScpHelper.DEFAULT_RECEIVE_BUFFER_SIZE);
channel.close(false);
}
@@ -155,7 +156,8 @@ public class DefaultScpClient implements ScpClient {
helper.send(Arrays.asList(local),
options.contains(Option.Recursive),
- options.contains(Option.PreserveAttributes));
+ options.contains(Option.PreserveAttributes),
+ ScpHelper.DEFAULT_SEND_BUFFER_SIZE);
channel.close(false);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6af9457d/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 af4daa5..517cb36 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
@@ -46,6 +46,20 @@ public class ScpHelper {
public static final int WARNING = 1;
public static final int ERROR = 2;
+ /**
+ * Default size (in bytes) of send / receive buffer size
+ */
+ public static final int DEFAULT_COPY_BUFFER_SIZE = 8192;
+ public static final int DEFAULT_RECEIVE_BUFFER_SIZE = DEFAULT_COPY_BUFFER_SIZE;
+ public static final int DEFAULT_SEND_BUFFER_SIZE = DEFAULT_COPY_BUFFER_SIZE;
+
+ /**
+ * The minimum size for sending / receiving files
+ */
+ public static final int MIN_COPY_BUFFER_SIZE = Byte.MAX_VALUE;
+ public static final int MIN_RECEIVE_BUFFER_SIZE = MIN_COPY_BUFFER_SIZE;
+ public static final int MIN_SEND_BUFFER_SIZE = MIN_COPY_BUFFER_SIZE;
+
public static final int S_IRUSR = 0000400;
public static final int S_IWUSR = 0000200;
public static final int S_IXUSR = 0000100;
@@ -66,7 +80,7 @@ public class ScpHelper {
this.root = root;
}
- public void receive(SshFile path, boolean recursive, boolean shouldBeDir, boolean preserve) throws IOException {
+ public void receive(SshFile path, boolean recursive, boolean shouldBeDir, boolean preserve, int bufferSize) throws IOException {
if (shouldBeDir) {
if (!path.doesExist()) {
throw new SshException("Target directory " + path.toString() + " does not exists");
@@ -110,19 +124,19 @@ public class ScpHelper {
if (recursive && isDir)
{
- receiveDir(line, path, time, preserve);
+ receiveDir(line, path, time, preserve, bufferSize);
time = null;
}
else
{
- receiveFile(line, path, time, preserve);
+ receiveFile(line, path, time, preserve, bufferSize);
time = null;
}
}
}
- public void receiveDir(String header, SshFile path, long[] time, boolean preserve) throws IOException {
+ public void receiveDir(String header, SshFile path, long[] time, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Receiving directory {}", path);
}
@@ -166,10 +180,10 @@ public class ScpHelper {
header = readLine();
log.debug("Received header: " + header);
if (header.startsWith("C")) {
- receiveFile(header, file, time, preserve);
+ receiveFile(header, file, time, preserve, bufferSize);
time = null;
} else if (header.startsWith("D")) {
- receiveDir(header, file, time, preserve);
+ receiveDir(header, file, time, preserve, bufferSize);
time = null;
} else if (header.equals("E")) {
ack();
@@ -184,7 +198,7 @@ public class ScpHelper {
}
- public void receiveFile(String header, SshFile path, long[] time, boolean preserve) throws IOException {
+ public void receiveFile(String header, SshFile path, long[] time, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Receiving file {}", path);
}
@@ -192,9 +206,24 @@ public class ScpHelper {
throw new IOException("Expected a C message but got '" + header + "'");
}
+ if (bufferSize < MIN_RECEIVE_BUFFER_SIZE) {
+ throw new IOException("receiveFile(" + path + ") buffer size (" + bufferSize + ") below minimum (" + MIN_RECEIVE_BUFFER_SIZE + ")");
+ }
+
String perms = header.substring(1, 5);
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...
+ log.warn("receiveFile(" + path + ") bad length in header: " + header);
+ }
+
+ // if file size is less than buffer size allocate only expected file size
+ int bufSize = (int) Math.min(length, bufferSize);
+ if (bufSize < 0) { // TODO consider throwing an exception
+ log.warn("receiveFile(" + path + ") bad buffer size (" + bufSize + ") using default (" + MIN_RECEIVE_BUFFER_SIZE + ")");
+ bufSize = MIN_RECEIVE_BUFFER_SIZE;
+ }
+
SshFile file;
if (path.doesExist() && path.isDirectory()) {
@@ -218,7 +247,7 @@ public class ScpHelper {
try {
ack();
- byte[] buffer = new byte[8192];
+ byte[] buffer = new byte[bufSize];
while (length > 0) {
int len = (int) Math.min(length, buffer.length);
len = in.read(buffer, 0, len);
@@ -267,7 +296,7 @@ public class ScpHelper {
}
}
- public void send(List<String> paths, boolean recursive, boolean preserve) throws IOException {
+ public void send(List<String> paths, boolean recursive, boolean preserve, int bufferSize) throws IOException {
readAck(false);
for (String pattern : paths) {
int idx = pattern.indexOf('*');
@@ -282,13 +311,13 @@ public class ScpHelper {
for (String path : included) {
SshFile file = root.getFile(basedir + "/" + path);
if (file.isFile()) {
- sendFile(file, preserve);
+ sendFile(file, preserve, bufferSize);
} else if (file.isDirectory()) {
if (!recursive) {
out.write(ScpHelper.WARNING);
out.write((path + " not a regular file\n").getBytes());
} else {
- sendDir(file, preserve);
+ sendDir(file, preserve, bufferSize);
}
} else {
out.write(ScpHelper.WARNING);
@@ -307,12 +336,12 @@ public class ScpHelper {
throw new IOException(file + ": no such file or directory");
}
if (file.isFile()) {
- sendFile(file, preserve);
+ sendFile(file, preserve, bufferSize);
} else if (file.isDirectory()) {
if (!recursive) {
throw new IOException(file + " not a regular file");
} else {
- sendDir(file, preserve);
+ sendDir(file, preserve, bufferSize);
}
} else {
throw new IOException(file + ": unknown file type");
@@ -321,11 +350,15 @@ public class ScpHelper {
}
}
- public void sendFile(SshFile path, boolean preserve) throws IOException {
+ public void sendFile(SshFile path, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Sending file {}", path);
}
+ if (bufferSize < MIN_SEND_BUFFER_SIZE) {
+ throw new IOException("sendFile(" + path + ") buffer size (" + bufferSize + ") below minimum (" + MIN_SEND_BUFFER_SIZE + ")");
+ }
+
Map<SshFile.Attribute,Object> attrs = path.getAttributes(true);
if (preserve) {
StringBuffer buf = new StringBuffer();
@@ -355,9 +388,21 @@ public class ScpHelper {
out.flush();
readAck(false);
+ long fileSize = path.getSize();
+ if (fileSize < 0L) { // TODO consider throwing an exception...
+ log.warn("sendFile(" + path + ") bad file size: " + fileSize);
+ }
+
+ // if file size is less than buffer size allocate only expected file size
+ int bufSize = (int) Math.min(fileSize, bufferSize);
+ if (bufSize < 0) { // TODO consider throwing an exception
+ log.warn("sendFile(" + path + ") bad buffer size (" + bufSize + ") using default (" + MIN_SEND_BUFFER_SIZE + ")");
+ bufSize = MIN_SEND_BUFFER_SIZE;
+ }
+
InputStream is = path.createInputStream(0);
try {
- byte[] buffer = new byte[8192];
+ byte[] buffer = new byte[bufSize];
for (;;) {
int len = is.read(buffer, 0, buffer.length);
if (len == -1) {
@@ -372,7 +417,7 @@ public class ScpHelper {
readAck(false);
}
- public void sendDir(SshFile path, boolean preserve) throws IOException {
+ public void sendDir(SshFile path, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Sending directory {}", path);
}
@@ -407,9 +452,9 @@ public class ScpHelper {
for (SshFile child : path.listSshFiles()) {
if (child.isFile()) {
- sendFile(child, preserve);
+ sendFile(child, preserve, bufferSize);
} else if (child.isDirectory()) {
- sendDir(child, preserve);
+ sendDir(child, preserve, bufferSize);
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6af9457d/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 4355be4..68dc9ef 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
@@ -63,26 +63,107 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
protected ExecutorService executors;
protected boolean shutdownExecutor;
protected Future<?> pendingFuture;
+ protected int sendBufferSize;
+ protected int receiveBufferSize;
+ /**
+ * Simple constructor - uses an ad-hoc {@link ExecutorService} to
+ * run the command as well as default send / receive buffer sizes
+ *
+ * @param command The command to be executed
+ * @see #ScpCommand(String, ExecutorService, boolean, int, int)
+ * @see ScpHelper#DEFAULT_COPY_BUFFER_SIZE
+ */
public ScpCommand(String command) {
- this(command, null);
+ this(command, ScpHelper.DEFAULT_COPY_BUFFER_SIZE);
}
+ /**
+ * Uses an ad-hoc {@link ExecutorService} to run the command
+ *
+ * @param command The command to be executed
+ * @param bufferSize Size (in bytes) of buffer to be used for <U>both</U>
+ * sending and receiving files
+ * @see #ScpCommand(String, ExecutorService, int)
+ */
+ public ScpCommand(String command, int bufferSize) {
+ this(command, null, bufferSize);
+ }
+
+ /**
+ * @param command The command to be executed
+ * @param executorService An {@link ExecutorService} to be used when
+ * {@link #start(Environment)}-ing execution. If {@code null} an ad-hoc
+ * single-threaded service is created and used. <B>Note:</B> the
+ * executor service will <U>not</U> be shutdown when command terminates
+ * unless it is the ad-hoc service
+ * @param bufferSize Size (in bytes) of buffer to be used for <U>both</U>
+ * sending and receiving files
+ * @see #ScpCommand(String, ExecutorService, boolean, int, int)
+ */
+ public ScpCommand(String command, ExecutorService executorService, int bufferSize) {
+ this(command, executorService, false, bufferSize);
+ }
+
+ /**
+ * @param command The command to be executed
+ * @param executorService An {@link ExecutorService} to be used when
+ * {@link #start(Environment)}-ing execution. If {@code null} an ad-hoc
+ * single-threaded service is created and used. <B>Note:</B> the
+ * executor service will <U>not</U> be shutdown when command terminates
+ * unless it is the ad-hoc service
+ * @see #ScpCommand(String, ExecutorService, boolean, int)
+ */
public ScpCommand(String command, ExecutorService executorService) {
this(command, executorService, false);
}
/**
- * @param command The command to be executed
+ * @param command The command to be executed
* @param executorService An {@link ExecutorService} to be used when
- * {@link #start(Environment)}-ing execution. If {@code null} an ad-hoc
- * single-threaded service is created and used.
- * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
- * will be called when command terminates - unless it is the ad-hoc
- * service, which will be shutdown regardless
- * @see ThreadUtils#newSingleThreadExecutor(String)
+ * {@link #start(Environment)}-ing execution. If {@code null} an ad-hoc
+ * single-threaded service is created and used.
+ * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
+ * will be called when command terminates - unless it is the ad-hoc
+ * service, which will be shutdown regardless
+ * @see #ScpCommand(String, ExecutorService, boolean, int)
+ * @see ScpHelper#DEFAULT_COPY_BUFFER_SIZE
*/
public ScpCommand(String command, ExecutorService executorService, boolean shutdownOnExit) {
+ this(command, executorService, false, ScpHelper.DEFAULT_COPY_BUFFER_SIZE);
+ }
+
+ /**
+ * @param command The command to be executed
+ * @param executorService An {@link ExecutorService} to be used when
+ * {@link #start(Environment)}-ing execution. If {@code null} an ad-hoc
+ * single-threaded service is created and used.
+ * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
+ * will be called when command terminates - unless it is the ad-hoc
+ * service, which will be shutdown regardless
+ * @param bufferSize Size (in bytes) of buffer to be used for <U>both</U>
+ * sending and receiving files
+ * @see #ScpCommand(String, ExecutorService, boolean, int, int)
+ */
+ public ScpCommand(String command, ExecutorService executorService, boolean shutdownOnExit, int bufferSize) {
+ this(command, executorService, shutdownOnExit, bufferSize, bufferSize);
+ }
+
+ /**
+ * @param command The command to be executed
+ * @param executorService An {@link ExecutorService} to be used when
+ * {@link #start(Environment)}-ing execution. If {@code null} an ad-hoc
+ * single-threaded service is created and used.
+ * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
+ * will be called when command terminates - unless it is the ad-hoc
+ * 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
+ * @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) {
name = command;
if ((executors = executorService) == null) {
@@ -93,6 +174,14 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
shutdownExecutor = shutdownOnExit;
}
+ if ((sendBufferSize = sendSize) < ScpHelper.MIN_SEND_BUFFER_SIZE) {
+ throw new IllegalArgumentException("<ScpCommmand>(" + command + ") send buffer size (" + sendSize + ") below minimum required (" + ScpHelper.MIN_SEND_BUFFER_SIZE + ")");
+ }
+
+ if ((receiveBufferSize = receiveSize) < ScpHelper.MIN_RECEIVE_BUFFER_SIZE) {
+ throw new IllegalArgumentException("<ScpCommmand>(" + command + ") receive buffer size (" + sendSize + ") below minimum required (" + ScpHelper.MIN_RECEIVE_BUFFER_SIZE + ")");
+ }
+
log.debug("Executing command {}", command);
String[] args = command.split(" ");
for (int i = 1; i < args.length; i++) {
@@ -120,7 +209,7 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
}
}
} else {
- path = command.substring(command.indexOf(args[i-1]) + args[i-1].length() + 1);
+ path = command.substring(command.indexOf(args[i - 1]) + args[i - 1].length() + 1);
if (path.startsWith("\"") && path.endsWith("\"") || path.startsWith("'") && path.endsWith("'")) {
path = path.substring(1, path.length() - 1);
}
@@ -193,9 +282,9 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
ScpHelper helper = new ScpHelper(in, out, root);
try {
if (optT) {
- helper.receive(root.getFile(path), optR, optD, optP);
+ helper.receive(root.getFile(path), optR, optD, optP, receiveBufferSize);
} else if (optF) {
- helper.send(Collections.singletonList(path), optR, optP);
+ helper.send(Collections.singletonList(path), optR, optP, sendBufferSize);
} else {
throw new IOException("Unsupported mode");
}
@@ -218,5 +307,8 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
}
}
-
+ @Override
+ public String toString() {
+ return name;
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/6af9457d/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 07ec477..87e980f 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
@@ -20,6 +20,7 @@ package org.apache.sshd.server.command;
import java.util.concurrent.ExecutorService;
+import org.apache.sshd.common.scp.ScpHelper;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.CommandFactory;
@@ -41,15 +42,37 @@ public class ScpCommandFactory implements CommandFactory {
private CommandFactory delegate;
private ExecutorService executors;
private boolean shutdownExecutor;
+ private int sendBufferSize;
+ private int receiveBufferSize;
+ /**
+ * Default constructor - uses an ad-hoc {@link ExecutorService} with
+ * no delegate {@link CommandFactory} and default send/receive buffer
+ * sizes
+ *
+ * @see ScpHelper#DEFAULT_COPY_BUFFER_SIZE
+ */
public ScpCommandFactory() {
this(null, null);
}
/**
+ * Uses an ad-hoc {@link ExecutorService} with no delegate {@link CommandFactory}
+ *
+ * @param bufferSize Size (in bytes) of buffer to be used for <U>both</U>
+ * sending and receiving files
+ * @see #ScpCommandFactory(CommandFactory, ExecutorService, boolean, int, int)
+ */
+ public ScpCommandFactory(int bufferSize) {
+ this(null, null, true, bufferSize);
+ }
+
+ /**
* @param executorService An {@link ExecutorService} to be used when
- * starting {@link ScpCommand} execution. If {@code null} an ad-hoc
- * single-threaded service is created and used.
+ * starting {@link ScpCommand} execution. If {@code null} an ad-hoc
+ * single-threaded service is created and used. <B>Note:</B> the
+ * executor service will <U>not</U> be shutdown when command terminates
+ * unless it is the ad-hoc service
*/
public ScpCommandFactory(ExecutorService executorService) {
this(null, executorService);
@@ -57,9 +80,9 @@ public class ScpCommandFactory implements CommandFactory {
/**
* @param delegateFactory A {@link CommandFactory} to be used if the
- * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
- * will be thrown when attempting to invoke {@link #createCommand(String)}
- * with a non-SCP command
+ * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
+ * will be thrown when attempting to invoke {@link #createCommand(String)}
+ * with a non-SCP command
* @see #SCP_COMMAND_PREFIX
*/
public ScpCommandFactory(CommandFactory delegateFactory) {
@@ -68,14 +91,14 @@ public class ScpCommandFactory implements CommandFactory {
/**
* @param delegateFactory A {@link CommandFactory} to be used if the
- * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
- * will be thrown when attempting to invoke {@link #createCommand(String)}
- * with a non-SCP command
+ * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
+ * will be thrown when attempting to invoke {@link #createCommand(String)}
+ * with a non-SCP command
* @param executorService An {@link ExecutorService} to be used when
- * starting {@link ScpCommand} execution. If {@code null} then a single-threaded
- * ad-hoc service is used. <B>Note:</B> the service will <U>not</U> be shutdown
- * when the command is terminated - unless it is the ad-hoc service, which will be
- * shutdown regardless
+ * starting {@link ScpCommand} execution. If {@code null} then a single-threaded
+ * ad-hoc service is used. <B>Note:</B> the service will <U>not</U> be shutdown
+ * when the command is terminated - unless it is the ad-hoc service, which will be
+ * shutdown regardless
* @see #ScpCommandFactory(CommandFactory, ExecutorService, boolean)
*/
public ScpCommandFactory(CommandFactory delegateFactory, ExecutorService executorService) {
@@ -84,22 +107,72 @@ public class ScpCommandFactory implements CommandFactory {
/**
* @param delegateFactory A {@link CommandFactory} to be used if the
- * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
- * will be thrown when attempting to invoke {@link #createCommand(String)}
- * with a non-SCP command
+ * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
+ * will be thrown when attempting to invoke {@link #createCommand(String)}
+ * with a non-SCP command
* @param executorService An {@link ExecutorService} to be used when
- * starting {@link ScpCommand} execution. If {@code null} then a single-threaded
- * ad-hoc service is used. <B>Note:</B> the service will <U>not</U> be shutdown
- * when the command is terminated - unless it is the ad-hoc service, which will be
- * shutdown regardless
- * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
- * will be called when command terminates - unless it is the ad-hoc
- * service, which will be shutdown regardless
+ * starting {@link ScpCommand} execution. If {@code null} then a single-threaded
+ * ad-hoc service is used. <B>Note:</B> the service will <U>not</U> be shutdown
+ * when the command is terminated - unless it is the ad-hoc service, which will be
+ * shutdown regardless
+ * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
+ * will be called when command terminates - unless it is the ad-hoc
+ * service, which will be shutdown regardless
+ * @see #ScpCommandFactory(CommandFactory, ExecutorService, boolean, int)
*/
public ScpCommandFactory(CommandFactory delegateFactory, ExecutorService executorService, boolean shutdownOnExit) {
+ this(delegateFactory, executorService, shutdownOnExit, ScpHelper.DEFAULT_COPY_BUFFER_SIZE);
+ }
+
+ /**
+ * @param delegateFactory A {@link CommandFactory} to be used if the
+ * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
+ * will be thrown when attempting to invoke {@link #createCommand(String)}
+ * with a non-SCP command
+ * @param executorService An {@link ExecutorService} to be used when
+ * starting {@link ScpCommand} execution. If {@code null} then a single-threaded
+ * ad-hoc service is used. <B>Note:</B> the service will <U>not</U> be shutdown
+ * when the command is terminated - unless it is the ad-hoc service, which will be
+ * shutdown regardless
+ * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
+ * will be called when command terminates - unless it is the ad-hoc
+ * service, which will be shutdown regardless
+ * @param bufferSize Size (in bytes) of buffer to be used for <U>both</U>
+ * sending and receiving files
+ * @see #ScpCommandFactory(CommandFactory, ExecutorService, boolean, int, int)
+ */
+ public ScpCommandFactory(CommandFactory delegateFactory, ExecutorService executorService, boolean shutdownOnExit, int bufferSize) {
+ this(delegateFactory, executorService, shutdownOnExit, bufferSize, bufferSize);
+ }
+
+ /**
+ * @param delegateFactory A {@link CommandFactory} to be used if the
+ * command is not an SCP one. If {@code null} then an {@link IllegalArgumentException}
+ * will be thrown when attempting to invoke {@link #createCommand(String)}
+ * with a non-SCP command
+ * @param executorService An {@link ExecutorService} to be used when
+ * starting {@link ScpCommand} execution. If {@code null} then a single-threaded
+ * ad-hoc service is used. <B>Note:</B> the service will <U>not</U> be shutdown
+ * when the command is terminated - unless it is the ad-hoc service, which will be
+ * shutdown regardless
+ * @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
+ * will be called when command terminates - unless it is the ad-hoc
+ * 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
+ * @see ScpHelper#MIN_SEND_BUFFER_SIZE
+ * @see ScpHelper#MIN_RECEIVE_BUFFER_SIZE
+ */
+ public ScpCommandFactory(CommandFactory delegateFactory, ExecutorService executorService, boolean shutdownOnExit, int sendSize, int receiveSize) {
delegate = delegateFactory;
executors = executorService;
shutdownExecutor = shutdownOnExit;
+ if ((sendBufferSize = sendSize) < ScpHelper.MIN_SEND_BUFFER_SIZE) {
+ throw new IllegalArgumentException("<ScpCommandFactory>() send buffer size (" + sendSize + ") below minimum required (" + ScpHelper.MIN_SEND_BUFFER_SIZE + ")");
+ }
+ if ((receiveBufferSize = receiveSize) < ScpHelper.MIN_RECEIVE_BUFFER_SIZE) {
+ throw new IllegalArgumentException("<ScpCommandFactory>() receive buffer size (" + sendSize + ") below minimum required (" + ScpHelper.MIN_RECEIVE_BUFFER_SIZE + ")");
+ }
}
public CommandFactory getDelegateCommandFactory() {
@@ -114,6 +187,14 @@ public class ScpCommandFactory implements CommandFactory {
return shutdownExecutor;
}
+ public int getSendBufferSize() {
+ return sendBufferSize;
+ }
+
+ public int getReceiveBufferSize() {
+ return receiveBufferSize;
+ }
+
/**
* Parses a command string and verifies that the basic syntax is
* correct. If parsing fails the responsibility is delegated to
@@ -126,17 +207,15 @@ public class ScpCommandFactory implements CommandFactory {
* @see #SCP_COMMAND_PREFIX
*/
public Command createCommand(String command) {
- try {
- if (!command.startsWith(SCP_COMMAND_PREFIX)) {
- throw new IllegalArgumentException("Unknown command, does not begin with '" + SCP_COMMAND_PREFIX + "': " + command);
- }
-
- return new ScpCommand(command, getExecutorService(), isShutdownOnExit());
- } catch (IllegalArgumentException iae) {
- if (delegate != null) {
- return delegate.createCommand(command);
- }
- throw iae;
+ if (command.startsWith(SCP_COMMAND_PREFIX)) {
+ return new ScpCommand(command, getExecutorService(), isShutdownOnExit(), getSendBufferSize(), getReceiveBufferSize());
}
+
+ CommandFactory factory = getDelegateCommandFactory();
+ if (factory != null) {
+ return factory.createCommand(command);
+ }
+
+ throw new IllegalArgumentException("Unknown command, does not begin with '" + SCP_COMMAND_PREFIX + "': " + command);
}
}