James Moger
2014-03-18 23c416f30f4a1e69e76b70d71f6a9a7da4a020f1
Hook-up comprensive command cleanup (destroy)
6 files modified
116 ■■■■ changed files
src/main/java/com/gitblit/transport/ssh/SshDaemon.java 1 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java 10 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/commands/DispatchCommand.java 28 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/commands/SshCommandFactory.java 58 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/git/BaseGitCommand.java 10 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/git/GitDispatcher.java 9 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/SshDaemon.java
@@ -166,6 +166,7 @@
            run.set(false);
            try {
                ((SshCommandFactory) sshd.getCommandFactory()).stop();
                sshd.stop();
            } catch (InterruptedException e) {
                log.error("SSH Daemon stop interrupted", e);
src/main/java/com/gitblit/transport/ssh/commands/BaseCommand.java
@@ -48,6 +48,14 @@
    private static final Logger log = LoggerFactory.getLogger(BaseCommand.class);
    private static final int PRIVATE_STATUS = 1 << 30;
    public final static int STATUS_CANCEL = PRIVATE_STATUS | 1;
    public final static int STATUS_NOT_FOUND = PRIVATE_STATUS | 2;
    public final static int STATUS_NOT_ADMIN = PRIVATE_STATUS | 3;
    protected InputStream in;
    protected OutputStream out;
@@ -86,6 +94,8 @@
    @Override
    public void destroy() {
        log.debug("destroying " + getClass().getName());
        session = null;
        ctx = null;
    }
src/main/java/com/gitblit/transport/ssh/commands/DispatchCommand.java
@@ -48,20 +48,34 @@
    private List<String> args = new ArrayList<String>();
    private final Set<Class<? extends BaseCommand>> commands;
    private final Map<String, DispatchCommand> dispatchers;
    private final List<BaseCommand> instantiated;
    private Map<String, Class<? extends BaseCommand>> map;
    private Map<String, BaseCommand> dispatchers;
    protected DispatchCommand() {
        commands = new HashSet<Class<? extends BaseCommand>>();
        dispatchers = Maps.newHashMap();
        instantiated = new ArrayList<BaseCommand>();
    }
    @Override
    public void destroy() {
        super.destroy();
        commands.clear();
        map = null;
        for (BaseCommand command : instantiated) {
            command.destroy();
        }
        for (DispatchCommand dispatcher : dispatchers.values()) {
            dispatcher.destroy();
        }
    }
    protected void registerDispatcher(UserModel user, Class<? extends DispatchCommand> cmd) {
        if (!cmd.isAnnotationPresent(CommandMetaData.class)) {
            throw new RuntimeException(MessageFormat.format("{0} must be annotated with {1}!", cmd.getName(),
                    CommandMetaData.class.getName()));
        }
        if (dispatchers == null) {
            dispatchers = Maps.newHashMap();
        }
        CommandMetaData meta = cmd.getAnnotation(CommandMetaData.class);
@@ -108,10 +122,9 @@
                CommandMetaData meta = cmd.getAnnotation(CommandMetaData.class);
                map.put(meta.name(), cmd);
            }
            if (dispatchers != null) {
                for (Map.Entry<String, BaseCommand> entry : dispatchers.entrySet()) {
            for (Map.Entry<String, DispatchCommand> entry : dispatchers.entrySet()) {
                    map.put(entry.getKey(), entry.getValue().getClass());
                }
            }
        }
        return map;
@@ -163,6 +176,7 @@
        BaseCommand cmd = null;
        try {
            cmd = c.newInstance();
            instantiated.add(cmd);
        } catch (Exception e) {
            throw new UnloggedFailure(1, MessageFormat.format("Failed to instantiate {0} command", commandName));
        }
src/main/java/com/gitblit/transport/ssh/commands/SshCommandFactory.java
@@ -20,6 +20,8 @@
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -39,6 +41,7 @@
import com.gitblit.utils.IdGenerator;
import com.gitblit.utils.WorkQueue;
import com.google.common.util.concurrent.Atomics;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/**
 *
@@ -50,6 +53,7 @@
    private final IGitblit gitblit;
    private final ScheduledExecutorService startExecutor;
    private final ExecutorService destroyExecutor;
    public SshCommandFactory(IGitblit gitblit, IdGenerator idGenerator) {
        this.gitblit = gitblit;
@@ -57,6 +61,15 @@
        int threads = 2;// cfg.getInt("sshd","commandStartThreads", 2);
        WorkQueue workQueue = new WorkQueue(idGenerator);
        startExecutor = workQueue.createQueue(threads, "SshCommandStart");
        destroyExecutor = Executors.newSingleThreadExecutor(
                new ThreadFactoryBuilder()
                    .setNameFormat("SshCommandDestroy-%s")
                    .setDaemon(true)
                    .build());
    }
    public void stop() {
        destroyExecutor.shutdownNow();
    }
    public RootDispatcher createRootDispatcher(SshDaemonClient client, String commandLine) {
@@ -166,27 +179,23 @@
        }
        private int translateExit(final int rc) {
            return rc;
            //
            // switch (rc) {
            // case BaseCommand.STATUS_NOT_ADMIN:
            // return 1;
            //
            // case BaseCommand.STATUS_CANCEL:
            // return 15 /* SIGKILL */;
            //
            // case BaseCommand.STATUS_NOT_FOUND:
            // return 127 /* POSIX not found */;
            //
            // default:
            // return rc;
            // }
            switch (rc) {
            case BaseCommand.STATUS_NOT_ADMIN:
                return 1;
            case BaseCommand.STATUS_CANCEL:
                return 15 /* SIGKILL */;
            case BaseCommand.STATUS_NOT_FOUND:
                return 127 /* POSIX not found */;
            default:
                return rc;
            }
        }
        private void log(final int rc) {
            if (logged.compareAndSet(false, true)) {
                // log.onExecute(cmd, rc);
                logger.info("onExecute: {} exits with: {}", cmd.getClass().getSimpleName(), rc);
            }
        }
@@ -196,27 +205,22 @@
            Future<?> future = task.getAndSet(null);
            if (future != null) {
                future.cancel(true);
                // destroyExecutor.execute(new Runnable() {
                // @Override
                // public void run() {
                // onDestroy();
                // }
                // });
                destroyExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        onDestroy();
                    }
                });
            }
        }
        @SuppressWarnings("unused")
        private void onDestroy() {
            synchronized (this) {
                if (cmd != null) {
                    // final Context old = sshScope.set(ctx);
                    try {
                        cmd.destroy();
                        // log(BaseCommand.STATUS_CANCEL);
                    } finally {
                        // ctx = null;
                        cmd = null;
                        // sshScope.set(old);
                    }
                }
            }
src/main/java/com/gitblit/transport/ssh/git/BaseGitCommand.java
@@ -44,6 +44,16 @@
    protected Repository repo;
    @Override
    public void destroy() {
        super.destroy();
        repositoryResolver = null;
        receivePackFactory = null;
        uploadPackFactory = null;
        repo = null;
    }
    @Override
    public void start(final Environment env) {
        startThread(new RepositoryCommandRunnable() {
            @Override
src/main/java/com/gitblit/transport/ssh/git/GitDispatcher.java
@@ -44,6 +44,15 @@
    }
    @Override
    public void destroy() {
        super.destroy();
        repositoryResolver = null;
        receivePackFactory = null;
        uploadPackFactory = null;
    }
    @Override
    protected void registerCommands(UserModel user) {
        registerCommand(user, Upload.class);
        registerCommand(user, Receive.class);