org.jumpmind.symmetric.SymmetricAdmin.java Source code

Java tutorial

Introduction

Here is the source code for org.jumpmind.symmetric.SymmetricAdmin.java

Source

/**
 * Licensed to JumpMind Inc under one or more contributor
 * license agreements.  See the NOTICE file distributed
 * with this work for additional information regarding
 * copyright ownership.  JumpMind Inc licenses this file
 * to you under the GNU General Public License, version 3.0 (GPLv3)
 * (the "License"); you may not use this file except in compliance
 * with the License.
 *
 * You should have received a copy of the GNU General Public License,
 * version 3.0 (GPLv3) along with this library; if not, see
 * <http://www.gnu.org/licenses/>.
 *
 * 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.jumpmind.symmetric;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.h2.util.StringUtils;
import org.jumpmind.db.model.Table;
import org.jumpmind.security.ISecurityService;
import org.jumpmind.security.SecurityConstants;
import org.jumpmind.symmetric.model.Node;
import org.jumpmind.symmetric.service.IDataExtractorService;
import org.jumpmind.symmetric.service.IDataLoaderService;
import org.jumpmind.symmetric.service.IDataService;
import org.jumpmind.symmetric.service.IPurgeService;
import org.jumpmind.symmetric.service.IRegistrationService;
import org.jumpmind.symmetric.service.ITriggerRouterService;
import org.jumpmind.util.AppUtils;
import org.jumpmind.util.JarBuilder;

/**
 * Perform administration tasks with SymmetricDS.
 */
public class SymmetricAdmin extends AbstractCommandLauncher {

    @SuppressWarnings("unused")
    private static final Log log = LogFactory.getLog(SymmetricAdmin.class);

    private static final String CMD_RELOAD_NODE = "reload-node";

    private static final String CMD_RELOAD_TABLE = "reload-table";

    private static final String CMD_EXPORT_BATCH = "export-batch";

    private static final String CMD_IMPORT_BATCH = "import-batch";

    private static final String CMD_RUN_PURGE = "run-purge";

    private static final String CMD_ENCRYPT_TEXT = "encrypt-text";

    private static final String CMD_CREATE_WAR = "create-war";

    private static final String CMD_CREATE_SYM_TABLES = "create-sym-tables";

    private static final String CMD_EXPORT_SYM_TABLES = "export-sym-tables";

    private static final String CMD_OPEN_REGISTRATION = "open-registration";

    private static final String CMD_SYNC_TRIGGERS = "sync-triggers";

    private static final String CMD_DROP_TRIGGERS = "drop-triggers";

    private static final String CMD_EXPORT_PROPERTIES = "export-properties";

    private static final String CMD_UNINSTALL = "uninstall";

    private static final String CMD_SEND_SQL = "send-sql";

    private static final String CMD_SEND_SCRIPT = "send-script";

    private static final String CMD_SEND_SCHEMA = "send-schema";

    private static final String[] NO_ENGINE_REQUIRED = { CMD_EXPORT_PROPERTIES };

    private static final String OPTION_NODE = "node";

    private static final String OPTION_CATALOG = "catalog";

    private static final String OPTION_SCHEMA = "schema";

    private static final String OPTION_WHERE = "where";

    private static final String OPTION_FORCE = "force";

    private static final String OPTION_OUT = "out";

    private static final String OPTION_NODE_GROUP = "node-group";

    private static final String OPTION_REVERSE = "reverse";

    private static final int WIDTH = 80;

    private static final int PAD = 3;

    public SymmetricAdmin(String app, String argSyntax, String messageKeyPrefix) {
        super(app, argSyntax, messageKeyPrefix);
    }

    public static void main(String[] args) throws Exception {
        new SymmetricAdmin("symadmin", "<subcommand> [options] [args]", "SymAdmin.Option.").execute(args);
    }

    @Override
    protected boolean printHelpIfNoOptionsAreProvided() {
        return true;
    }

    @Override
    protected boolean requiresPropertiesFile() {
        return true;
    }

    @Override
    protected void printHelp(CommandLine line, Options options) {
        String[] args = line.getArgs();
        if (args.length > 1 && args[0].equals(HELP)) {
            printHelpCommand(line);
        } else {
            System.out.println(app + " version " + Version.version());
            System.out.println("Perform administration tasks with SymmetricDS.\n");
            System.out.println("Usage: symadmin <subcommand> --engine [engine.name] [options] [args]");
            System.out.println("       symadmin <subcommand> --properties [properties file] [options] [args]");
            System.out.println("\nType 'symadmin help <subcommand>' for help on a specific subcommand.\n");
            System.out.println("Available subcommands:");
            PrintWriter pw = new PrintWriter(System.out);
            printHelpLine(pw, CMD_OPEN_REGISTRATION);
            printHelpLine(pw, CMD_RELOAD_NODE);
            printHelpLine(pw, CMD_RELOAD_TABLE);
            printHelpLine(pw, CMD_EXPORT_BATCH);
            printHelpLine(pw, CMD_IMPORT_BATCH);
            printHelpLine(pw, CMD_RUN_PURGE);
            printHelpLine(pw, CMD_ENCRYPT_TEXT);
            printHelpLine(pw, CMD_CREATE_WAR);
            printHelpLine(pw, CMD_CREATE_SYM_TABLES);
            printHelpLine(pw, CMD_EXPORT_SYM_TABLES);
            printHelpLine(pw, CMD_SYNC_TRIGGERS);
            printHelpLine(pw, CMD_DROP_TRIGGERS);
            printHelpLine(pw, CMD_EXPORT_PROPERTIES);
            printHelpLine(pw, CMD_SEND_SQL);
            printHelpLine(pw, CMD_SEND_SCHEMA);
            printHelpLine(pw, CMD_SEND_SCRIPT);
            printHelpLine(pw, CMD_UNINSTALL);
            pw.flush();
        }
    }

    private void printHelpLine(PrintWriter pw, String cmd) {
        String text = StringUtils.pad("   " + cmd, 23, " ", true) + Message.get("SymAdmin.Cmd." + cmd);
        new HelpFormatter().printWrapped(pw, 79, 25, text);
    }

    private void printHelpCommand(CommandLine line) {
        String[] args = line.getArgs();
        if (args.length > 1) {
            String cmd = args[1];
            HelpFormatter format = new HelpFormatter();
            PrintWriter writer = new PrintWriter(System.out);
            Options options = new Options();

            if (!Message.containsKey("SymAdmin.Usage." + cmd)) {
                System.err.println("ERROR: no help text for subcommand '" + cmd + "' was found.");
                System.err.println("For a list of subcommands, use " + app + " --" + HELP + "\n");
                return;
            }
            format.printWrapped(writer, WIDTH,
                    "Usage: " + app + " " + cmd + " " + Message.get("SymAdmin.Usage." + cmd) + "\n");
            format.printWrapped(writer, WIDTH, Message.get("SymAdmin.Help." + cmd));

            if (cmd.equals(CMD_SEND_SQL) || cmd.equals(CMD_SEND_SCHEMA) || cmd.equals(CMD_RELOAD_TABLE)
                    || cmd.equals(CMD_SEND_SCRIPT)) {
                addOption(options, "n", OPTION_NODE, true);
                addOption(options, "g", OPTION_NODE_GROUP, true);
            }
            if (cmd.equals(CMD_RELOAD_TABLE)) {
                addOption(options, "c", OPTION_CATALOG, true);
                addOption(options, "s", OPTION_SCHEMA, true);
                addOption(options, "w", OPTION_WHERE, true);
            }
            if (cmd.equals(CMD_SYNC_TRIGGERS)) {
                addOption(options, "o", OPTION_OUT, false);
                addOption(options, "f", OPTION_FORCE, false);
            }
            if (cmd.equals(CMD_RELOAD_NODE)) {
                addOption(options, "r", OPTION_REVERSE, false);
            }

            if (options.getOptions().size() > 0) {
                format.printWrapped(writer, WIDTH, "\nOptions:");
                format.printOptions(writer, WIDTH, options, PAD, PAD);
            }

            if (!ArrayUtils.contains(NO_ENGINE_REQUIRED, cmd)) {
                format.printWrapped(writer, WIDTH, "\nEngine options:");
                options = new Options();
                super.buildOptions(options);
                format.printOptions(writer, WIDTH, options, PAD, PAD);

                format.printWrapped(writer, WIDTH, "\nCrypto options:");
                options = new Options();
                buildCryptoOptions(options);
                format.printOptions(writer, WIDTH, options, PAD, PAD);
            }
            writer.flush();
        }
    }

    @Override
    protected void buildOptions(Options options) {
        super.buildOptions(options);
        addOption(options, "n", OPTION_NODE, true);
        addOption(options, "g", OPTION_NODE_GROUP, true);
        addOption(options, "c", OPTION_CATALOG, true);
        addOption(options, "s", OPTION_SCHEMA, true);
        addOption(options, "w", OPTION_WHERE, true);
        addOption(options, "f", OPTION_FORCE, false);
        addOption(options, "o", OPTION_OUT, true);
        addOption(options, "r", OPTION_REVERSE, false);
        buildCryptoOptions(options);
    }

    @SuppressWarnings("unchecked")
    @Override
    protected boolean executeWithOptions(CommandLine line) throws Exception {
        List<String> args = line.getArgList();
        String cmd = args.remove(0);
        configureCrypto(line);

        if (cmd.equals(CMD_EXPORT_PROPERTIES)) {
            exportDefaultProperties(line, args);
            return true;
        } else if (cmd.equals(CMD_CREATE_WAR)) {
            generateWar(line, args);
            return true;
        } else if (cmd.equals(CMD_EXPORT_SYM_TABLES)) {
            exportSymTables(line, args);
            return true;
        } else if (cmd.equals(CMD_RUN_PURGE)) {
            runPurge(line, args);
            return true;
        } else if (cmd.equals(CMD_OPEN_REGISTRATION)) {
            openRegistration(line, args);
            return true;
        } else if (cmd.equals(CMD_RELOAD_NODE)) {
            reloadNode(line, args);
            return true;
        } else if (cmd.equals(CMD_EXPORT_BATCH)) {
            exportBatch(line, args);
            return true;
        } else if (cmd.equals(CMD_SYNC_TRIGGERS)) {
            syncTrigger(line, args);
            return true;
        } else if (cmd.equals(CMD_DROP_TRIGGERS)) {
            dropTrigger(line, args);
            return true;
        } else if (cmd.equals(CMD_CREATE_SYM_TABLES)) {
            createSymTables();
            return true;
        } else if (cmd.equals(CMD_IMPORT_BATCH)) {
            importBatch(line, args);
            return true;
        } else if (cmd.equals(CMD_ENCRYPT_TEXT)) {
            encryptText(line, args);
            return true;
        } else if (cmd.equals(CMD_SEND_SQL)) {
            sendSql(line, args);
            return true;
        } else if (cmd.equals(CMD_UNINSTALL)) {
            uninstall(line, args);
            return true;
        } else if (cmd.equals(CMD_RELOAD_TABLE)) {
            reloadTable(line, args);
            return true;
        } else if (cmd.equals(CMD_SEND_SCHEMA)) {
            sendSchema(line, args);
            return true;
        } else if (cmd.equals(CMD_SEND_SCRIPT)) {
            sendScript(line, args);
            return true;
        } else {
            System.err.println("ERROR: no subcommand '" + cmd + "' was found.");
            System.err.println("For a list of subcommands, use " + app + " --" + HELP + "\n");
        }

        return false;
    }

    private String popArg(List<String> args, String argName) {
        if (args.size() == 0) {
            System.out.println("ERROR: Expected argument for: " + argName);
            System.exit(1);
        }
        return args.remove(0);
    }

    private void runPurge(CommandLine line, List<String> args) {
        IPurgeService purgeService = getSymmetricEngine().getPurgeService();
        boolean all = args.contains("all") || args.size() == 0;
        if (args.contains("outgoing") || all) {
            purgeService.purgeOutgoing(true);
        }
        if (args.contains("incoming") || all) {
            purgeService.purgeIncoming(true);
        }
    }

    private void exportBatch(CommandLine line, List<String> args) throws Exception {
        IDataExtractorService dataExtractorService = getSymmetricEngine().getDataExtractorService();
        String nodeId = popArg(args, "Node ID");
        String batchId = popArg(args, "Batch ID");
        OutputStreamWriter writer = getWriter(args);
        dataExtractorService.extractBatchRange(writer, nodeId, Long.valueOf(batchId), Long.valueOf(batchId));
        writer.close();
    }

    private void importBatch(CommandLine line, List<String> args) throws Exception {
        IDataLoaderService service = getSymmetricEngine().getDataLoaderService();
        InputStream in = null;
        if (args.size() == 0) {
            in = System.in;
        } else {
            in = new FileInputStream(args.get(0));
        }
        service.loadDataFromPush(getSymmetricEngine().getNodeService().findIdentity(), in, System.out);
        System.out.flush();
        in.close();
    }

    private void encryptText(CommandLine line, List<String> args) {
        String plainText = popArg(args, "Text");
        ISecurityService service = getSymmetricEngine().getSecurityService();
        System.out.println(SecurityConstants.PREFIX_ENC + service.encrypt(plainText));
    }

    private void openRegistration(CommandLine line, List<String> args) {
        String nodeGroupId = popArg(args, "Node Group ID");
        String externalId = popArg(args, "External ID");

        IRegistrationService registrationService = getSymmetricEngine().getRegistrationService();
        registrationService.openRegistration(nodeGroupId, externalId);

        System.out.println(String.format("Opened registration for node group of '%s' external ID of '%s'",
                nodeGroupId, externalId));
    }

    private void reloadNode(CommandLine line, List<String> args) {
        String nodeId = popArg(args, "Node ID");
        boolean reverse = line.hasOption(OPTION_REVERSE);
        IDataService dataService = getSymmetricEngine().getDataService();
        String message = dataService.reloadNode(nodeId, reverse, "symadmin");
        System.out.println(message);
    }

    private void syncTrigger(CommandLine line, List<String> args) throws IOException {
        boolean genAlways = line.hasOption(OPTION_FORCE);
        String filename = line.getOptionValue(OPTION_OUT);
        String catalogName = line.getOptionValue(OPTION_CATALOG);
        String schemaName = line.getOptionValue(OPTION_SCHEMA);
        File file = null;
        if (filename != null) {
            file = new File(filename);
            if (file.getParentFile() != null) {
                file.getParentFile().mkdirs();
            }
        }
        ITriggerRouterService triggerService = getSymmetricEngine().getTriggerRouterService();
        StringBuilder sqlBuffer = new StringBuilder();
        if (args.size() == 0) {
            triggerService.syncTriggers(sqlBuffer, genAlways);
        } else {
            for (String tablename : args) {
                Table table = platform.getTableFromCache(catalogName, schemaName, tablename, true);
                if (table != null) {
                    triggerService.syncTriggers(table, genAlways);
                } else {
                    System.out.println("Unable to find table " + tablename);
                }
            }
        }
        if (file != null) {
            FileUtils.writeStringToFile(file, sqlBuffer.toString());
        }
    }

    private void dropTrigger(CommandLine line, List<String> args) throws IOException {
        ITriggerRouterService triggerService = getSymmetricEngine().getTriggerRouterService();
        if (args.size() == 0) {
            System.out.println("Dropping all triggers...");
            triggerService.dropTriggers();
        } else {
            for (String tablename : args) {
                System.out.println("Dropping trigger for table " + tablename);
                Set<String> tables = new HashSet<String>();
                tables.add(tablename);
                triggerService.dropTriggers(tables);
            }
        }
    }

    private void generateWar(CommandLine line, List<String> args) throws Exception {
        String warFileName = popArg(args, "Filename");
        final File workingDirectory = new File(AppUtils.getSymHome() + "/.war");
        FileUtils.deleteDirectory(workingDirectory);
        FileUtils.copyDirectory(new File(AppUtils.getSymHome() + "/web"), workingDirectory);
        FileUtils.copyDirectory(new File(AppUtils.getSymHome() + "/conf"),
                new File(workingDirectory, "WEB-INF/classes"));
        if (propertiesFile != null && propertiesFile.exists()) {
            FileUtils.copyFile(propertiesFile, new File(workingDirectory, "WEB-INF/classes/symmetric.properties"));
        }
        JarBuilder builder = new JarBuilder(workingDirectory, new File(warFileName),
                new File[] { workingDirectory }, Version.version());
        builder.build();
        FileUtils.deleteDirectory(workingDirectory);
    }

    private void exportSymTables(CommandLine line, List<String> args) throws IOException {
        OutputStreamWriter os = getWriter(args);
        os.write(getSymmetricEngine().getSymmetricDialect().getCreateSymmetricDDL());
        os.close();
    }

    private void exportDefaultProperties(CommandLine line, List<String> args) throws IOException {
        OutputStreamWriter os = getWriter(args);
        BufferedReader is = new BufferedReader(
                new InputStreamReader(SymmetricAdmin.class.getResourceAsStream("/symmetric-default.properties"),
                        Charset.defaultCharset()));
        String str = is.readLine();
        while (str != null) {
            os.write(str);
            os.write(System.getProperty("line.separator"));
            str = is.readLine();
        }
        is.close();
        os.close();
    }

    private void createSymTables() {
        getSymmetricEngine().setupDatabase(true);
    }

    private void uninstall(CommandLine line, List<String> args) {
        getSymmetricEngine().uninstall();
    }

    private void sendSql(CommandLine line, List<String> args) {
        String tableName = popArg(args, "Table Name");
        String sql = popArg(args, "SQL");
        String catalogName = line.getOptionValue(OPTION_CATALOG);
        String schemaName = line.getOptionValue(OPTION_SCHEMA);
        for (Node node : getNodes(line)) {
            System.out.println("Sending SQL to node '" + node.getNodeId() + "'");
            getSymmetricEngine().getDataService().sendSQL(node.getNodeId(), catalogName, schemaName, tableName,
                    sql);
        }
    }

    private void reloadTable(CommandLine line, List<String> args) {
        String catalogName = line.getOptionValue(OPTION_CATALOG);
        String schemaName = line.getOptionValue(OPTION_SCHEMA);

        if (args.size() == 0) {
            System.out.println("ERROR: Expected argument for: Table Name");
            System.exit(1);
        }
        for (String tableName : args) {
            for (Node node : getNodes(line)) {
                System.out.println("Reloading table '" + tableName + "' to node '" + node.getNodeId() + "'");

                if (line.hasOption(OPTION_WHERE)) {
                    System.out.println(getSymmetricEngine().getDataService().reloadTable(node.getNodeId(),
                            catalogName, schemaName, tableName, line.getOptionValue(OPTION_WHERE)));
                } else {
                    System.out.println(getSymmetricEngine().getDataService().reloadTable(node.getNodeId(),
                            catalogName, schemaName, tableName));
                }
            }
        }
    }

    private void sendSchema(CommandLine line, List<String> args) {
        String tableName = popArg(args, "Table Name");
        String catalog = line.getOptionValue(OPTION_CATALOG);
        String schema = line.getOptionValue(OPTION_SCHEMA);
        Collection<Node> nodes = getNodes(line);
        for (Node node : nodes) {
            getSymmetricEngine().getDataService().sendSchema(node.getNodeId(), catalog, schema, tableName, false);
        }
    }

    private void sendScript(CommandLine line, List<String> args) throws Exception {
        String scriptName = popArg(args, "Script Name");
        String scriptData = FileUtils.readFileToString(new File(scriptName));
        for (Node node : getNodes(line)) {
            System.out.println("Sending script to node '" + node.getNodeId() + "'");
            getSymmetricEngine().getDataService().sendScript(node.getNodeId(), scriptData, false);
        }
    }

    private Collection<Node> getNodes(CommandLine line) {
        if (line.hasOption(OPTION_NODE_GROUP)) {
            return getSymmetricEngine().getNodeService()
                    .findEnabledNodesFromNodeGroup(line.getOptionValue(OPTION_NODE_GROUP));
        } else if (line.hasOption(OPTION_NODE)) {
            Collection<Node> nodes = new ArrayList<Node>();
            String nodeId = line.getOptionValue(OPTION_NODE);
            Node node = getSymmetricEngine().getNodeService().findNode(nodeId);
            if (node == null) {
                System.out.println("ERROR: Unable to find node '" + nodeId + "'");
                System.exit(1);
            }
            nodes.add(node);
            return nodes;
        } else {
            System.out.println("ERROR: Must specify one option: " + OPTION_NODE + ", " + OPTION_NODE_GROUP);
            System.exit(1);
        }
        return null;
    }

    private OutputStreamWriter getWriter(List<String> args) throws IOException {
        OutputStreamWriter os = null;
        if (args.size() == 0) {
            os = new OutputStreamWriter(System.out);
        } else {
            File file = new File(args.get(0));
            if (file.getParentFile() != null) {
                file.getParentFile().mkdirs();
            }
            os = new FileWriter(file);
        }
        return os;
    }
}