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:10 UTC
[1/2] mina-sshd git commit: [SSHD-798] Provide pluggable
GitLocationResolver for the GIT commands
Repository: mina-sshd
Updated Branches:
refs/heads/master 59a7fcf88 -> f8a3e7299
[SSHD-798] Provide pluggable GitLocationResolver for the 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/f8a3e729
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/f8a3e729
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/f8a3e729
Branch: refs/heads/master
Commit: f8a3e72993e3536e4b277e39c8ea864947c93098
Parents: e0d6350
Author: Goldstein Lyor <ly...@c-b4.com>
Authored: Sun Feb 18 17:06:35 2018 +0200
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Sun Feb 18 20:22:50 2018 +0200
----------------------------------------------------------------------
README.md | 37 +++++++++--
.../org/apache/sshd/git/AbstractGitCommand.java | 67 ++++++++++++++++++--
.../sshd/git/AbstractGitCommandFactory.java | 53 ++++++++++------
.../apache/sshd/git/GitLocationResolver.java | 55 ++++++++++++++++
.../sshd/git/GitLocationResolverCarrier.java | 29 +++++++++
.../apache/sshd/git/pack/GitPackCommand.java | 37 ++++++++---
.../sshd/git/pack/GitPackCommandFactory.java | 22 +++++--
.../sshd/git/pgm/EmbeddedCommandRunner.java | 29 +++------
.../org/apache/sshd/git/pgm/GitPgmCommand.java | 39 +++++++-----
.../sshd/git/pgm/GitPgmCommandFactory.java | 22 +++++--
.../sshd/git/pack/GitPackCommandTest.java | 8 +--
.../apache/sshd/git/pgm/GitPgmCommandTest.java | 10 +--
12 files changed, 306 insertions(+), 102 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f8a3e729/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 283cc85..a4925a3 100644
--- a/README.md
+++ b/README.md
@@ -1364,20 +1364,41 @@ client instance and re-use it:
### Server-side
-See `GitPackCommandFactory` and `GitPgmCommandFactory`. These command factories accept a delegate to which non-_git_ commands are routed:
+See `GitPackCommandFactory` and `GitPgmCommandFactory` - in order for the various commands to function correctly, they require a `GitLocationResolver`
+that is invoked in order to allow the user to decide which is the correct GIT repository root location for a given command. The resolver is provided
+with all the relevant details - including the command and server session through which the command was received:
```java
- sshd.setCommandFactory(new GitPackCommandFactory(rootDir, new MyCommandFactory()));
+ GitLocationResolver resolver = (cmd, session, fs) -> ...consult some code - perhaps based on the authenticated username...
+ sshd.setCommandFactory(new GitPackCommandFactory().withGitLocationResolver(resolver));
+
+```
+
+ These command factories also accept a delegate to which non-_git_ commands are routed:
+
+
+```java
+
+ sshd.setCommandFactory(new GitPackCommandFactory()
+ .withDelegate(new MyCommandFactory())
+ .withGitLocationResolver(resolver));
// Here is how it looks if SCP is also requested
- sshd.setCommandFactory(new GitPackCommandFactory(rootDir, new ScpCommandFactory(new MyCommandFactory())))
- // or
- sshd.setCommandFactory(new ScpCommandFactory(new GitPackCommandFactory(rootDir, new MyCommandFactory())))
+ sshd.setCommandFactory(new GitPackCommandFactory()
+ .withDelegate(new ScpCommandFactory()
+ .withDelegate(new MyCommandFactory()))
+ .withGitLocationResolver(resolver));
+
// or
- sshd.setCommandFactory(new GitPackCommandFactory(rootDir, new ScpCommandFactory(new MyCommandFactory())))
+ sshd.setCommandFactory(new ScpCommandFactory()
+ .withDelegate(new GitPackCommandFactory()
+ .withDelegate(new MyCommandFactory())
+ .withGitLocationResolver(resolver)));
+
// 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
@@ -1387,12 +1408,14 @@ is completed (regardless of whether successful or not):
```java
- sshd.setCommandFactory(new GitPackCommandFactory(rootDir, new MyCommandFactory())
+ sshd.setCommandFactory(new GitPackCommandFactory(resolver)
+ .withDelegate(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/f8a3e729/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
index 2ed85b0..d4dacfa 100644
--- a/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommand.java
+++ b/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommand.java
@@ -19,34 +19,65 @@
package org.apache.sshd.git;
+import java.io.IOException;
import java.io.OutputStream;
+import java.nio.file.FileSystem;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.ExecutorService;
import org.apache.sshd.common.channel.ChannelOutputStream;
+import org.apache.sshd.common.file.FileSystemAware;
import org.apache.sshd.server.AbstractCommandSupport;
+import org.apache.sshd.server.SessionAware;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.session.ServerSessionHolder;
/**
* 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 abstract class AbstractGitCommand
+ extends AbstractCommandSupport
+ implements SessionAware, FileSystemAware, ServerSessionHolder, GitLocationResolverCarrier {
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;
+ private final GitLocationResolver rootDirResolver;
+ private FileSystem fileSystem;
+ private ServerSession session;
- protected AbstractGitCommand(String rootDir, String command, ExecutorService executorService, boolean shutdownOnExit) {
+ protected AbstractGitCommand(GitLocationResolver rootDirResolver, String command, ExecutorService executorService, boolean shutdownOnExit) {
super(command, executorService, shutdownOnExit);
- this.rootDir = rootDir;
+ this.rootDirResolver = Objects.requireNonNull(rootDirResolver, "No GIT root directory resolver provided");
}
- public String getRootDir() {
- return rootDir;
+ @Override
+ public GitLocationResolver getGitLocationResolver() {
+ return rootDirResolver;
+ }
+
+ public FileSystem getFileSystem() {
+ return fileSystem;
+ }
+
+ @Override
+ public void setFileSystem(FileSystem fileSystem) {
+ this.fileSystem = fileSystem;
+ }
+
+ @Override
+ public ServerSession getServerSession() {
+ return session;
+ }
+
+ @Override
+ public void setSession(ServerSession session) {
+ this.session = session;
}
@Override
@@ -65,6 +96,30 @@ public abstract class AbstractGitCommand extends AbstractCommandSupport {
}
}
+ @Override
+ public void destroy() {
+ try {
+ super.destroy();
+ } finally {
+ FileSystem fs = getFileSystem();
+ if (fs != null) {
+ try {
+ fs.close();
+ } catch (UnsupportedOperationException | IOException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("destroy({}) - failed ({}) to close file system={}: {}",
+ this, e.getClass().getSimpleName(), fs, e.getMessage());
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "[session=" + getServerSession() + "]";
+ }
+
/**
* Parses delimited string and returns an array containing the tokens. This
* parser obeys quotes, so the delimiter character will be ignored if it is
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f8a3e729/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
index 337643d..0d42626 100644
--- a/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommandFactory.java
+++ b/sshd-git/src/main/java/org/apache/sshd/git/AbstractGitCommandFactory.java
@@ -32,34 +32,38 @@ import org.apache.sshd.server.scp.UnknownCommand;
*
* @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;
+public abstract class AbstractGitCommandFactory implements CommandFactory, ExecutorServiceCarrier, GitLocationResolverCarrier {
private final String cmdPrefix;
+ private GitLocationResolver rootDirResolver;
+ private CommandFactory delegate;
private ExecutorService executorService;
private boolean shutdownOnExit;
- protected AbstractGitCommandFactory(String rootDir, CommandFactory delegate, String cmdPrefix) {
- this.rootDir = rootDir;
- this.delegate = delegate;
+ /**
+ * @param cmdPrefix The command prefix used to detect and intercept GIT commands handled by this
+ * factory (never {@code null}/empty)
+ */
+ protected AbstractGitCommandFactory(String cmdPrefix) {
this.cmdPrefix = ValidateUtils.checkNotNullAndNotEmpty(cmdPrefix, "No command prefix provided");
}
- public AbstractGitCommandFactory withExecutorService(ExecutorService executorService) {
- this.executorService = executorService;
- return this;
+ public String getCommandPrefix() {
+ return cmdPrefix;
}
- public String getRootDir() {
- return rootDir;
+ @Override
+ public ExecutorService getExecutorService() {
+ return executorService;
}
- public CommandFactory getDelegate() {
- return delegate;
+ @Override
+ public boolean isShutdownOnExit() {
+ return shutdownOnExit;
}
- public String getCommandPrefix() {
- return cmdPrefix;
+ public AbstractGitCommandFactory withExecutorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ return this;
}
public AbstractGitCommandFactory withShutdownOnExit(boolean shutdownOnExit) {
@@ -68,13 +72,22 @@ public abstract class AbstractGitCommandFactory implements CommandFactory, Execu
}
@Override
- public ExecutorService getExecutorService() {
- return executorService;
+ public GitLocationResolver getGitLocationResolver() {
+ return rootDirResolver;
}
- @Override
- public boolean isShutdownOnExit() {
- return shutdownOnExit;
+ public AbstractGitCommandFactory withGitLocationResolver(GitLocationResolver rootDirResolver) {
+ this.rootDirResolver = rootDirResolver;
+ return this;
+ }
+
+ public CommandFactory getDelegate() {
+ return delegate;
+ }
+
+ public AbstractGitCommandFactory withDelegate(CommandFactory delegate) {
+ this.delegate = delegate;
+ return this;
}
@Override
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f8a3e729/sshd-git/src/main/java/org/apache/sshd/git/GitLocationResolver.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/main/java/org/apache/sshd/git/GitLocationResolver.java b/sshd-git/src/main/java/org/apache/sshd/git/GitLocationResolver.java
new file mode 100644
index 0000000..7679c90
--- /dev/null
+++ b/sshd-git/src/main/java/org/apache/sshd/git/GitLocationResolver.java
@@ -0,0 +1,55 @@
+/*
+ * 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.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.Path;
+import java.util.Objects;
+
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ * Used by the GIT command(s) to resolve the root directory of the GIT repository
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface GitLocationResolver {
+ /**
+ * @param command The received command
+ * @param session The {@link ServerSession} through which the command was received
+ * @param fs The {@link FileSystem} associated with the server session
+ * @return The local GIT repository root path
+ * @throws IOException If failed to resolve
+ */
+ Path resolveRootDirectory(String command, ServerSession session, FileSystem fs) throws IOException;
+
+ /**
+ * Creates a resolver that returns the same root directory for any invocation of
+ * {@link #resolveRootDirectory(String, ServerSession, FileSystem) resolveRootDirectory}
+ *
+ * @param rootDir The (never {@code null}) root directory to return
+ * @return The wrapper resolver
+ */
+ static GitLocationResolver constantPath(Path rootDir) {
+ Objects.requireNonNull(rootDir, "No root directory provided");
+ return (cmd, session, fs) -> rootDir;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f8a3e729/sshd-git/src/main/java/org/apache/sshd/git/GitLocationResolverCarrier.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/main/java/org/apache/sshd/git/GitLocationResolverCarrier.java b/sshd-git/src/main/java/org/apache/sshd/git/GitLocationResolverCarrier.java
new file mode 100644
index 0000000..2b53524
--- /dev/null
+++ b/sshd-git/src/main/java/org/apache/sshd/git/GitLocationResolverCarrier.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface GitLocationResolverCarrier {
+ GitLocationResolver getGitLocationResolver();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f8a3e729/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 8aebac5..bb123da 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
@@ -18,11 +18,15 @@
*/
package org.apache.sshd.git.pack;
-import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.ExecutorService;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.git.AbstractGitCommand;
+import org.apache.sshd.git.GitLocationResolver;
import org.apache.sshd.server.Environment;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
@@ -38,15 +42,15 @@ import org.eclipse.jgit.util.FS;
*/
public class GitPackCommand extends AbstractGitCommand {
/**
- * @param rootDir Root directory for the command
+ * @param rootDirResolver Resolver for GIT root directory
* @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);
+ public GitPackCommand(GitLocationResolver rootDirResolver, String command, ExecutorService executorService, boolean shutdownOnExit) {
+ super(rootDirResolver, command, executorService, shutdownOnExit);
}
@Override
@@ -71,13 +75,13 @@ public class GitPackCommand extends AbstractGitCommand {
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);
+ String subCommand = args[0];
+ Path rootDir = resolveRootDirectory(command, subCommand, args[1]);
+ RepositoryCache.FileKey key = RepositoryCache.FileKey.lenient(rootDir.toFile(), FS.DETECTED);
Repository db = key.open(true /* must exist */);
- if (RemoteConfig.DEFAULT_UPLOAD_PACK.equals(args[0])) {
+ if (RemoteConfig.DEFAULT_UPLOAD_PACK.equals(subCommand)) {
new UploadPack(db).upload(getInputStream(), getOutputStream(), getErrorStream());
- } else if (RemoteConfig.DEFAULT_RECEIVE_PACK.equals(args[0])) {
+ } else if (RemoteConfig.DEFAULT_RECEIVE_PACK.equals(subCommand)) {
new ReceivePack(db).receive(getInputStream(), getOutputStream(), getErrorStream());
} else {
throw new IllegalArgumentException("Unknown git command: " + command);
@@ -88,4 +92,19 @@ public class GitPackCommand extends AbstractGitCommand {
onExit(-1, t.getClass().getSimpleName());
}
}
+
+ protected Path resolveRootDirectory(String command, String subCommand, String pathArg) throws IOException {
+ GitLocationResolver resolver = getGitLocationResolver();
+ Path rootDir = resolver.resolveRootDirectory(command, getServerSession(), getFileSystem());
+ ValidateUtils.checkState(rootDir != null, "No root directory provided for %s command", command);
+ int len = GenericUtils.length(pathArg);
+ // Strip any leading path separator since we use relative to root
+ if ((len > 0) && (pathArg.charAt(0) == '/')) {
+ pathArg = (len > 1) ? pathArg.substring(1) : "";
+ len--;
+ }
+
+ ValidateUtils.checkNotNullAndNotEmpty(pathArg, "No %s command sub-path specified", subCommand);
+ return rootDir.resolve(pathArg);
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f8a3e729/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 e12c228..a21365f 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
@@ -21,6 +21,7 @@ package org.apache.sshd.git.pack;
import java.util.concurrent.ExecutorService;
import org.apache.sshd.git.AbstractGitCommandFactory;
+import org.apache.sshd.git.GitLocationResolver;
import org.apache.sshd.server.CommandFactory;
/**
@@ -31,12 +32,23 @@ import org.apache.sshd.server.CommandFactory;
public class GitPackCommandFactory extends AbstractGitCommandFactory {
public static final String GIT_COMMAND_PREFIX = "git-";
- public GitPackCommandFactory(String rootDir) {
- this(rootDir, null);
+ public GitPackCommandFactory() {
+ super(GIT_COMMAND_PREFIX);
}
- public GitPackCommandFactory(String rootDir, CommandFactory delegate) {
- super(rootDir, delegate, GIT_COMMAND_PREFIX);
+ public GitPackCommandFactory(GitLocationResolver resolver) {
+ super(GIT_COMMAND_PREFIX);
+ withGitLocationResolver(resolver);
+ }
+
+ @Override
+ public GitPackCommandFactory withDelegate(CommandFactory delegate) {
+ return (GitPackCommandFactory) super.withDelegate(delegate);
+ }
+
+ @Override
+ public GitPackCommandFactory withGitLocationResolver(GitLocationResolver rootDirResolver) {
+ return (GitPackCommandFactory) super.withGitLocationResolver(rootDirResolver);
}
@Override
@@ -51,6 +63,6 @@ public class GitPackCommandFactory extends AbstractGitCommandFactory {
@Override
public GitPackCommand createGitCommand(String command) {
- return new GitPackCommand(getRootDir(), command, getExecutorService(), isShutdownOnExit());
+ return new GitPackCommand(getGitLocationResolver(), command, getExecutorService(), isShutdownOnExit());
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f8a3e729/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 204dc80..20c0902 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
@@ -26,9 +26,11 @@ import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Repository;
@@ -66,10 +68,10 @@ public class EmbeddedCommandRunner {
@Argument(index = 1, metaVar = "metaVar_arg")
private List<String> arguments = new ArrayList<>();
- private String rootDir;
+ private Path rootDir;
- public EmbeddedCommandRunner(String rootDir) {
- this.rootDir = rootDir;
+ public EmbeddedCommandRunner(Path rootDir) {
+ this.rootDir = Objects.requireNonNull(rootDir, "No root directory specified");
}
/**
@@ -126,24 +128,9 @@ public class EmbeddedCommandRunner {
throw new Die(true);
}
- gitdir = new File(rootDir, gitdir).getPath();
-
- final TextBuiltin cmd = subcommand;
-// cmd.ins = in;
-// cmd.outs = out;
-// cmd.errs = err;
-// if (cmd.requiresRepository())
-// cmd.init(openGitDir(gitdir), null);
-// else
-// cmd.init(null, gitdir);
-// try {
-// cmd.execute(arguments.toArray(new String[arguments.size()]));
-// } finally {
-// if (cmd.outw != null)
-// cmd.outw.flush();
-// if (cmd.errw != null)
-// cmd.errw.flush();
-// }
+ gitdir = Objects.toString(rootDir.resolve(gitdir));
+
+ TextBuiltin cmd = subcommand;
set(cmd, "ins", in);
set(cmd, "outs", out);
set(cmd, "errs", err);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f8a3e729/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 2b7a1ab..a80012d 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
@@ -21,12 +21,14 @@ package org.apache.sshd.git.pgm;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.ExecutorService;
+import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.git.AbstractGitCommand;
+import org.apache.sshd.git.GitLocationResolver;
import org.apache.sshd.server.Environment;
-import org.apache.sshd.server.ExitCallback;
/**
* TODO Add javadoc
@@ -35,49 +37,52 @@ import org.apache.sshd.server.ExitCallback;
*/
public class GitPgmCommand extends AbstractGitCommand {
/**
- * @param rootDir Root directory for the command
+ * @param rootDirResolver Resolver for GIT root directory
* @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);
+ public GitPgmCommand(GitLocationResolver rootDirResolver, String command, ExecutorService executorService, boolean shutdownOnExit) {
+ super(rootDirResolver, 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()]);
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];
}
}
- new EmbeddedCommandRunner(getRootDir()).execute(args, getInputStream(), getOutputStream(), err);
- if (callback != null) {
- callback.onExit(0);
- }
+ GitLocationResolver resolver = getGitLocationResolver();
+ Path rootDir = resolver.resolveRootDirectory(command, getServerSession(), getFileSystem());
+ ValidateUtils.checkState(rootDir != null, "No root directory provided for %s command", command);
+
+ new EmbeddedCommandRunner(rootDir).execute(args, getInputStream(), getOutputStream(), err);
+ onExit(0);
} catch (Throwable t) {
try {
err.write((t.getMessage() + "\n").getBytes(StandardCharsets.UTF_8));
err.flush();
} catch (IOException e) {
log.warn("Failed {} to flush command={} failure: {}",
- e.getClass().getSimpleName(), command, e.getMessage());
- }
- if (callback != null) {
- callback.onExit(-1);
+ e.getClass().getSimpleName(), command, e.getMessage());
}
+ onExit(-1, t.getMessage());
}
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f8a3e729/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 cba6c4e..7e28fe8 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
@@ -21,6 +21,7 @@ package org.apache.sshd.git.pgm;
import java.util.concurrent.ExecutorService;
import org.apache.sshd.git.AbstractGitCommandFactory;
+import org.apache.sshd.git.GitLocationResolver;
import org.apache.sshd.server.CommandFactory;
/**
@@ -31,12 +32,23 @@ import org.apache.sshd.server.CommandFactory;
public class GitPgmCommandFactory extends AbstractGitCommandFactory {
public static final String GIT_COMMAND_PREFIX = "git ";
- public GitPgmCommandFactory(String rootDir) {
- this(rootDir, null);
+ public GitPgmCommandFactory() {
+ super(GIT_COMMAND_PREFIX);
}
- public GitPgmCommandFactory(String rootDir, CommandFactory delegate) {
- super(rootDir, delegate, GIT_COMMAND_PREFIX);
+ public GitPgmCommandFactory(GitLocationResolver resolver) {
+ super(GIT_COMMAND_PREFIX);
+ withGitLocationResolver(resolver);
+ }
+
+ @Override
+ public GitPgmCommandFactory withDelegate(CommandFactory delegate) {
+ return (GitPgmCommandFactory) super.withDelegate(delegate);
+ }
+
+ @Override
+ public GitPgmCommandFactory withGitLocationResolver(GitLocationResolver rootDirResolver) {
+ return (GitPgmCommandFactory) super.withGitLocationResolver(rootDirResolver);
}
@Override
@@ -51,6 +63,6 @@ public class GitPgmCommandFactory extends AbstractGitCommandFactory {
@Override
public GitPgmCommand createGitCommand(String command) {
- return new GitPgmCommand(getRootDir(), command.substring(GIT_COMMAND_PREFIX.length()), getExecutorService(), isShutdownOnExit());
+ return new GitPgmCommand(getGitLocationResolver(), command.substring(GIT_COMMAND_PREFIX.length()), getExecutorService(), isShutdownOnExit());
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f8a3e729/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
index 339170c..f6bc31d 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
@@ -26,6 +26,7 @@ import com.jcraft.jsch.JSch;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.git.GitLocationResolver;
import org.apache.sshd.git.transport.GitSshdSessionFactory;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.auth.password.AcceptAllPasswordAuthenticator;
@@ -34,6 +35,7 @@ import org.apache.sshd.util.test.BaseTestSupport;
import org.apache.sshd.util.test.JSchLogger;
import org.apache.sshd.util.test.Utils;
import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
@@ -67,18 +69,16 @@ public class GitPackCommandTest extends BaseTestSupport {
public void testGitPack() throws Exception {
Assume.assumeFalse("On windows this activates TortoisePlink", OsUtils.isWin32());
- Path targetParent = detectTargetFolder().getParent();
Path gitRootDir = getTempTargetRelativeFile(getClass().getSimpleName());
-
try (SshServer sshd = setupTestServer()) {
Path serverRootDir = gitRootDir.resolve("server");
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
- sshd.setCommandFactory(new GitPackCommandFactory(Utils.resolveRelativeRemotePath(targetParent, serverRootDir)));
+ sshd.setCommandFactory(new GitPackCommandFactory(GitLocationResolver.constantPath(serverRootDir)));
sshd.start();
int port = sshd.getPort();
try {
- Path serverDir = serverRootDir.resolve("test.git");
+ Path serverDir = serverRootDir.resolve(getCurrentTestName() + Constants.DOT_GIT_EXT);
Utils.deleteRecursive(serverDir);
Git.init().setBare(true).setDirectory(serverDir.toFile()).call();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f8a3e729/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
index 4d84722..1e5973d 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
@@ -30,6 +30,7 @@ import org.apache.sshd.client.channel.ChannelExec;
import org.apache.sshd.client.channel.ClientChannelEvent;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.apache.sshd.git.GitLocationResolver;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
import org.apache.sshd.util.test.BaseTestSupport;
@@ -49,17 +50,10 @@ public class GitPgmCommandTest extends BaseTestSupport {
@Test
public void testGitPgm() throws Exception {
- Path targetParent = detectTargetFolder().getParent();
Path serverDir = getTempTargetRelativeFile(getClass().getSimpleName());
-
- //
- // TODO: the GitpgmCommandFactory is kept in the test tree
- // TODO: because it's quite limited for now
- //
-
try (SshServer sshd = setupTestServer()) {
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
- sshd.setCommandFactory(new GitPgmCommandFactory(Utils.resolveRelativeRemotePath(targetParent, serverDir)));
+ sshd.setCommandFactory(new GitPgmCommandFactory(GitLocationResolver.constantPath(serverDir)));
sshd.start();
int port = sshd.getPort();
[2/2] mina-sshd git commit: [SSHD-798] Provide capability to use a
user-defined ExecutorService when servicing GIT commands
Posted by lg...@apache.org.
[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());
}
}