org.rapidcontext.app.Main.java Source code

Java tutorial

Introduction

Here is the source code for org.rapidcontext.app.Main.java

Source

/*
 * RapidContext <http://www.rapidcontext.com/>
 * Copyright (c) 2007-2014 Per Cederberg. All rights reserved.
 *
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the BSD license.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the RapidContext LICENSE for more details.
 */

package org.rapidcontext.app;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang.SystemUtils;
import org.rapidcontext.app.ui.ControlPanel;
import org.rapidcontext.util.ClasspathUtil;
import org.rapidcontext.util.FileUtil;

/**
 * The application start point, handling command-line parsing and
 * launching the application in the appropriate mode.
 *
 * @author   Per Cederberg
 * @version  1.0
 */
public class Main {

    /**
     * The class logger.
     */
    private static final Logger LOG = Logger.getLogger(Main.class.getName());

    /**
     * The command-line usage information.
     */
    public static final String USAGE = "Usage: [1] rapidcontext [--app] [<options>]\n"
            + "       [2] rapidcontext --server [<options>]\n"
            + "       [3] rapidcontext [--script] [<options>] [<procedure> [<arg1> ...]]\n" + "\n"
            + "Alternative [1] is assumed when no procedure is specified.\n"
            + "Alternative [3] is assumed when a procedure is specified.";

    // Static initializer (fix for Mac UI)
    static {
        String str = "com.apple.mrj.application.apple.menu.about.name";
        System.setProperty(str, "RapidContext");
        System.setProperty("apple.awt.brushMetalLook", "true");
    }

    /**
     * Application entry point.
     *
     * @param args           the command-line parameters
     */
    public static void main(String[] args) {
        Options options = new Options();
        OptionGroup grp;
        Option opt;
        CommandLine cli;

        // Create main command-line options
        grp = new OptionGroup();
        opt = new Option(null, "app", false, "Launch in interactive application mode.");
        grp.addOption(opt);
        opt = new Option(null, "server", false, "Launch in server mode.");
        grp.addOption(opt);
        opt = new Option(null, "script", false, "Launch in script execution mode.");
        grp.addOption(opt);
        options.addOptionGroup(grp);

        // Create other command-line options
        options.addOption("h", "help", false, "Displays this help message,");
        opt = new Option("l", "local", true, "Use a specified local app directory.");
        opt.setArgName("dir");
        options.addOption(opt);
        opt = new Option("p", "port", true, "Use a specified port number (non-script mode).");
        opt.setArgName("number");
        options.addOption(opt);
        opt = new Option("d", "delay", true, "Add a delay after each command (script mode).");
        opt.setArgName("secs");
        options.addOption(opt);
        options.addOption("t", "trace", false, "Print detailed execution trace (script mode).");
        opt = new Option("u", "user", true, "Authenticate as a another user (script mode).");
        opt.setArgName("name");
        options.addOption(opt);
        options.addOption(null, "stdin", false, "Read commands from stdin (script mode).");
        opt = new Option("f", "file", true, "Read commands from a file (script mode).");
        opt.setArgName("file");
        options.addOption(opt);

        // Parse command-line arguments
        try {
            cli = new DefaultParser().parse(options, args);
            if (cli.hasOption("help")) {
                exit(options, null);
            }
            args = cli.getArgs();
            if (cli.hasOption("app")) {
                runApp(cli, args, options);
            } else if (cli.hasOption("server")) {
                runServer(cli, args, options);
            } else if (cli.hasOption("script")) {
                runScript(cli, args, options);
            } else if (args.length == 0 && !SystemUtils.isJavaAwtHeadless()) {
                runApp(cli, args, options);
            } else {
                runScript(cli, args, options);
            }
        } catch (ParseException e) {
            exit(options, e.getMessage());
        }
    }

    /**
     * Launches the interactive application mode.
     *
     * @param cli            the parsed command line
     * @param args           the additional arguments array
     * @param opts           the command-line options object
     */
    private static void runApp(CommandLine cli, String[] args, Options opts) {
        ServerApplication app = createServer(cli, opts);
        ControlPanel panel;

        if (args.length > 0) {
            exit(opts, "No arguments supported for app launch mode.");
        }
        if (SystemUtils.isJavaAwtHeadless()) {
            exit(opts, "Cannot launch app without graphical display.");
        }
        panel = new ControlPanel(app);
        panel.setVisible(true);
        panel.start();
    }

    /**
     * Launches the server mode.
     *
     * @param cli            the parsed command line
     * @param args           the additional arguments array
     * @param opts           the command-line options object
     */
    private static void runServer(CommandLine cli, String[] args, Options opts) {
        ServerApplication app = createServer(cli, opts);

        if (args.length > 0) {
            exit(opts, "No arguments supported for server launch mode.");
        }
        try {
            app.start();
        } catch (Exception e) {
            LOG.log(Level.SEVERE, "error starting server", e);
            exit(null, e.getMessage());
        }
        System.out.print("Server started -- http://localhost");
        if (app.port != 80) {
            System.out.print(":");
            System.out.print(app.port);
        }
        System.out.println("/");
        System.out.println("Press Ctrl-C to shutdown (or terminate process by other means).");
        System.out.println();
    }

    /**
     * Launches the script mode.
     *
     * @param cli            the parsed command line
     * @param args           the additional arguments array
     * @param opts           the command-line options object
     */
    private static void runScript(CommandLine cli, String[] args, Options opts) {
        ScriptApplication app = new ScriptApplication();
        ServerApplication server = createServer(cli, opts);

        app.appDir = server.appDir;
        app.localDir = server.localDir;
        app.user = cli.getOptionValue("user", System.getProperty("user.name"));
        try {
            app.delay = Integer.parseInt(cli.getOptionValue("delay", "0"));
        } catch (Exception e) {
            exit(opts, "Invalid delay number: " + cli.getOptionValue("delay"));
        }
        if (app.delay < 0 || app.delay > 3600) {
            exit(opts, "Invalid delay number, must be between 0 and 3600.");
        }
        app.trace = cli.hasOption("trace");
        try {
            if (cli.hasOption("stdin")) {
                app.runStdin(args);
            } else if (cli.hasOption("file")) {
                app.runFile(args, new File(cli.getOptionValue("file")));
            } else if (args.length <= 0) {
                exit(opts, "No command specified for --script mode.");
            } else {
                app.runSingle(args);
            }
        } catch (Exception e) {
            LOG.log(Level.SEVERE, "error running script", e);
            exit(null, e.getMessage());
        }
    }

    /**
     * Creates a server application instance from the command-line
     * arguments.
     *
     * @param cli            the parsed command line
     * @param opts           the command-line options object
     *
     * @return the server application created (not started)
     */
    private static ServerApplication createServer(CommandLine cli, Options opts) {
        ServerApplication app = new ServerApplication();

        try {
            app.port = Integer.parseInt(cli.getOptionValue("port", "0"));
        } catch (Exception e) {
            exit(opts, "Invalid port number: " + cli.getOptionValue("port"));
        }
        if (app.port < 0 || app.port > 65535) {
            exit(opts, "Invalid port number, must be between 0 and 65535.");
        }
        app.appDir = locateAppDir();
        if (app.appDir == null) {
            exit(null, "Failed to locate application directory.");
        }
        app.appDir = app.appDir.getAbsoluteFile();
        app.localDir = app.appDir;
        if (cli.hasOption("local")) {
            app.localDir = new File(cli.getOptionValue("local"));
            if (!app.localDir.exists()) {
                app.localDir.mkdirs();
            }
        }
        File pluginDir = new File(app.localDir, "plugin");
        if (!pluginDir.exists()) {
            pluginDir.mkdirs();
        }
        if (!isDir(app.localDir, true)) {
            exit(null, "Cannot write to directory: " + app.localDir);
        } else if (!isDir(pluginDir, true)) {
            exit(null, "Cannot write to plug-in directory: " + pluginDir);
        }
        app.localDir = app.localDir.getAbsoluteFile();
        try {
            setupLocalAppDir(app.appDir, app.localDir);
        } catch (IOException e) {
            exit(null, "Failed to setup local directory: " + e.getMessage());
        }
        return app;
    }

    /**
     * Exits the application with optional help and/or error messages.
     * This method WILL NOT RETURN.
     *
     * @param options        the command-line options, or null
     * @param error          the error message, or null
     */
    private static void exit(Options options, String error) {
        PrintWriter out = new PrintWriter(System.err);
        HelpFormatter fmt = new HelpFormatter();

        if (options != null) {
            out.println(USAGE);
            out.println();
            out.println("Options:");
            fmt.setOptionComparator(null);
            fmt.printOptions(out, 74, options, 2, 3);
            out.println();
        }
        if (error != null && error.length() > 0) {
            out.println("ERROR:");
            out.print("    ");
            out.println(error);
            out.println();
        }
        out.flush();
        System.exit(error == null ? 0 : 1);
    }

    /**
     * Attempts to locate the application directory based on the
     * current working directory and the class path.
     *
     * @return the application directory found, or
     *         null otherwise
     */
    private static File locateAppDir() {
        File[] dirs = { new File("."), SystemUtils.getUserDir(),
                ClasspathUtil.locateFile(ServerApplication.class) };

        for (int i = 0; i < dirs.length; i++) {
            File file = dirs[i];
            for (int j = 0; file != null && j < 4; j++) {
                if (isAppDir(file)) {
                    return file;
                }
                file = file.getParentFile();
            }
        }
        return null;
    }

    /**
     * Sets up the local application directory if it is different
     * from the built-in application directory.
     *
     * @param appDir         the built-in application directory
     * @param localDir       the local application directory
     *
     * @throws IOException if the local directory couldn't be created
     */
    private static void setupLocalAppDir(File appDir, File localDir) throws IOException {

        if (!appDir.equals(localDir)) {
            appDir = new File(appDir, "plugin/local");
            localDir = new File(localDir, "plugin/local");
            if (!localDir.exists()) {
                FileUtil.copy(appDir, localDir);
            }
        }
    }

    /**
     * Checks if the specified file is the application directory.
     *
     * @param file           the file to check
     *
     * @return true if the file matches, or
     *         false otherwise
     */
    private static boolean isAppDir(File file) {
        return file != null && isDir(file, false) && isDir(new File(file, "plugin"), false)
                && (new File(file, "doc.zip")).canRead();
    }

    /**
     * Checks if the specified file is a readable directory.
     *
     * @param file           the file to check
     * @param write          the write check flag
     *
     * @return true if the file is a directory, or
     *         false otherwise
     */
    private static boolean isDir(File file, boolean write) {
        return file != null && file.isDirectory() && file.canRead() && (!write || file.canWrite());
    }

    /**
     * Returns the build information for the application.
     *
     * @return the build information
     */
    public static Properties buildInfo() {
        Properties info = new Properties();
        InputStream is = Main.class.getResourceAsStream("build.properties");
        try {
            info.load(is);
        } catch (IOException ignore) {
            // Ignore exception on loading properties
        } finally {
            try {
                is.close();
            } catch (Exception ignore) {
                // Ignore exception on closing file
            }
        }
        return info;
    }
}