You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2018/02/18 18:20:11 UTC
[2/2] mina-sshd git commit: [SSHD-798] Provide capability to use a
user-defined ExecutorService when servicing GIT commands
[SSHD-798] Provide capability to use a user-defined ExecutorService when servicing GIT commands
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/e0d63503
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/e0d63503
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/e0d63503
Branch: refs/heads/master
Commit: e0d6350360f6e36c309020ae13d75683c9a500b2
Parents: 59a7fcf
Author: Goldstein Lyor <ly...@c-b4.com>
Authored: Sun Feb 18 15:09:27 2018 +0200
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Sun Feb 18 20:22:50 2018 +0200
----------------------------------------------------------------------
README.md | 13 ++
.../sshd/server/AbstractCommandSupport.java | 168 ++++++++++++++++++
.../org/apache/sshd/server/scp/ScpCommand.java | 36 ++--
.../server/subsystem/sftp/SftpSubsystem.java | 34 ++--
.../java/org/apache/sshd/agent/AgentTest.java | 6 +-
.../sshd/client/channel/ChannelExecTest.java | 2 +-
.../sshd/client/session/ClientSessionTest.java | 6 +-
.../sshd/util/test/CommandExecutionHelper.java | 90 +---------
.../org/apache/sshd/util/test/EchoShell.java | 2 +-
.../org/apache/sshd/git/AbstractGitCommand.java | 133 ++++++++++++++
.../sshd/git/AbstractGitCommandFactory.java | 96 ++++++++++
.../apache/sshd/git/pack/GitPackCommand.java | 175 ++++---------------
.../sshd/git/pack/GitPackCommandFactory.java | 34 ++--
.../sshd/git/pgm/EmbeddedCommandRunner.java | 6 +-
.../org/apache/sshd/git/pgm/GitPgmCommand.java | 145 ++-------------
.../sshd/git/pgm/GitPgmCommandFactory.java | 35 ++--
16 files changed, 558 insertions(+), 423 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 82e92d3..283cc85 100644
--- a/README.md
+++ b/README.md
@@ -1380,6 +1380,19 @@ See `GitPackCommandFactory` and `GitPgmCommandFactory`. These command factories
// or any other combination ...
```
+as with all other built-in commands, the factories allow the user to provide an `ExecutorService` in order to control the spwaned threads
+for servicing the commands. If none provided, an internal single-threaded "pool" is created ad-hoc and destroyed once the command execution
+is completed (regardless of whether successful or not):
+
+
+```java
+
+ sshd.setCommandFactory(new GitPackCommandFactory(rootDir, new MyCommandFactory())
+ .withExecutorService(myService)
+ .withShutdownOnExit(false));
+
+```
+
## LDAP adaptors
The _sshd-ldap_ artifact contains an [LdapPasswordAuthenticator](https://issues.apache.org/jira/browse/SSHD-607) and an [LdapPublicKeyAuthenticator](https://issues.apache.org/jira/browse/SSHD-608) that have been written along the same lines as the [openssh-ldap-publickey](https://github.com/AndriiGrytsenko/openssh-ldap-publickey) project. The authenticators can be easily configured to match most LDAP schemes, or alternatively serve as base classes for code that extends them and adds proprietary logic.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-core/src/main/java/org/apache/sshd/server/AbstractCommandSupport.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/AbstractCommandSupport.java b/sshd-core/src/main/java/org/apache/sshd/server/AbstractCommandSupport.java
new file mode 100644
index 0000000..7533157
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/AbstractCommandSupport.java
@@ -0,0 +1,168 @@
+/*
+ * 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.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+
+/**
+ * Provides a basic useful skeleton for {@link Command} executions
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractCommandSupport
+ extends AbstractLoggingBean
+ implements Command, Runnable, ExitCallback, ExecutorServiceCarrier {
+ private final String command;
+ private InputStream in;
+ private OutputStream out;
+ private OutputStream err;
+ private ExitCallback callback;
+ private Environment environment;
+ private Future<?> cmdFuture;
+ private ExecutorService executorService;
+ private boolean shutdownOnExit;
+ private boolean cbCalled;
+
+ protected AbstractCommandSupport(String command, ExecutorService executorService, boolean shutdownOnExit) {
+ this.command = command;
+
+ if (executorService == null) {
+ String poolName = GenericUtils.isEmpty(command) ? getClass().getSimpleName() : command.replace(' ', '_').replace('/', ':');
+ this.executorService = ThreadUtils.newSingleThreadExecutor(poolName);
+ this.shutdownOnExit = true; // we always close the ad-hoc executor service
+ } else {
+ this.executorService = executorService;
+ this.shutdownOnExit = shutdownOnExit;
+ }
+ }
+
+ public String getCommand() {
+ return command;
+ }
+
+ @Override
+ public ExecutorService getExecutorService() {
+ return executorService;
+ }
+
+ @Override
+ public boolean isShutdownOnExit() {
+ return shutdownOnExit;
+ }
+
+ public InputStream getInputStream() {
+ return in;
+ }
+
+ @Override
+ public void setInputStream(InputStream in) {
+ this.in = in;
+ }
+
+ public OutputStream getOutputStream() {
+ return out;
+ }
+
+ @Override
+ public void setOutputStream(OutputStream out) {
+ this.out = out;
+ }
+
+ public OutputStream getErrorStream() {
+ return err;
+ }
+
+ @Override
+ public void setErrorStream(OutputStream err) {
+ this.err = err;
+ }
+
+ public ExitCallback getExitCallback() {
+ return callback;
+ }
+
+ @Override
+ public void setExitCallback(ExitCallback callback) {
+ this.callback = callback;
+ }
+
+ public Environment getEnvironment() {
+ return environment;
+ }
+
+ protected Future<?> getStartedCommandFuture() {
+ return cmdFuture;
+ }
+
+ @Override
+ public void start(Environment env) throws IOException {
+ environment = env;
+ ExecutorService executors = getExecutorService();
+ cmdFuture = executors.submit(this);
+ }
+
+ @Override
+ public void destroy() {
+ ExecutorService executors = getExecutorService();
+ if ((executors != null) && (!executors.isShutdown()) && isShutdownOnExit()) {
+ Collection<Runnable> runners = executors.shutdownNow();
+ if (log.isDebugEnabled()) {
+ log.debug("destroy() - shutdown executor service - runners count=" + runners.size());
+ }
+ }
+ this.executorService = null;
+ }
+
+ @Override
+ public void onExit(int exitValue, String exitMessage) {
+ if (cbCalled) {
+ if (log.isTraceEnabled()) {
+ log.trace("onExit({}) ignore exitValue={}, message={} - already called",
+ this, exitValue, exitMessage);
+ }
+ return;
+ }
+
+ ExitCallback cb = getExitCallback();
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("onExit({}) exiting - value={}, message={}", this, exitValue, exitMessage);
+ }
+ cb.onExit(exitValue, exitMessage);
+ } finally {
+ cbCalled = true;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + getCommand() + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommand.java b/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommand.java
index 8ff52c1..23d204b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommand.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/scp/ScpCommand.java
@@ -37,6 +37,7 @@ import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.session.SessionHolder;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
import org.apache.sshd.common.util.threads.ThreadUtils;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
@@ -55,7 +56,7 @@ import org.apache.sshd.server.session.ServerSessionHolder;
public class ScpCommand
extends AbstractLoggingBean
implements Command, Runnable, FileSystemAware, SessionAware,
- SessionHolder<Session>, ServerSessionHolder {
+ SessionHolder<Session>, ServerSessionHolder, ExecutorServiceCarrier {
protected final String name;
protected final int sendBufferSize;
@@ -73,12 +74,13 @@ public class ScpCommand
protected OutputStream err;
protected ExitCallback callback;
protected IOException error;
- protected ExecutorService executors;
- protected boolean shutdownExecutor;
protected Future<?> pendingFuture;
protected ScpTransferEventListener listener;
protected ServerSession serverSession;
+ private ExecutorService executorService;
+ private boolean shutdownOnExit;
+
/**
* @param command The command to be executed
* @param executorService An {@link ExecutorService} to be used when
@@ -103,11 +105,11 @@ public class ScpCommand
if (executorService == null) {
String poolName = command.replace(' ', '_').replace('/', ':');
- executors = ThreadUtils.newSingleThreadExecutor(poolName);
- shutdownExecutor = true; // we always close the ad-hoc executor service
+ this.executorService = ThreadUtils.newSingleThreadExecutor(poolName);
+ this.shutdownOnExit = true; // we always close the ad-hoc executor service
} else {
- executors = executorService;
- shutdownExecutor = shutdownOnExit;
+ this.executorService = executorService;
+ this.shutdownOnExit = shutdownOnExit;
}
if (sendSize < ScpHelper.MIN_SEND_BUFFER_SIZE) {
@@ -174,12 +176,23 @@ public class ScpCommand
break;
}
}
- if (!optF && !optT) {
+
+ if ((!optF) && (!optT)) {
error = new IOException("Either -f or -t option should be set for " + command);
}
}
@Override
+ public ExecutorService getExecutorService() {
+ return executorService;
+ }
+
+ @Override
+ public boolean isShutdownOnExit() {
+ return shutdownOnExit;
+ }
+
+ @Override
public Session getSession() {
return getServerSession();
}
@@ -226,6 +239,7 @@ public class ScpCommand
}
try {
+ ExecutorService executors = getExecutorService();
pendingFuture = executors.submit(this);
} catch (RuntimeException e) { // e.g., RejectedExecutionException
log.error("Failed (" + e.getClass().getSimpleName() + ") to start command=" + name + ": " + e.getMessage(), e);
@@ -246,14 +260,14 @@ public class ScpCommand
pendingFuture = null;
- if ((executors != null) && (!executors.isShutdown()) && shutdownExecutor) {
+ ExecutorService executors = getExecutorService();
+ if ((executors != null) && (!executors.isShutdown()) && isShutdownOnExit()) {
Collection<Runnable> runners = executors.shutdownNow();
if (log.isDebugEnabled()) {
log.debug("destroy() - shutdown executor service - runners count=" + runners.size());
}
}
-
- executors = null;
+ this.executorService = null;
try {
fileSystem.close();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
index 5d87d8e..e2cfa5e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
@@ -59,6 +59,7 @@ import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.BufferUtils;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
import org.apache.sshd.common.util.threads.ThreadUtils;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
@@ -73,7 +74,7 @@ import org.apache.sshd.server.session.ServerSession;
*/
public class SftpSubsystem
extends AbstractSftpSubsystemHelper
- implements Command, Runnable, SessionAware, FileSystemAware {
+ implements Command, Runnable, SessionAware, FileSystemAware, ExecutorServiceCarrier {
/**
* Properties key for the maximum of available open handles per session.
@@ -120,8 +121,6 @@ public class SftpSubsystem
protected Random randomizer;
protected int fileHandleSize = DEFAULT_FILE_HANDLE_SIZE;
protected int maxFileHandleRounds = DEFAULT_FILE_HANDLE_ROUNDS;
- protected ExecutorService executors;
- protected boolean shutdownExecutor;
protected Future<?> pendingFuture;
protected byte[] workBuf = new byte[Math.max(DEFAULT_FILE_HANDLE_SIZE, Integer.BYTES)];
protected FileSystem fileSystem = FileSystems.getDefault();
@@ -133,6 +132,8 @@ public class SftpSubsystem
private ServerSession serverSession;
private final AtomicBoolean closed = new AtomicBoolean(false);
+ private ExecutorService executorService;
+ private boolean shutdownOnExit;
/**
* @param executorService The {@link ExecutorService} to be used by
@@ -153,11 +154,11 @@ public class SftpSubsystem
super(policy, accessor, errorStatusDataHandler);
if (executorService == null) {
- executors = ThreadUtils.newSingleThreadExecutor(getClass().getSimpleName());
- shutdownExecutor = true; // we always close the ad-hoc executor service
+ this.executorService = ThreadUtils.newSingleThreadExecutor(getClass().getSimpleName());
+ this.shutdownOnExit = true; // we always close the ad-hoc executor service
} else {
- executors = executorService;
- shutdownExecutor = shutdownOnExit;
+ this.executorService = executorService;
+ this.shutdownOnExit = shutdownOnExit;
}
}
@@ -172,6 +173,16 @@ public class SftpSubsystem
}
@Override
+ public ExecutorService getExecutorService() {
+ return executorService;
+ }
+
+ @Override
+ public boolean isShutdownOnExit() {
+ return shutdownOnExit;
+ }
+
+ @Override
public void setSession(ServerSession session) {
this.serverSession = Objects.requireNonNull(session, "No session");
@@ -233,7 +244,8 @@ public class SftpSubsystem
public void start(Environment env) throws IOException {
this.env = env;
try {
- pendingFuture = executors.submit(this);
+ ExecutorService executor = getExecutorService();
+ pendingFuture = executor.submit(this);
} catch (RuntimeException e) { // e.g., RejectedExecutionException
log.error("Failed (" + e.getClass().getSimpleName() + ") to start command: " + e.toString(), e);
throw new IOException(e);
@@ -1029,14 +1041,14 @@ public class SftpSubsystem
pendingFuture = null;
- if ((executors != null) && (!executors.isShutdown()) && shutdownExecutor) {
+ ExecutorService executors = getExecutorService();
+ if ((executors != null) && (!executors.isShutdown()) && isShutdownOnExit()) {
Collection<Runnable> runners = executors.shutdownNow();
if (log.isDebugEnabled()) {
log.debug("destroy(" + session + ") - shutdown executor service - runners count=" + runners.size());
}
}
-
- executors = null;
+ this.executorService = null;
try {
fileSystem.close();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
index 113dc98..f252be7 100644
--- a/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
@@ -159,9 +159,9 @@ public class AgentTest extends BaseTestSupport {
session2.auth().verify(15L, TimeUnit.SECONDS);
try (ChannelShell channel2 = session2.createShellChannel()) {
- channel2.setIn(shellFactory.shell.getIn());
- channel2.setOut(shellFactory.shell.getOut());
- channel2.setErr(shellFactory.shell.getErr());
+ channel2.setIn(shellFactory.shell.getInputStream());
+ channel2.setOut(shellFactory.shell.getOutputStream());
+ channel2.setErr(shellFactory.shell.getErrorStream());
channel2.setAgentForwarding(true);
channel2.open().verify(9L, TimeUnit.SECONDS);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java b/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
index 01cfed4..038ce55 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
@@ -54,7 +54,7 @@ public class ChannelExecTest extends BaseTestSupport {
sshd.setCommandFactory(command -> new CommandExecutionHelper(command) {
@Override
protected boolean handleCommandLine(String command) throws Exception {
- OutputStream stdout = getOut();
+ OutputStream stdout = getOutputStream();
stdout.write(command.getBytes(StandardCharsets.US_ASCII));
stdout.flush();
return false;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java b/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
index 2b14a4c..aeb5e3b 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
@@ -91,7 +91,7 @@ public class ClientSessionTest extends BaseTestSupport {
protected boolean handleCommandLine(String command) throws Exception {
assertEquals("Mismatched incoming command", expectedCommand, command);
assertFalse("Duplicated command call", cmdProcessed);
- OutputStream stdout = getOut();
+ OutputStream stdout = getOutputStream();
stdout.write(expectedResponse.getBytes(StandardCharsets.US_ASCII));
stdout.flush();
cmdProcessed = true;
@@ -120,7 +120,7 @@ public class ClientSessionTest extends BaseTestSupport {
protected boolean handleCommandLine(String command) throws Exception {
assertEquals("Mismatched incoming command", expectedCommand, command);
assertFalse("Duplicated command call", cmdProcessed);
- OutputStream stderr = getErr();
+ OutputStream stderr = getErrorStream();
stderr.write(expectedErrorMessage.getBytes(StandardCharsets.US_ASCII));
stderr.flush();
cmdProcessed = true;
@@ -171,7 +171,7 @@ public class ClientSessionTest extends BaseTestSupport {
protected boolean handleCommandLine(String command) throws Exception {
assertEquals("Mismatched incoming command", expectedCommand, command);
assertFalse("Duplicated command call", cmdProcessed);
- OutputStream stdout = getOut();
+ OutputStream stdout = getOutputStream();
stdout.write(command.getBytes(StandardCharsets.US_ASCII));
stdout.flush();
cmdProcessed = true;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-core/src/test/java/org/apache/sshd/util/test/CommandExecutionHelper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/CommandExecutionHelper.java b/sshd-core/src/test/java/org/apache/sshd/util/test/CommandExecutionHelper.java
index ce47a65..c44a2ad 100644
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/CommandExecutionHelper.java
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/CommandExecutionHelper.java
@@ -21,97 +21,31 @@ package org.apache.sshd.util.test;
import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.Environment;
-import org.apache.sshd.server.ExitCallback;
+import org.apache.sshd.server.AbstractCommandSupport;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public abstract class CommandExecutionHelper extends AbstractLoggingBean implements Command, Runnable, ExitCallback {
- private InputStream in;
- private OutputStream out;
- private OutputStream err;
- private ExitCallback callback;
- private Environment environment;
- private Thread thread;
- private boolean cbCalled;
- // null/empty if shell
- private String command;
-
+public abstract class CommandExecutionHelper extends AbstractCommandSupport {
protected CommandExecutionHelper() {
this(null);
}
protected CommandExecutionHelper(String command) {
- this.command = command;
- }
-
- public InputStream getIn() {
- return in;
- }
-
- public OutputStream getOut() {
- return out;
- }
-
- public OutputStream getErr() {
- return err;
- }
-
- public Environment getEnvironment() {
- return environment;
- }
-
- public ExitCallback getExitCallback() {
- return callback;
- }
-
- @Override
- public void setInputStream(InputStream in) {
- this.in = in;
- }
-
- @Override
- public void setOutputStream(OutputStream out) {
- this.out = out;
- }
-
- @Override
- public void setErrorStream(OutputStream err) {
- this.err = err;
- }
-
- @Override
- public void setExitCallback(ExitCallback callback) {
- this.callback = callback;
- }
-
- @Override
- public void start(Environment env) throws IOException {
- environment = env;
- thread = new Thread(this, "CommandExecutionHelper");
- thread.setDaemon(true);
- thread.start();
- }
-
- @Override
- public void destroy() {
- thread.interrupt();
+ super(command, null, true);
}
@Override
public void run() {
+ String command = getCommand();
try {
if (command == null) {
- try (BufferedReader r = new BufferedReader(new InputStreamReader(getIn(), StandardCharsets.UTF_8))) {
+ try (BufferedReader r = new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8))) {
for (;;) {
command = r.readLine();
if (command == null) {
@@ -131,7 +65,7 @@ public abstract class CommandExecutionHelper extends AbstractLoggingBean impleme
} catch (Exception e) {
String message = "Failed (" + e.getClass().getSimpleName() + ") to handle '" + command + "': " + e.getMessage();
try {
- OutputStream stderr = getErr();
+ OutputStream stderr = getErrorStream();
stderr.write(message.getBytes(StandardCharsets.US_ASCII));
} catch (IOException ioe) {
log.warn("Failed ({}) to write error message={}: {}",
@@ -144,18 +78,6 @@ public abstract class CommandExecutionHelper extends AbstractLoggingBean impleme
}
}
- @Override
- public void onExit(int exitValue, String exitMessage) {
- if (!cbCalled) {
- ExitCallback cb = getExitCallback();
- try {
- cb.onExit(exitValue, exitMessage);
- } finally {
- cbCalled = true;
- }
- }
- }
-
/**
* @param command The command line
* @return {@code true} if continue accepting command
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-core/src/test/java/org/apache/sshd/util/test/EchoShell.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/EchoShell.java b/sshd-core/src/test/java/org/apache/sshd/util/test/EchoShell.java
index 76a8d5d..9ad8a14 100644
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/EchoShell.java
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/EchoShell.java
@@ -31,7 +31,7 @@ public class EchoShell extends CommandExecutionHelper {
@Override
protected boolean handleCommandLine(String command) throws Exception {
- OutputStream out = getOut();
+ OutputStream out = getOutputStream();
out.write((command + "\n").getBytes(StandardCharsets.UTF_8));
out.flush();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommand.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommand.java b/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommand.java
new file mode 100644
index 0000000..2ed85b0
--- /dev/null
+++ b/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommand.java
@@ -0,0 +1,133 @@
+/*
+ * 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.git;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.sshd.common.channel.ChannelOutputStream;
+import org.apache.sshd.server.AbstractCommandSupport;
+
+/**
+ * Provides basic support for GIT command implementations
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractGitCommand extends AbstractCommandSupport {
+ public static final int CHAR = 0x001;
+ public static final int DELIMITER = 0x002;
+ public static final int STARTQUOTE = 0x004;
+ public static final int ENDQUOTE = 0x008;
+
+ private String rootDir;
+
+ protected AbstractGitCommand(String rootDir, String command, ExecutorService executorService, boolean shutdownOnExit) {
+ super(command, executorService, shutdownOnExit);
+ this.rootDir = rootDir;
+ }
+
+ public String getRootDir() {
+ return rootDir;
+ }
+
+ @Override
+ public void setOutputStream(OutputStream out) {
+ super.setOutputStream(out);
+ if (out instanceof ChannelOutputStream) {
+ ((ChannelOutputStream) out).setNoDelay(true);
+ }
+ }
+
+ @Override
+ public void setErrorStream(OutputStream err) {
+ super.setErrorStream(err);
+ if (err instanceof ChannelOutputStream) {
+ ((ChannelOutputStream) err).setNoDelay(true);
+ }
+ }
+
+ /**
+ * Parses delimited string and returns an array containing the tokens. This
+ * parser obeys quotes, so the delimiter character will be ignored if it is
+ * inside of a quote. This method assumes that the quote character is not
+ * included in the set of delimiter characters.
+ *
+ * @param value the delimited string to parse.
+ * @param delim the characters delimiting the tokens.
+ * @param trim {@code true} if the strings are trimmed before being added to the list
+ * @return a list of string or an empty list if there are none.
+ */
+ public static List<String> parseDelimitedString(String value, String delim, boolean trim) {
+ if (value == null) {
+ value = "";
+ }
+
+ List<String> list = new ArrayList<>();
+ StringBuilder sb = new StringBuilder();
+ int expecting = CHAR | DELIMITER | STARTQUOTE;
+ boolean isEscaped = false;
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ boolean isDelimiter = delim.indexOf(c) >= 0;
+ if (!isEscaped && (c == '\\')) {
+ isEscaped = true;
+ continue;
+ }
+
+ if (isEscaped) {
+ sb.append(c);
+ } else if (isDelimiter && ((expecting & DELIMITER) != 0)) {
+ if (trim) {
+ String str = sb.toString();
+ list.add(str.trim());
+ } else {
+ list.add(sb.toString());
+ }
+ sb.delete(0, sb.length());
+ expecting = CHAR | DELIMITER | STARTQUOTE;
+ } else if ((c == '"') && ((expecting & STARTQUOTE) != 0)) {
+ sb.append(c);
+ expecting = CHAR | ENDQUOTE;
+ } else if ((c == '"') && ((expecting & ENDQUOTE) != 0)) {
+ sb.append(c);
+ expecting = CHAR | STARTQUOTE | DELIMITER;
+ } else if ((expecting & CHAR) != 0) {
+ sb.append(c);
+ } else {
+ throw new IllegalArgumentException("Invalid delimited string: " + value);
+ }
+
+ isEscaped = false;
+ }
+
+ if (sb.length() > 0) {
+ if (trim) {
+ String str = sb.toString();
+ list.add(str.trim());
+ } else {
+ list.add(sb.toString());
+ }
+ }
+
+ return list;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommandFactory.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommandFactory.java b/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommandFactory.java
new file mode 100644
index 0000000..337643d
--- /dev/null
+++ b/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommandFactory.java
@@ -0,0 +1,96 @@
+/*
+ * 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.git;
+
+import java.util.concurrent.ExecutorService;
+
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.threads.ExecutorServiceCarrier;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.scp.UnknownCommand;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractGitCommandFactory implements CommandFactory, ExecutorServiceCarrier {
+ private final String rootDir;
+ private final CommandFactory delegate;
+ private final String cmdPrefix;
+ private ExecutorService executorService;
+ private boolean shutdownOnExit;
+
+ protected AbstractGitCommandFactory(String rootDir, CommandFactory delegate, String cmdPrefix) {
+ this.rootDir = rootDir;
+ this.delegate = delegate;
+ this.cmdPrefix = ValidateUtils.checkNotNullAndNotEmpty(cmdPrefix, "No command prefix provided");
+ }
+
+ public AbstractGitCommandFactory withExecutorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ return this;
+ }
+
+ public String getRootDir() {
+ return rootDir;
+ }
+
+ public CommandFactory getDelegate() {
+ return delegate;
+ }
+
+ public String getCommandPrefix() {
+ return cmdPrefix;
+ }
+
+ public AbstractGitCommandFactory withShutdownOnExit(boolean shutdownOnExit) {
+ this.shutdownOnExit = shutdownOnExit;
+ return this;
+ }
+
+ @Override
+ public ExecutorService getExecutorService() {
+ return executorService;
+ }
+
+ @Override
+ public boolean isShutdownOnExit() {
+ return shutdownOnExit;
+ }
+
+ @Override
+ public Command createCommand(String command) {
+ String prefix = getCommandPrefix();
+ if (command.startsWith(prefix)) {
+ return createGitCommand(command);
+ }
+
+ CommandFactory delegate = getDelegate();
+ if (delegate != null) {
+ return delegate.createCommand(command);
+ } else {
+ return new UnknownCommand(command);
+ }
+ }
+
+ protected abstract AbstractGitCommand createGitCommand(String command);
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-git/src/main/java/org/apache/sshd/git/pack/GitPackCommand.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/main/java/org/apache/sshd/git/pack/GitPackCommand.java b/sshd-git/src/main/java/org/apache/sshd/git/pack/GitPackCommand.java
index 229cbfb..8aebac5 100644
--- a/sshd-git/src/main/java/org/apache/sshd/git/pack/GitPackCommand.java
+++ b/sshd-git/src/main/java/org/apache/sshd/git/pack/GitPackCommand.java
@@ -19,20 +19,15 @@
package org.apache.sshd.git.pack;
import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.ExecutorService;
-import org.apache.sshd.common.channel.ChannelOutputStream;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-import org.apache.sshd.server.Command;
+import org.apache.sshd.git.AbstractGitCommand;
import org.apache.sshd.server.Environment;
-import org.apache.sshd.server.ExitCallback;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.transport.ReceivePack;
+import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.UploadPack;
import org.eclipse.jgit.util.FS;
@@ -41,166 +36,56 @@ import org.eclipse.jgit.util.FS;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class GitPackCommand extends AbstractLoggingBean implements Command, Runnable {
-
- private static final int CHAR = 1;
- private static final int DELIMITER = 2;
- private static final int STARTQUOTE = 4;
- private static final int ENDQUOTE = 8;
-
- private String rootDir;
- private String command;
- private InputStream in;
- private OutputStream out;
- private OutputStream err;
- private ExitCallback callback;
-
- public GitPackCommand(String rootDir, String command) {
- this.rootDir = rootDir;
- this.command = command;
- }
-
- @Override
- public void setInputStream(InputStream in) {
- this.in = in;
- }
-
- @Override
- public void setOutputStream(OutputStream out) {
- this.out = out;
- if (out instanceof ChannelOutputStream) {
- ((ChannelOutputStream) out).setNoDelay(true);
- }
- }
-
- @Override
- public void setErrorStream(OutputStream err) {
- this.err = err;
- if (err instanceof ChannelOutputStream) {
- ((ChannelOutputStream) err).setNoDelay(true);
- }
- }
-
- @Override
- public void setExitCallback(ExitCallback callback) {
- this.callback = callback;
- }
-
- @Override
- public void start(Environment env) throws IOException {
- Thread thread = new Thread(this);
- thread.setDaemon(true);
- thread.start();
+public class GitPackCommand extends AbstractGitCommand {
+ /**
+ * @param rootDir Root directory for the command
+ * @param command Command to execute
+ * @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
+ */
+ public GitPackCommand(String rootDir, String command, ExecutorService executorService, boolean shutdownOnExit) {
+ super(rootDir, command, executorService, shutdownOnExit);
}
@Override
public void run() {
+ String command = getCommand();
try {
List<String> strs = parseDelimitedString(command, " ", true);
String[] args = strs.toArray(new String[strs.size()]);
for (int i = 0; i < args.length; i++) {
- if (args[i].startsWith("'") && args[i].endsWith("'")) {
- args[i] = args[i].substring(1, args[i].length() - 1);
+ String argVal = args[i];
+ if (argVal.startsWith("'") && argVal.endsWith("'")) {
+ args[i] = argVal.substring(1, argVal.length() - 1);
+ argVal = args[i];
}
- if (args[i].startsWith("\"") && args[i].endsWith("\"")) {
- args[i] = args[i].substring(1, args[i].length() - 1);
+ if (argVal.startsWith("\"") && argVal.endsWith("\"")) {
+ args[i] = argVal.substring(1, argVal.length() - 1);
+ argVal = args[i];
}
}
if (args.length != 2) {
throw new IllegalArgumentException("Invalid git command line: " + command);
}
+
+ String rootDir = getRootDir();
File srcGitdir = new File(rootDir, args[1]);
RepositoryCache.FileKey key = RepositoryCache.FileKey.lenient(srcGitdir, FS.DETECTED);
Repository db = key.open(true /* must exist */);
- if ("git-upload-pack".equals(args[0])) {
- new UploadPack(db).upload(in, out, err);
- } else if ("git-receive-pack".equals(args[0])) {
- new ReceivePack(db).receive(in, out, err);
+ if (RemoteConfig.DEFAULT_UPLOAD_PACK.equals(args[0])) {
+ new UploadPack(db).upload(getInputStream(), getOutputStream(), getErrorStream());
+ } else if (RemoteConfig.DEFAULT_RECEIVE_PACK.equals(args[0])) {
+ new ReceivePack(db).receive(getInputStream(), getOutputStream(), getErrorStream());
} else {
throw new IllegalArgumentException("Unknown git command: " + command);
}
- if (callback != null) {
- callback.onExit(0);
- }
+ onExit(0);
} catch (Throwable t) {
- log.warn("Failed {} to execute command={}: {}",
- t.getClass().getSimpleName(), command, t.getMessage());
- if (callback != null) {
- callback.onExit(-1, t.getClass().getSimpleName());
- }
- }
- }
-
- @Override
- public void destroy() {
- //To change body of implemented methods use File | Settings | File Templates.
- }
-
- /**
- * Parses delimited string and returns an array containing the tokens. This
- * parser obeys quotes, so the delimiter character will be ignored if it is
- * inside of a quote. This method assumes that the quote character is not
- * included in the set of delimiter characters.
- *
- * @param value the delimited string to parse.
- * @param delim the characters delimiting the tokens.
- * @param trim {@code true} if the strings are trimmed before being added to the list
- * @return a list of string or an empty list if there are none.
- */
- private static List<String> parseDelimitedString(String value, String delim, boolean trim) {
- if (value == null) {
- value = "";
+ onExit(-1, t.getClass().getSimpleName());
}
-
- List<String> list = new ArrayList<>();
- StringBuilder sb = new StringBuilder();
- int expecting = CHAR | DELIMITER | STARTQUOTE;
- boolean isEscaped = false;
- for (int i = 0; i < value.length(); i++) {
- char c = value.charAt(i);
- boolean isDelimiter = delim.indexOf(c) >= 0;
- if (!isEscaped && (c == '\\')) {
- isEscaped = true;
- continue;
- }
-
- if (isEscaped) {
- sb.append(c);
- } else if (isDelimiter && ((expecting & DELIMITER) != 0)) {
- if (trim) {
- String str = sb.toString();
- list.add(str.trim());
- } else {
- list.add(sb.toString());
- }
- sb.delete(0, sb.length());
- expecting = CHAR | DELIMITER | STARTQUOTE;
- } else if ((c == '"') && ((expecting & STARTQUOTE) != 0)) {
- sb.append(c);
- expecting = CHAR | ENDQUOTE;
- } else if ((c == '"') && ((expecting & ENDQUOTE) != 0)) {
- sb.append(c);
- expecting = CHAR | STARTQUOTE | DELIMITER;
- } else if ((expecting & CHAR) != 0) {
- sb.append(c);
- } else {
- throw new IllegalArgumentException("Invalid delimited string: " + value);
- }
-
- isEscaped = false;
- }
-
- if (sb.length() > 0) {
- if (trim) {
- String str = sb.toString();
- list.add(str.trim());
- } else {
- list.add(sb.toString());
- }
- }
-
- return list;
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-git/src/main/java/org/apache/sshd/git/pack/GitPackCommandFactory.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/main/java/org/apache/sshd/git/pack/GitPackCommandFactory.java b/sshd-git/src/main/java/org/apache/sshd/git/pack/GitPackCommandFactory.java
index b5399c5..e12c228 100644
--- a/sshd-git/src/main/java/org/apache/sshd/git/pack/GitPackCommandFactory.java
+++ b/sshd-git/src/main/java/org/apache/sshd/git/pack/GitPackCommandFactory.java
@@ -18,37 +18,39 @@
*/
package org.apache.sshd.git.pack;
-import org.apache.sshd.server.Command;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.sshd.git.AbstractGitCommandFactory;
import org.apache.sshd.server.CommandFactory;
-import org.apache.sshd.server.scp.UnknownCommand;
/**
* TODO Add javadoc
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class GitPackCommandFactory implements CommandFactory {
-
- private final String rootDir;
- private final CommandFactory delegate;
+public class GitPackCommandFactory extends AbstractGitCommandFactory {
+ public static final String GIT_COMMAND_PREFIX = "git-";
public GitPackCommandFactory(String rootDir) {
this(rootDir, null);
}
public GitPackCommandFactory(String rootDir, CommandFactory delegate) {
- this.rootDir = rootDir;
- this.delegate = delegate;
+ super(rootDir, delegate, GIT_COMMAND_PREFIX);
+ }
+
+ @Override
+ public GitPackCommandFactory withExecutorService(ExecutorService executorService) {
+ return (GitPackCommandFactory) super.withExecutorService(executorService);
+ }
+
+ @Override
+ public GitPackCommandFactory withShutdownOnExit(boolean shutdownOnExit) {
+ return (GitPackCommandFactory) super.withShutdownOnExit(shutdownOnExit);
}
@Override
- public Command createCommand(String command) {
- if (command.startsWith("git-")) {
- return new GitPackCommand(rootDir, command);
- } else if (delegate != null) {
- return delegate.createCommand(command);
- } else {
- return new UnknownCommand(command);
- }
+ public GitPackCommand createGitCommand(String command) {
+ return new GitPackCommand(getRootDir(), command, getExecutorService(), isShutdownOnExit());
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-git/src/main/java/org/apache/sshd/git/pgm/EmbeddedCommandRunner.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/main/java/org/apache/sshd/git/pgm/EmbeddedCommandRunner.java b/sshd-git/src/main/java/org/apache/sshd/git/pgm/EmbeddedCommandRunner.java
index 8ccc510..204dc80 100644
--- a/sshd-git/src/main/java/org/apache/sshd/git/pgm/EmbeddedCommandRunner.java
+++ b/sshd-git/src/main/java/org/apache/sshd/git/pgm/EmbeddedCommandRunner.java
@@ -81,8 +81,8 @@ public class EmbeddedCommandRunner {
* @param err the error stream, may be null in which case the system error stream will be used
* @throws Exception if an error occurs
*/
- public void execute(final String[] argv, InputStream in, OutputStream out, OutputStream err) throws Exception {
- final CmdLineParser clp = new CmdLineParser(this);
+ public void execute(String[] argv, InputStream in, OutputStream out, OutputStream err) throws Exception {
+ CmdLineParser clp = new CmdLineParser(this);
PrintWriter writer = new PrintWriter(err != null ? err : System.err);
try {
clp.parseArgument(argv);
@@ -95,7 +95,7 @@ public class EmbeddedCommandRunner {
}
if (argv.length == 0 || help) {
- final String ex = clp.printExample(OptionHandlerFilter.ALL, CLIText.get().resourceBundle());
+ String ex = clp.printExample(OptionHandlerFilter.ALL, CLIText.get().resourceBundle());
writer.println("jgit" + ex + " command [ARG ...]"); //$NON-NLS-1$
if (help) {
writer.println();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-git/src/main/java/org/apache/sshd/git/pgm/GitPgmCommand.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/main/java/org/apache/sshd/git/pgm/GitPgmCommand.java b/sshd-git/src/main/java/org/apache/sshd/git/pgm/GitPgmCommand.java
index 013f4e5..2b7a1ab 100644
--- a/sshd-git/src/main/java/org/apache/sshd/git/pgm/GitPgmCommand.java
+++ b/sshd-git/src/main/java/org/apache/sshd/git/pgm/GitPgmCommand.java
@@ -19,15 +19,12 @@
package org.apache.sshd.git.pgm;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.ExecutorService;
-import org.apache.sshd.common.channel.ChannelOutputStream;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-import org.apache.sshd.server.Command;
+import org.apache.sshd.git.AbstractGitCommand;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
@@ -36,60 +33,24 @@ import org.apache.sshd.server.ExitCallback;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class GitPgmCommand extends AbstractLoggingBean implements Command, Runnable {
-
- private static final int CHAR = 1;
- private static final int DELIMITER = 2;
- private static final int STARTQUOTE = 4;
- private static final int ENDQUOTE = 8;
-
- private String rootDir;
- private String command;
- private InputStream in;
- private OutputStream out;
- private OutputStream err;
- private ExitCallback callback;
-
- public GitPgmCommand(String rootDir, String command) {
- this.rootDir = rootDir;
- this.command = command;
- }
-
- @Override
- public void setInputStream(InputStream in) {
- this.in = in;
- }
-
- @Override
- public void setOutputStream(OutputStream out) {
- this.out = out;
- if (out instanceof ChannelOutputStream) {
- ((ChannelOutputStream) out).setNoDelay(true);
- }
- }
-
- @Override
- public void setErrorStream(OutputStream err) {
- this.err = err;
- if (err instanceof ChannelOutputStream) {
- ((ChannelOutputStream) err).setNoDelay(true);
- }
- }
-
- @Override
- public void setExitCallback(ExitCallback callback) {
- this.callback = callback;
- }
-
- @Override
- public void start(Environment env) throws IOException {
- Thread thread = new Thread(this);
- thread.setDaemon(true);
- thread.start();
+public class GitPgmCommand extends AbstractGitCommand {
+ /**
+ * @param rootDir Root directory for the command
+ * @param command Command to execute
+ * @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
+ */
+ public GitPgmCommand(String rootDir, String command, ExecutorService executorService, boolean shutdownOnExit) {
+ super(rootDir, command, executorService, shutdownOnExit);
}
@Override
public void run() {
+ String command = getCommand();
+ ExitCallback callback = getExitCallback();
+ OutputStream err = getErrorStream();
try {
List<String> strs = parseDelimitedString(command, " ", true);
String[] args = strs.toArray(new String[strs.size()]);
@@ -102,7 +63,7 @@ public class GitPgmCommand extends AbstractLoggingBean implements Command, Runna
}
}
- new EmbeddedCommandRunner(rootDir).execute(args, in, out, err);
+ new EmbeddedCommandRunner(getRootDir()).execute(args, getInputStream(), getOutputStream(), err);
if (callback != null) {
callback.onExit(0);
}
@@ -119,76 +80,4 @@ public class GitPgmCommand extends AbstractLoggingBean implements Command, Runna
}
}
}
-
- @Override
- public void destroy() {
- //To change body of implemented methods use File | Settings | File Templates.
- }
-
- /**
- * Parses delimited string and returns an array containing the tokens. This
- * parser obeys quotes, so the delimiter character will be ignored if it is
- * inside of a quote. This method assumes that the quote character is not
- * included in the set of delimiter characters.
- *
- * @param value the delimited string to parse.
- * @param delim the characters delimiting the tokens.
- * @param trim {@code true} if the strings are trimmed before being added to the list
- * @return a list of string or an empty list if there are none.
- */
- private static List<String> parseDelimitedString(String value, String delim, boolean trim) {
- if (value == null) {
- value = "";
- }
-
- List<String> list = new ArrayList<>();
- StringBuilder sb = new StringBuilder();
- int expecting = CHAR | DELIMITER | STARTQUOTE;
- boolean isEscaped = false;
- for (int i = 0; i < value.length(); i++) {
- char c = value.charAt(i);
- boolean isDelimiter = delim.indexOf(c) >= 0;
-
- if (!isEscaped && (c == '\\')) {
- isEscaped = true;
- continue;
- }
-
- if (isEscaped) {
- sb.append(c);
- } else if (isDelimiter && ((expecting & DELIMITER) != 0)) {
- if (trim) {
- String str = sb.toString();
- list.add(str.trim());
- } else {
- list.add(sb.toString());
- }
- sb.delete(0, sb.length());
- expecting = CHAR | DELIMITER | STARTQUOTE;
- } else if ((c == '"') && ((expecting & STARTQUOTE) != 0)) {
- sb.append(c);
- expecting = CHAR | ENDQUOTE;
- } else if ((c == '"') && ((expecting & ENDQUOTE) != 0)) {
- sb.append(c);
- expecting = CHAR | STARTQUOTE | DELIMITER;
- } else if ((expecting & CHAR) != 0) {
- sb.append(c);
- } else {
- throw new IllegalArgumentException("Invalid delimited string: " + value);
- }
-
- isEscaped = false;
- }
-
- if (sb.length() > 0) {
- if (trim) {
- String str = sb.toString();
- list.add(str.trim());
- } else {
- list.add(sb.toString());
- }
- }
-
- return list;
- }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0d63503/sshd-git/src/main/java/org/apache/sshd/git/pgm/GitPgmCommandFactory.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/main/java/org/apache/sshd/git/pgm/GitPgmCommandFactory.java b/sshd-git/src/main/java/org/apache/sshd/git/pgm/GitPgmCommandFactory.java
index 8323528..cba6c4e 100644
--- a/sshd-git/src/main/java/org/apache/sshd/git/pgm/GitPgmCommandFactory.java
+++ b/sshd-git/src/main/java/org/apache/sshd/git/pgm/GitPgmCommandFactory.java
@@ -18,38 +18,39 @@
*/
package org.apache.sshd.git.pgm;
-import org.apache.sshd.server.Command;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.sshd.git.AbstractGitCommandFactory;
import org.apache.sshd.server.CommandFactory;
-import org.apache.sshd.server.scp.UnknownCommand;
/**
- * TODO Add javadoc
+ * Runs a GIT command locally using an embedded executor
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class GitPgmCommandFactory implements CommandFactory {
+public class GitPgmCommandFactory extends AbstractGitCommandFactory {
public static final String GIT_COMMAND_PREFIX = "git ";
- private final String rootDir;
- private final CommandFactory delegate;
-
public GitPgmCommandFactory(String rootDir) {
this(rootDir, null);
}
public GitPgmCommandFactory(String rootDir, CommandFactory delegate) {
- this.rootDir = rootDir;
- this.delegate = delegate;
+ super(rootDir, delegate, GIT_COMMAND_PREFIX);
+ }
+
+ @Override
+ public GitPgmCommandFactory withExecutorService(ExecutorService executorService) {
+ return (GitPgmCommandFactory) super.withExecutorService(executorService);
+ }
+
+ @Override
+ public GitPgmCommandFactory withShutdownOnExit(boolean shutdownOnExit) {
+ return (GitPgmCommandFactory) super.withShutdownOnExit(shutdownOnExit);
}
@Override
- public Command createCommand(String command) {
- if (command.startsWith(GIT_COMMAND_PREFIX)) {
- return new GitPgmCommand(rootDir, command.substring(GIT_COMMAND_PREFIX.length()));
- } else if (delegate != null) {
- return delegate.createCommand(command);
- } else {
- return new UnknownCommand(command);
- }
+ public GitPgmCommand createGitCommand(String command) {
+ return new GitPgmCommand(getRootDir(), command.substring(GIT_COMMAND_PREFIX.length()), getExecutorService(), isShutdownOnExit());
}
}