James Moger
2014-03-14 503a853acad49ac6da7f520c26b3b27942dbfec5
src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java
@@ -14,10 +14,13 @@
package com.gitblit.transport.ssh.commands;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
@@ -26,13 +29,14 @@
import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.apache.sshd.server.SessionAware;
import org.apache.sshd.server.session.ServerSession;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.transport.ssh.AbstractSshCommand;
import com.gitblit.transport.ssh.SshCommandContext;
import com.gitblit.utils.IdGenerator;
import com.gitblit.utils.WorkQueue;
@@ -41,18 +45,28 @@
import com.google.common.base.Charsets;
import com.google.common.util.concurrent.Atomics;
public abstract class BaseCommand extends AbstractSshCommand {
  private static final Logger log = LoggerFactory
      .getLogger(BaseCommand.class);
public abstract class BaseCommand implements Command, SessionAware {
   private static final Logger log = LoggerFactory.getLogger(BaseCommand.class);
   /** Ssh context */
   protected SshCommandContext ctx;
   protected InputStream in;
   protected OutputStream out;
   protected OutputStream err;
   protected ExitCallback exit;
   protected ServerSession session;
  /** Text of the command line which lead up to invoking this instance. */
  private String commandName = "";
  /** Unparsed command line options. */
  private String[] argv;
  /** Ssh context */
  protected SshCommandContext ctx;
  /** The task, as scheduled on a worker thread. */
  private final AtomicReference<Future<?>> task;
@@ -66,22 +80,42 @@
    this.executor = w.getDefaultQueue();
  }
   @Override
   public void setSession(final ServerSession session) {
      this.session = session;
   }
   @Override
   public void destroy() {
   }
   protected static PrintWriter toPrintWriter(final OutputStream o) {
      return new PrintWriter(new BufferedWriter(new OutputStreamWriter(o, Charsets.UTF_8)));
   }
   @Override
   public abstract void start(Environment env) throws IOException;
  public void setContext(SshCommandContext ctx) {
   this.ctx = ctx;
  }
   @Override
  public void setInputStream(final InputStream in) {
    this.in = in;
  }
   @Override
  public void setOutputStream(final OutputStream out) {
    this.out = out;
  }
   @Override
  public void setErrorStream(final OutputStream err) {
    this.err = err;
  }
   @Override
  public void setExitCallback(final ExitCallback callback) {
    this.exit = callback;
  }
@@ -117,7 +151,8 @@
   * <p>
   * This method must be explicitly invoked to cause a parse.
   *
   * @throws UnloggedFailure if the command line arguments were invalid.
    * @throws UnloggedFailure
    *             if the command line arguments were invalid.
   * @see Option
   * @see Argument
   */
@@ -130,9 +165,11 @@
   * <p>
   * This method must be explicitly invoked to cause a parse.
   *
   * @param options object whose fields declare Option and Argument annotations
   *        to describe the parameters of the command. Usually {@code this}.
   * @throws UnloggedFailure if the command line arguments were invalid.
    * @param options
    *            object whose fields declare Option and Argument annotations to
    *            describe the parameters of the command. Usually {@code this}.
    * @throws UnloggedFailure
    *             if the command line arguments were invalid.
   * @see Option
   * @see Argument
   */
@@ -233,7 +270,6 @@
    public void run() throws Exception;
  }
  /** Runnable function which can retrieve a project name related to the task */
  public static interface RepositoryCommandRunnable extends CommandRunnable {
   public String getRepository();
@@ -242,8 +278,8 @@
  /**
   * Spawn a function into its own thread.
   * <p>
   * Typically this should be invoked within {@link Command#start(Environment)},
   * such as:
    * Typically this should be invoked within
    * {@link Command#start(Environment)}, such as:
   *
   * <pre>
   * startThread(new Runnable() {
@@ -253,7 +289,8 @@
   * });
   * </pre>
   *
   * @param thunk the runnable to execute on the thread, performing the
    * @param thunk
    *            the runnable to execute on the thread, performing the
   *        command's logic.
   */
  protected void startThread(final Runnable thunk) {
@@ -272,7 +309,8 @@
   * lose access to request based resources as any callbacks previously
   * registered with {@link RequestCleanup} will fire.
   *
   * @param rc exit code for the remote client.
    * @param rc
    *            exit code for the remote client.
   */
  protected void onExit(final int rc) {
    exit.onExit(rc);
@@ -282,12 +320,8 @@
  }
  private int handleError(final Throwable e) {
    if ((e.getClass() == IOException.class
         && "Pipe closed".equals(e.getMessage()))
        || //
        (e.getClass() == SshException.class
         && "Already closed".equals(e.getMessage()))
        || //
      if ((e.getClass() == IOException.class && "Pipe closed".equals(e.getMessage())) || //
            (e.getClass() == SshException.class && "Already closed".equals(e.getMessage())) || //
        e.getClass() == InterruptedIOException.class) {
      // This is sshd telling us the client just dropped off while
      // we were waiting for a read or a write to complete. Either
@@ -339,8 +373,8 @@
  /**
   * Spawn a function into its own thread.
   * <p>
   * Typically this should be invoked within {@link Command#start(Environment)},
   * such as:
    * Typically this should be invoked within
    * {@link Command#start(Environment)}, such as:
   *
   * <pre>
   * startThread(new CommandRunnable() {
@@ -353,7 +387,8 @@
   * If the function throws an exception, it is translated to a simple message
   * for the client, a non-zero exit code, and the stack trace is logged.
   *
   * @param thunk the runnable to execute on the thread, performing the
    * @param thunk
    *            the runnable to execute on the thread, performing the
   *        command's logic.
   */
  protected void startThread(final CommandRunnable thunk) {
@@ -370,10 +405,12 @@
    /**
     * Create a new failure.
     *
     * @param exitCode exit code to return the client, which indicates the
     *        failure status of this command. Should be between 1 and 255,
     *        inclusive.
     * @param msg message to also send to the client's stderr.
       * @param exitCode
       *            exit code to return the client, which indicates the
       *            failure status of this command. Should be between 1 and
       *            255, inclusive.
       * @param msg
       *            message to also send to the client's stderr.
     */
    public Failure(final int exitCode, final String msg) {
      this(exitCode, msg, null);
@@ -382,12 +419,15 @@
    /**
     * Create a new failure.
     *
     * @param exitCode exit code to return the client, which indicates the
     *        failure status of this command. Should be between 1 and 255,
     *        inclusive.
     * @param msg message to also send to the client's stderr.
     * @param why stack trace to include in the server's log, but is not sent to
     *        the client's stderr.
       * @param exitCode
       *            exit code to return the client, which indicates the
       *            failure status of this command. Should be between 1 and
       *            255, inclusive.
       * @param msg
       *            message to also send to the client's stderr.
       * @param why
       *            stack trace to include in the server's log, but is not
       *            sent to the client's stderr.
     */
    public Failure(final int exitCode, final String msg, final Throwable why) {
      super(msg, why);
@@ -402,7 +442,8 @@
    /**
     * Create a new failure.
     *
     * @param msg message to also send to the client's stderr.
       * @param msg
       *            message to also send to the client's stderr.
     */
    public UnloggedFailure(final String msg) {
      this(1, msg);
@@ -411,10 +452,12 @@
    /**
     * Create a new failure.
     *
     * @param exitCode exit code to return the client, which indicates the
     *        failure status of this command. Should be between 1 and 255,
     *        inclusive.
     * @param msg message to also send to the client's stderr.
       * @param exitCode
       *            exit code to return the client, which indicates the
       *            failure status of this command. Should be between 1 and
       *            255, inclusive.
       * @param msg
       *            message to also send to the client's stderr.
     */
    public UnloggedFailure(final int exitCode, final String msg) {
      this(exitCode, msg, null);
@@ -423,15 +466,17 @@
    /**
     * Create a new failure.
     *
     * @param exitCode exit code to return the client, which indicates the
     *        failure status of this command. Should be between 1 and 255,
     *        inclusive.
     * @param msg message to also send to the client's stderr.
     * @param why stack trace to include in the server's log, but is not sent to
     *        the client's stderr.
       * @param exitCode
       *            exit code to return the client, which indicates the
       *            failure status of this command. Should be between 1 and
       *            255, inclusive.
       * @param msg
       *            message to also send to the client's stderr.
       * @param why
       *            stack trace to include in the server's log, but is not
       *            sent to the client's stderr.
     */
    public UnloggedFailure(final int exitCode, final String msg,
        final Throwable why) {
      public UnloggedFailure(final int exitCode, final String msg, final Throwable why) {
      super(exitCode, msg, why);
    }
  }