James Moger
2014-03-30 413e9b486b1a84960d4c8ddac130e87280f64c6a
Split administration commands into a plugin, enhance plugin manager
1 files added
3 files renamed
8 files modified
9 files deleted
2725 ■■■■ changed files
build.xml 42 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/GitblitManager.java 52 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/IPluginManager.java 23 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/PluginManager.java 32 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/WelcomeShell.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/commands/PluginDispatcher.java 293 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/commands/RootDispatcher.java 6 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/commands/VersionCommand.java 4 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/ConfigCommand.java 174 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/GitblitDispatcher.java 40 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/ListDispatcher.java 58 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/ProjectsDispatcher.java 94 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/RepositoriesDispatcher.java 532 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/ReviewCommand.java 89 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/TeamsDispatcher.java 507 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/TicketsDispatcher.java 157 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java 592 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/keys/BaseKeyCommand.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/transport/ssh/keys/KeysDispatcher.java 2 ●●● patch | view | raw | blame | history
src/site/setup_transport_ssh.mkd 22 ●●●●● patch | view | raw | blame | history
src/test/java/com/gitblit/tests/SshDaemonTest.java 2 ●●● patch | view | raw | blame | history
build.xml
@@ -569,14 +569,14 @@
                        <page name="using the Eclipse plugin" src="eclipse_plugin.mkd" />
                    </menu>
                    <divider />
                    <menu name="Tickets" pager="true" pagerPlacement="bottom" pagerLayout="justified">
                      <page name="overview" src="tickets_overview.mkd" />
                      <page name="using" src="tickets_using.mkd" />
                      <page name="barnum" src="tickets_barnum.mkd" />
                    <menu name="Tickets" pager="true" pagerPlacement="bottom" pagerLayout="justified">
                      <page name="overview" src="tickets_overview.mkd" />
                      <page name="using" src="tickets_using.mkd" />
                      <page name="barnum" src="tickets_barnum.mkd" />
                      <page name="setup" src="tickets_setup.mkd" />
                      <page name="replication &amp; advanced administration" src="tickets_replication.mkd" />
                    </menu>
                    <divider />
                      <page name="replication &amp; advanced administration" src="tickets_replication.mkd" />
                    </menu>
                    <divider />
                    <page name="federation" src="federation.mkd" />
                    <divider />
                    <page name="settings" src="properties.mkd" />
@@ -888,14 +888,14 @@
                            <page name="using the Eclipse plugin" src="eclipse_plugin.mkd" />
                        </menu>
                        <divider />
                        <menu name="Tickets" pager="true" pagerPlacement="bottom" pagerLayout="justified">
                            <page name="overview" src="tickets_overview.mkd" />
                            <page name="using" src="tickets_using.mkd" />
                            <page name="barnum" src="tickets_barnum.mkd" />
                            <page name="setup" src="tickets_setup.mkd" />
                        <menu name="Tickets" pager="true" pagerPlacement="bottom" pagerLayout="justified">
                            <page name="overview" src="tickets_overview.mkd" />
                            <page name="using" src="tickets_using.mkd" />
                            <page name="barnum" src="tickets_barnum.mkd" />
                            <page name="setup" src="tickets_setup.mkd" />
                            <page name="replication &amp; advanced administration" src="tickets_replication.mkd" />
                        </menu>
                        <divider />
                        </menu>
                        <divider />
                        <page name="federation" src="federation.mkd" />
                        <divider />
                        <page name="settings" src="properties.mkd" />
@@ -1046,5 +1046,19 @@
            <arg value="-DcreateChecksum=true" />
        </exec>
    </target>
    <!--
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        Install Gitblit JAR for usage as Moxie artifact
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    -->
    <target name="installMoxie" depends="compile" description="Install Gitblit JAR as a Moxie artifact">
        <local name="project.jar" />
        <property name="project.jar" value="${project.targetDirectory}/${project.artifactId}-${project.version}.jar" />
        <property name="resourceFolderPrefix" value="" />
        <mx:jar destfile="${project.jar}" includeresources="true" resourceFolderPrefix="${resourceFolderPrefix}" />
        <mx:install />
    </target>
    
</project>
src/main/java/com/gitblit/manager/GitblitManager.java
@@ -42,7 +42,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.fortsoft.pf4j.PluginClassLoader;
import ro.fortsoft.pf4j.PluginWrapper;
import ro.fortsoft.pf4j.RuntimeMode;
import com.gitblit.Constants;
import com.gitblit.Constants.AccessPermission;
@@ -1187,4 +1189,54 @@
    public PluginWrapper whichPlugin(Class<?> clazz) {
        return pluginManager.whichPlugin(clazz);
    }
    @Override
    public boolean deletePlugin(PluginWrapper wrapper) {
        return pluginManager.deletePlugin(wrapper);
    }
    @Override
    public List<PluginWrapper> getPlugins() {
        return pluginManager.getPlugins();
    }
    @Override
    public List<PluginWrapper> getResolvedPlugins() {
        return pluginManager.getResolvedPlugins();
    }
    @Override
    public List<PluginWrapper> getUnresolvedPlugins() {
        return pluginManager.getUnresolvedPlugins();
    }
    @Override
    public List<PluginWrapper> getStartedPlugins() {
        return pluginManager.getStartedPlugins();
    }
    @Override
    public void loadPlugins() {
        pluginManager.loadPlugins();
    }
    @Override
    public void startPlugins() {
        pluginManager.startPlugins();
    }
    @Override
    public void stopPlugins() {
        pluginManager.stopPlugins();
    }
    @Override
    public PluginClassLoader getPluginClassLoader(String pluginId) {
        return pluginManager.getPluginClassLoader(pluginId);
    }
    @Override
    public RuntimeMode getRuntimeMode() {
        return pluginManager.getRuntimeMode();
    }
}
src/main/java/com/gitblit/manager/IPluginManager.java
@@ -15,19 +15,10 @@
 */
package com.gitblit.manager;
import java.util.List;
import ro.fortsoft.pf4j.PluginManager;
import ro.fortsoft.pf4j.PluginWrapper;
public interface IPluginManager extends IManager {
    /**
     * Retrieves the extension for given class 'clazz'.
     *
     * @param clazz extension point class to retrieve extension for
     * @return list of extensions
     */
    public <T> List<T> getExtensions(Class<T> clazz);
public interface IPluginManager extends IManager, PluginManager {
    /**
     * Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'.
@@ -35,5 +26,13 @@
     * @param clazz extension point class to retrieve extension for
     * @return PluginWrapper that loaded the given class
     */
    public PluginWrapper whichPlugin(Class<?> clazz);
    PluginWrapper whichPlugin(Class<?> clazz);
    /**
     * Delete the plugin represented by {@link PluginWrapper}.
     *
     * @param wrapper
     * @return true if successful
     */
    boolean deletePlugin(PluginWrapper wrapper);
}
src/main/java/com/gitblit/manager/PluginManager.java
@@ -15,12 +15,16 @@
 */
package com.gitblit.manager;
import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.fortsoft.pf4j.DefaultPluginManager;
import ro.fortsoft.pf4j.PluginWrapper;
import com.gitblit.Keys;
import com.gitblit.utils.FileUtils;
/**
 * The plugin manager maintains the lifecycle of plugins. It is exposed as
@@ -30,27 +34,45 @@
 * @author David Ostrovsky
 * 
 */
public class PluginManager extends DefaultPluginManager implements
        IPluginManager {
public class PluginManager extends DefaultPluginManager implements IPluginManager {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final IRuntimeManager runtimeManager;
    public PluginManager(IRuntimeManager runtimeManager) {
        super(runtimeManager.getFileOrFolder(Keys.plugins.folder,
                "${baseFolder}/plugins"));
        super(runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins"));
        this.runtimeManager = runtimeManager;
    }
    @Override
    public PluginManager start() {
        logger.info("Plugin manager started");
        logger.info("Loading plugins...");
        loadPlugins();
        logger.info("Starting loaded plugins...");
        startPlugins();
        return this;
    }
    @Override
    public PluginManager stop() {
        logger.info("Stopping loaded plugins...");
        stopPlugins();
        return null;
    }
    @Override
    public boolean deletePlugin(PluginWrapper pw) {
        File folder = runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins");
        File pluginFolder = new File(folder, pw.getPluginPath());
        File pluginZip = new File(folder, pw.getPluginPath() + ".zip");
        if (pluginFolder.exists()) {
            FileUtils.delete(pluginFolder);
        }
        if (pluginZip.exists()) {
            FileUtils.delete(pluginZip);
        }
        return true;
    }
}
src/main/java/com/gitblit/transport/ssh/WelcomeShell.java
@@ -165,7 +165,7 @@
                msg.append(nl);
                msg.append(nl);
                msg.append(String.format("   cat ~/.ssh/id_rsa.pub | ssh -l %s -p %d %s gitblit keys add", user.username, port, hostname));
                msg.append(String.format("   cat ~/.ssh/id_rsa.pub | ssh -l %s -p %d %s keys add", user.username, port, hostname));
                msg.append(nl);
                msg.append(nl);
src/main/java/com/gitblit/transport/ssh/commands/PluginDispatcher.java
New file
@@ -0,0 +1,293 @@
/*
 * Copyright 2014 gitblit.com.
 *
 * Licensed 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 com.gitblit.transport.ssh.commands;
import java.util.ArrayList;
import java.util.List;
import org.kohsuke.args4j.Argument;
import ro.fortsoft.pf4j.PluginDependency;
import ro.fortsoft.pf4j.PluginDescriptor;
import ro.fortsoft.pf4j.PluginState;
import ro.fortsoft.pf4j.PluginWrapper;
import com.gitblit.manager.IGitblit;
import com.gitblit.models.UserModel;
import com.gitblit.utils.FlipTable;
import com.gitblit.utils.FlipTable.Borders;
/**
 * The plugin dispatcher and commands for runtime plugin management.
 *
 * @author James Moger
 *
 */
@CommandMetaData(name = "plugin", description = "Plugin management commands", admin = true)
public class PluginDispatcher extends DispatchCommand {
    @Override
    protected void setup(UserModel user) {
        register(user, ListPlugins.class);
        register(user, StartPlugin.class);
        register(user, StopPlugin.class);
        register(user, ShowPlugin.class);
        register(user, RemovePlugin.class);
        register(user, UploadPlugin.class);
    }
    @CommandMetaData(name = "list", aliases = { "ls" }, description = "List the loaded plugins")
    public static class ListPlugins extends ListCommand<PluginWrapper> {
        @Override
        protected List<PluginWrapper> getItems() throws UnloggedFailure {
            IGitblit gitblit = getContext().getGitblit();
            List<PluginWrapper> list = gitblit.getPlugins();
            return list;
        }
        @Override
        protected void asTable(List<PluginWrapper> list) {
            String[] headers;
            if (verbose) {
                String [] h = { "#", "Id", "Version", "State", "Mode", "Path", "Provider"};
                headers = h;
            } else {
                String [] h = { "#", "Id", "Version", "State", "Path"};
                headers = h;
            }
            Object[][] data = new Object[list.size()][];
            for (int i = 0; i < list.size(); i++) {
                PluginWrapper p = list.get(i);
                PluginDescriptor d = p.getDescriptor();
                if (verbose) {
                    data[i] = new Object[] { "" + (i + 1), d.getPluginId(), d.getVersion(), p.getPluginState(), p.getRuntimeMode(), p.getPluginPath(), d.getProvider() };
                } else {
                    data[i] = new Object[] { "" + (i + 1), d.getPluginId(), d.getVersion(), p.getPluginState(), p.getPluginPath() };
                }
            }
            stdout.println(FlipTable.of(headers, data, Borders.BODY_HCOLS));
        }
        @Override
        protected void asTabbed(List<PluginWrapper> list) {
            for (PluginWrapper pw : list) {
                PluginDescriptor d = pw.getDescriptor();
                if (verbose) {
                    outTabbed(d.getPluginId(), d.getVersion(), pw.getPluginState(), pw.getRuntimeMode(), pw.getPluginPath(), d.getProvider());
                } else {
                    outTabbed(d.getPluginId(), d.getVersion(), pw.getPluginState(), pw.getPluginPath());
                }
            }
        }
    }
    @CommandMetaData(name = "start", description = "Start a plugin")
    public static class StartPlugin extends SshCommand {
        @Argument(index = 0, required = true, metaVar = "ALL|<id>", usage = "the plugin to start")
        protected String plugin;
        @Override
        public void run() throws UnloggedFailure {
            IGitblit gitblit = getContext().getGitblit();
            if (plugin.equalsIgnoreCase("ALL")) {
                gitblit.startPlugins();
                stdout.println("All plugins started");
            } else {
                try {
                    int index = Integer.parseInt(plugin);
                    List<PluginWrapper> plugins = gitblit.getPlugins();
                    if (index > plugins.size()) {
                        throw new UnloggedFailure(1,  "Invalid plugin index specified!");
                    }
                    PluginWrapper pw = plugins.get(index - 1);
                    start(pw);
                } catch (NumberFormatException n) {
                    for (PluginWrapper pw : gitblit.getPlugins()) {
                        PluginDescriptor pd = pw.getDescriptor();
                        if (pd.getPluginId().equalsIgnoreCase(plugin)) {
                            start(pw);
                            break;
                        }
                    }
                }
            }
        }
        protected void start(PluginWrapper pw) throws UnloggedFailure {
            String id = pw.getDescriptor().getPluginId();
            if (pw.getPluginState() == PluginState.STARTED) {
                throw new UnloggedFailure(1, String.format("%s is already started.", id));
            }
            try {
                pw.getPlugin().start();
//                pw.setPluginState(PluginState.STARTED);
                stdout.println(String.format("%s started", id));
            } catch (Exception pe) {
                throw new UnloggedFailure(1, String.format("Failed to start %s", id), pe);
            }
        }
    }
    @CommandMetaData(name = "stop", description = "Stop a plugin")
    public static class StopPlugin extends SshCommand {
        @Argument(index = 0, required = true, metaVar = "ALL|<id>", usage = "the plugin to stop")
        protected String plugin;
        @Override
        public void run() throws UnloggedFailure {
            IGitblit gitblit = getContext().getGitblit();
            if (plugin.equalsIgnoreCase("ALL")) {
                gitblit.stopPlugins();
                stdout.println("All plugins stopped");
            } else {
                try {
                int index = Integer.parseInt(plugin);
                List<PluginWrapper> plugins = gitblit.getPlugins();
                if (index > plugins.size()) {
                    throw new UnloggedFailure(1,  "Invalid plugin index specified!");
                }
                PluginWrapper pw = plugins.get(index - 1);
                stop(pw);
            } catch (NumberFormatException n) {
                for (PluginWrapper pw : gitblit.getPlugins()) {
                    PluginDescriptor pd = pw.getDescriptor();
                    if (pd.getPluginId().equalsIgnoreCase(plugin)) {
                        stop(pw);
                        break;
                    }
                }
            }
            }
        }
        protected void stop(PluginWrapper pw) throws UnloggedFailure {
            String id = pw.getDescriptor().getPluginId();
            if (pw.getPluginState() == PluginState.STOPPED) {
                throw new UnloggedFailure(1, String.format("%s is already stopped.", id));
            }
            try {
                pw.getPlugin().stop();
//                pw.setPluginState(PluginState.STOPPED);
                stdout.println(String.format("%s stopped", id));
            } catch (Exception pe) {
                throw new UnloggedFailure(1, String.format("Failed to stop %s", id), pe);
            }
        }
    }
    @CommandMetaData(name = "show", description = "Show the details of a plugin")
    public static class ShowPlugin extends SshCommand {
        @Argument(index = 0, required = true, metaVar = "<id>", usage = "the plugin to stop")
        protected int index;
        @Override
        public void run() throws UnloggedFailure {
            IGitblit gitblit = getContext().getGitblit();
            List<PluginWrapper> plugins = gitblit.getPlugins();
            if (index > plugins.size()) {
                throw new UnloggedFailure(1, "Invalid plugin index specified!");
            }
            PluginWrapper pw = plugins.get(index - 1);
            PluginDescriptor d = pw.getDescriptor();
            // FIELDS
            StringBuilder sb = new StringBuilder();
            sb.append("Version  : ").append(d.getVersion()).append('\n');
            sb.append("Provider : ").append(d.getProvider()).append('\n');
            sb.append("Path     : ").append(pw.getPluginPath()).append('\n');
            sb.append("State    : ").append(pw.getPluginState()).append('\n');
            final String fields = sb.toString();
            // TODO EXTENSIONS
            sb.setLength(0);
            List<String> exts = new ArrayList<String>();
            String extensions;
            if (exts.isEmpty()) {
                extensions = FlipTable.EMPTY;
            } else {
                String[] headers = { "Id", "Version" };
                Object[][] data = new Object[exts.size()][];
                for (int i = 0; i < exts.size(); i++) {
                    String ext = exts.get(i);
                    data[0] = new Object[] { ext.toString(), ext.toString() };
                }
                extensions = FlipTable.of(headers, data, Borders.COLS);
            }
            // DEPENDENCIES
            sb.setLength(0);
            List<PluginDependency> deps = d.getDependencies();
            String dependencies;
            if (deps.isEmpty()) {
                dependencies = FlipTable.EMPTY;
            } else {
                String[] headers = { "Id", "Version" };
                Object[][] data = new Object[deps.size()][];
                for (int i = 0; i < deps.size(); i++) {
                    PluginDependency dep = deps.get(i);
                    data[0] = new Object[] { dep.getPluginId(), dep.getPluginVersion() };
                }
                dependencies = FlipTable.of(headers, data, Borders.COLS);
            }
            String[] headers = { d.getPluginId() };
            Object[][] data = new Object[5][];
            data[0] = new Object[] { fields };
            data[1] = new Object[] { "EXTENSIONS" };
            data[2] = new Object[] { extensions };
            data[3] = new Object[] { "DEPENDENCIES" };
            data[4] = new Object[] { dependencies };
            stdout.println(FlipTable.of(headers, data));
        }
    }
    @CommandMetaData(name = "remove", aliases= { "rm", "del" }, description = "Remove a plugin", hidden = true)
    public static class RemovePlugin extends SshCommand {
        @Argument(index = 0, required = true, metaVar = "<id>", usage = "the plugin to stop")
        protected int index;
        @Override
        public void run() throws UnloggedFailure {
            IGitblit gitblit = getContext().getGitblit();
            List<PluginWrapper> plugins = gitblit.getPlugins();
            if (index > plugins.size()) {
                throw new UnloggedFailure(1, "Invalid plugin index specified!");
            }
            PluginWrapper pw = plugins.get(index - 1);
            PluginDescriptor d = pw.getDescriptor();
            if (gitblit.deletePlugin(pw)) {
                stdout.println(String.format("Deleted %s %s", d.getPluginId(), d.getVersion()));
            } else {
                throw new UnloggedFailure(1,  String.format("Failed to delete %s %s", d.getPluginId(), d.getVersion()));
            }
        }
    }
    @CommandMetaData(name = "receive", aliases= { "upload" }, description = "Upload a plugin to the server", hidden = true)
    public static class UploadPlugin extends SshCommand {
        @Override
        public void run() throws UnloggedFailure {
        }
    }
}
src/main/java/com/gitblit/transport/ssh/commands/RootDispatcher.java
@@ -24,7 +24,7 @@
import com.gitblit.models.UserModel;
import com.gitblit.transport.ssh.SshDaemonClient;
import com.gitblit.transport.ssh.git.GitDispatcher;
import com.gitblit.transport.ssh.gitblit.GitblitDispatcher;
import com.gitblit.transport.ssh.keys.KeysDispatcher;
/**
 * The root dispatcher is the dispatch command that handles registering all
@@ -41,8 +41,10 @@
        setContext(new SshCommandContext(gitblit, client, cmdLine));
        UserModel user = client.getUser();
        register(user, GitblitDispatcher.class);
        register(user, VersionCommand.class);
        register(user, GitDispatcher.class);
        register(user, KeysDispatcher.class);
        register(user, PluginDispatcher.class);
        List<DispatchCommand> exts = gitblit.getExtensions(DispatchCommand.class);
        for (DispatchCommand ext : exts) {
src/main/java/com/gitblit/transport/ssh/commands/VersionCommand.java
File was renamed from src/main/java/com/gitblit/transport/ssh/gitblit/VersionCommand.java
@@ -14,11 +14,9 @@
 * limitations under the License.
 */
package com.gitblit.transport.ssh.gitblit;
package com.gitblit.transport.ssh.commands;
import com.gitblit.Constants;
import com.gitblit.transport.ssh.commands.CommandMetaData;
import com.gitblit.transport.ssh.commands.SshCommand;
@CommandMetaData(name="version", description = "Display the Gitblit version")
public class VersionCommand extends SshCommand {
src/main/java/com/gitblit/transport/ssh/gitblit/ConfigCommand.java
File was deleted
src/main/java/com/gitblit/transport/ssh/gitblit/GitblitDispatcher.java
File was deleted
src/main/java/com/gitblit/transport/ssh/gitblit/ListDispatcher.java
File was deleted
src/main/java/com/gitblit/transport/ssh/gitblit/ProjectsDispatcher.java
File was deleted
src/main/java/com/gitblit/transport/ssh/gitblit/RepositoriesDispatcher.java
File was deleted
src/main/java/com/gitblit/transport/ssh/gitblit/ReviewCommand.java
File was deleted
src/main/java/com/gitblit/transport/ssh/gitblit/TeamsDispatcher.java
File was deleted
src/main/java/com/gitblit/transport/ssh/gitblit/TicketsDispatcher.java
File was deleted
src/main/java/com/gitblit/transport/ssh/gitblit/UsersDispatcher.java
File was deleted
src/main/java/com/gitblit/transport/ssh/keys/BaseKeyCommand.java
File was renamed from src/main/java/com/gitblit/transport/ssh/gitblit/BaseKeyCommand.java
@@ -13,7 +13,7 @@
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.gitblit.transport.ssh.gitblit;
package com.gitblit.transport.ssh.keys;
import java.io.BufferedReader;
import java.io.IOException;
src/main/java/com/gitblit/transport/ssh/keys/KeysDispatcher.java
File was renamed from src/main/java/com/gitblit/transport/ssh/gitblit/KeysDispatcher.java
@@ -13,7 +13,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitblit.transport.ssh.gitblit;
package com.gitblit.transport.ssh.keys;
import java.io.IOException;
import java.util.ArrayList;
src/site/setup_transport_ssh.mkd
@@ -23,8 +23,8 @@
Then you can upload your *public* key right from the command-line.
    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add
    cat c:\<userfolder>\.ssh\id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add
    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> keys add
    cat c:\<userfolder>\.ssh\id_rsa.pub | ssh -l <username> -p 29418 <hostname> keys add
**NOTE:** It is important to note that *ssh-keygen* generates a public/private keypair (e.g. id_rsa and id_rsa.pub).  You want to upload the *public* key, which is denoted by the *.pub* file extension.
@@ -36,7 +36,7 @@
Typing the following command syntax all the time gets to be rather tedious.
    ssh -l <username> -p 29418 <hostname> gitblit version
    ssh -l <username> -p 29418 <hostname>
You can define an alias for your server which will reduce your command syntax to something like this.
@@ -54,29 +54,33 @@
Gitblit supports SSH command plugins and provides several commands out-of-the-box.
#### gitblit
#### keys
The *gitblit* command has many subcommands for interacting with Gitblit.
The *keys* command dispatcher allows you to manage your public ssh keys.  You can list keys, add keys, remove keys, and identify the key in-use for the active session.
##### keys add
Add an SSH public key to your account.  This command accepts a public key piped to stdin.
    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> gitblit keys add
    cat ~/.ssh/id_rsa.pub | ssh -l <username> -p 29418 <hostname> keys add
##### keys list
Show the SSH public keys you have added to your account.
    ssh -l <username> -p 29418 <hostname> gitblit keys list
    ssh -l <username> -p 29418 <hostname> keys list
##### keys remove
Remove an SSH public key from your account.  This command accepts several input values, the most useful one is an index number which matches the index number displayed in the `list` command.
    ssh -l <username> -p 29418 <hostname> gitblit keys remove 2
    ssh -l <username> -p 29418 <hostname> keys remove 2
You can also remove all your public keys from your account.
    ssh -l <username> -p 29418 <hostname> gitblit keys remove ALL
    ssh -l <username> -p 29418 <hostname> keys remove ALL
### SSH Command Plugins
Gitblit supports loading custom SSH command plugins.
src/test/java/com/gitblit/tests/SshDaemonTest.java
@@ -93,7 +93,7 @@
        pair.getPublic().getEncoded();
        assertTrue(session.authPublicKey("admin", pair).await().isSuccess());
        ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_EXEC, "gitblit version");
        ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_EXEC, "version");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Writer w = new OutputStreamWriter(baos);
        w.close();