// Copyright 2009 Google Inc. // // 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.google.enterprise.connector.manager; import com.google.enterprise.connector.common.AbstractCommandLineApp; import com.google.enterprise.connector.encryptpassword.EncryptPassword; import com.google.enterprise.connector.importexport.DumpConnectors; import com.google.enterprise.connector.importexport.ImportExport; import com.google.enterprise.connector.persist.MigrateStore; import com.google.enterprise.connector.servlet.ServletUtil; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import java.util.TreeMap; /** * The Connector Manager Command Processor. This is thean entry point * for command line access to Connector Manager features. It is * typically invoked from {@link ManagerMain} when that Jar Main class * is called with command line arguments, while avoiding ManagerMain direct * dependence on external libraries (specifically Apache Commons CLI). * <p> * There are shell scripts, {@code Manager} and {@code Manager.bat}, that * may be used to more conveniently invoke the command processor. * <p> * <pre> * Usage: Manager [-?] [-v] [command] [options] [arguments] * -?, --help Display the set of available commands * -v, --version Display the Connector Manager version * * To get help for a command, specify the command along with -? or --help * For instance: * Manager MigrateStore --help * </pre> */ public class CommandDispatcher extends AbstractCommandLineApp { // TODO: The command implementations should really register themselves. private static TreeMap<String, Command> commands; { commands = new TreeMap<String, Command>(); addCommand(new DumpConnectors(), false); addCommand(new EncryptPassword(), false); addCommand(new MigrateStore(), false); addCommand(new ImportExport(), true); } /** * Adds a new command line app to the set of supported commands. * * @param app the command line application. * @param hidden if true, do not list the command in help. */ private static void addCommand(AbstractCommandLineApp app, boolean hidden) { Command command = new Command(app, hidden); commands.put(command.name.toLowerCase(), command); } /** * Represents a Command Line application. */ private static class Command { String name; String description; boolean hidden; Class<? extends AbstractCommandLineApp> appClass; public Command(AbstractCommandLineApp app, boolean hidden) { this.name = app.getName().trim(); this.appClass = app.getClass(); this.hidden = hidden; if (!hidden) { this.description = app.getDescription(); } } } private final String[] originalArgs; /** * Construct a new CommandDispatcher, preserving the original args. */ public CommandDispatcher(String[] args) { this.originalArgs = args; } @Override public String getName() { return ServletUtil.MANAGER_NAME; } @Override public String getDescription() { return ServletUtil.MANAGER_NAME + " command processor."; } @Override public String getCommandLineSyntax() { return "Manager [-?] [-v]"; } @Override public String getUsageHeader() { return "or Manager [command] [options] [arguments]"; } @Override protected void printUsage() { super.printUsage(); System.err.print(getAdditionalUsage()); } // Doesn't override getUsageFooter() because HelpFormatter // strips the leading whitespace from my lines. private String getAdditionalUsage() { StringBuilder builder = new StringBuilder(); // Figure out the longest command name for formatting output. int longestCommand = 0; for (Command command : commands.values()) { if (command.name.length() > longestCommand) { longestCommand = command.name.length(); } } // Now add the descriptions of the available commands. builder.append("Available commands:").append(NL); for (Command command : commands.values()) { addDescription(command, builder, longestCommand); } builder.append(NL); builder.append("To get help for a command, specify the command along"); builder.append(" with -? or --help").append(NL); builder.append("For instance:").append(NL); builder.append(" Manager MigrateStore --help").append(NL).append(NL); return builder.toString(); } private void addDescription(Command command, StringBuilder builder, int longestCommand) { if (!command.hidden) { builder.append(" ").append(command.name); for (int i = command.name.length(); i < longestCommand; i++) { builder.append(' '); } builder.append(" ").append(command.description).append(NL); } } @Override public CommandLine parseArgs(String[] args) { try { // Stop parsing at first non-option, so we don't accidently try // to interperet options intended for the commands themselves. commandLine = new PosixParser().parse(getOptions(), args, true); return commandLine; } catch (ParseException pe) { printUsageAndExit(-1); } return null; } @Override public void run(CommandLine commandLine) throws Exception { String[] args = commandLine.getArgs(); if (args.length > 0) { Command command = commands.get(args[0].toLowerCase()); if (command != null) { AbstractCommandLineApp app = command.appClass.newInstance(); app.run(app.parseArgs(shift(originalArgs))); return; } printUsageAndExit(-1); } if (commandLine.hasOption(HELP_OPTION.getLongOpt())) { printUsageAndExit(0); } // The default behavior is to display the product version. printVersion(); } /** * Returns a subarray of the supplied array. This performs the equivalent of * the 'shift' shell command. * * @param args An array of String arguments * @return args[1..n] subarray */ private static String[] shift(String[] args) { String[] shifted = new String[args.length - 1]; System.arraycopy(args, 1, shifted, 0, shifted.length); return shifted; } /** * Proper main() in case this is called directly. */ public static void main(String[] args) throws Exception { CommandDispatcher app = new CommandDispatcher(args); app.run(app.parseArgs(args)); System.exit(0); } }