org.lilyproject.runtime.cli.LilyRuntimeCli.java Source code

Java tutorial

Introduction

Here is the source code for org.lilyproject.runtime.cli.LilyRuntimeCli.java

Source

/*
 * Copyright 2013 NGDATA nv
 * Copyright 2007 Outerthought bvba and Schaubroeck nv
 *
 * 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 org.lilyproject.runtime.cli;

import javax.management.JMException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.LogManager;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.lilyproject.runtime.LilyRTException;
import org.lilyproject.runtime.LilyRuntime;
import org.lilyproject.runtime.LilyRuntimeSettings;
import org.lilyproject.runtime.configuration.ConfManagerImpl;
import org.lilyproject.runtime.model.SourceLocations;
import org.lilyproject.runtime.rapi.Mode;
import org.lilyproject.runtime.repository.ArtifactRepository;
import org.lilyproject.runtime.repository.ChainedMaven2StyleArtifactRepository;
import org.lilyproject.runtime.repository.Maven2StyleArtifactRepository;
import org.lilyproject.util.io.IOUtils;
import org.lilyproject.util.xml.SimpleNamespaceContext;
import org.slf4j.bridge.SLF4JBridgeHandler;
import org.w3c.dom.Document;

@SuppressWarnings({ "AccessStaticViaInstance" })
public class LilyRuntimeCli {
    protected final Log infolog = LogFactory.getLog(LilyRuntime.INFO_LOG_CATEGORY);
    private static String DEFAULT_CONF_DIR = "conf";

    public static void main(String[] args) throws Exception {
        new LilyRuntimeCli().run(args);
    }

    private LilyRuntimeCli() {

    }

    private void run(String[] args) throws Exception {
        // Forward JDK logging to SLF4J
        LogManager.getLogManager().reset();
        LogManager.getLogManager().getLogger("").addHandler(new SLF4JBridgeHandler());
        LogManager.getLogManager().getLogger("").setLevel(Level.ALL);

        Options cliOptions = new Options();

        Option confDirsOption = OptionBuilder.withArgName("confdir").hasArg()
                .withDescription("The Lily runtime configuration directory. Can be multiple paths separated by "
                        + File.pathSeparator)
                .withLongOpt("confdir").create('c');
        cliOptions.addOption(confDirsOption);

        Option repositoryLocationOption = OptionBuilder.withArgName("maven-repo-path").hasArg()
                .withDescription(
                        "Location of the (Maven-style) artifact repository. Use comma-separated entries to "
                                + "specify multiple locations which will be searched in the order as specified.")
                .withLongOpt("repository").create('r');
        cliOptions.addOption(repositoryLocationOption);

        Option disabledModulesOption = OptionBuilder.withArgName("mod-id1,mod-id2,...").hasArg()
                .withDescription("Comma-separated list of modules that should be disabled.")
                .withLongOpt("disable-modules").create('i');
        cliOptions.addOption(disabledModulesOption);

        Option disableClassSharingOption = OptionBuilder
                .withDescription("Disable optional sharing of classes between modules")
                .withLongOpt("disable-class-sharing").create('d');
        cliOptions.addOption(disableClassSharingOption);

        Option consoleLoggingOption = OptionBuilder.withArgName("loglevel").hasArg()
                .withDescription("Enable logging to console for the root log category with specified loglevel "
                        + "(debug, info, warn, error)")
                .withLongOpt("console-logging").create('l');
        cliOptions.addOption(consoleLoggingOption);

        Option consoleLogCatOption = OptionBuilder.withArgName("logcategory").hasArg()
                .withDescription("Enable console logging only for this category")
                .withLongOpt("console-log-category").create('m');
        cliOptions.addOption(consoleLogCatOption);

        Option logConfigurationOption = OptionBuilder.withArgName("config").hasArg()
                .withDescription("Log4j configuration file (properties or .xml)").withLongOpt("log-configuration")
                .create("o");
        cliOptions.addOption(logConfigurationOption);

        Option classLoadingLoggingOption = OptionBuilder
                .withDescription("Print information about the classloader setup (at startup).")
                .withLongOpt("classloader-log").create("z");
        cliOptions.addOption(classLoadingLoggingOption);

        Option verboseOption = OptionBuilder.withDescription("Prints lots of information.").withLongOpt("verbose")
                .create("v");
        cliOptions.addOption(verboseOption);

        Option quietOption = OptionBuilder.withDescription("Suppress normal output.").withLongOpt("quiet")
                .create("q");
        cliOptions.addOption(quietOption);

        Option sourceLocationsOption = OptionBuilder.withArgName("sourcelocationfile").hasArg()
                .withDescription(
                        "Path to property file containing alternate source location directory for artifacts.")
                .withLongOpt("source-locations").create("s");
        cliOptions.addOption(sourceLocationsOption);

        Option modeOption = OptionBuilder.withArgName("modename").hasArg()
                .withDescription("The runtime mode: prototype, production").withLongOpt("runtime-mode").create("p");
        cliOptions.addOption(modeOption);

        Option versionOption = OptionBuilder.withDescription(
                "Don't start the service, only dump the version info string for the module defined with -Dlilyruntime.info.module")
                .withLongOpt("version").create("V");
        cliOptions.addOption(versionOption);

        Option helpOption = new Option("h", "help", false, "Shows help");
        cliOptions.addOption(helpOption);

        CommandLineParser parser = new PosixParser();
        CommandLine cmd = null;
        boolean showHelp = false;
        try {
            cmd = parser.parse(cliOptions, args);
        } catch (ParseException e) {
            showHelp = true;
        }

        if (showHelp || cmd.hasOption(helpOption.getOpt())) {
            printHelp(cliOptions);
            System.exit(1);
        }

        Logging.setupLogging(cmd.hasOption(verboseOption.getOpt()), cmd.hasOption(quietOption.getOpt()),
                cmd.hasOption(classLoadingLoggingOption.getOpt()),
                cmd.getOptionValue(logConfigurationOption.getOpt()),
                cmd.getOptionValue(consoleLoggingOption.getOpt()),
                cmd.getOptionValue(consoleLogCatOption.getOpt()));

        try {
            Logging.registerLog4jMBeans();
        } catch (JMException e) {
            infolog.error("Unable to register log4j JMX control", e);
        }

        infolog.info("Starting the Lily Runtime.");

        List<File> confDirs = new ArrayList<File>();

        if (!cmd.hasOption(confDirsOption.getOpt())) {
            File confDir = new File(DEFAULT_CONF_DIR).getAbsoluteFile();
            if (!confDir.exists()) {
                System.out.println("Default configuration directory " + DEFAULT_CONF_DIR
                        + " not found in current directory: " + confDir.getAbsolutePath());
                System.out
                        .println("To specify another location, use the -" + confDirsOption.getOpt() + " argument");
                System.exit(1);
            }
            confDirs.add(confDir);
        } else {
            String confPathArg = cmd.getOptionValue(confDirsOption.getOpt());
            String[] confPaths = confPathArg.split(File.pathSeparator);
            for (String confPath : confPaths) {
                confPath = confPath.trim();
                if (confPath.length() == 0) {
                    continue;
                }
                File confDir = new File(confPath);
                if (!confDir.exists()) {
                    System.out.println(
                            "Specified configuration directory does not exist: " + confDir.getAbsolutePath());
                    System.exit(1);
                }
                confDirs.add(confDir);
            }
        }

        ArtifactRepository artifactRepository;

        if (cmd.hasOption(repositoryLocationOption.getOpt())) {
            artifactRepository = new ChainedMaven2StyleArtifactRepository(
                    cmd.getOptionValue(repositoryLocationOption.getOpt()));
        } else {
            File maven2Repository = findLocalMavenRepository();
            infolog.info("Using local Maven repository at " + maven2Repository.getAbsolutePath());
            artifactRepository = new Maven2StyleArtifactRepository(maven2Repository);
        }

        Set<String> disabledModuleIds = getDisabledModuleIds(cmd.getOptionValue(disabledModulesOption.getOpt()));

        SourceLocations sourceLocations;
        if (cmd.hasOption(sourceLocationsOption.getOpt())) {
            File file = new File(cmd.getOptionValue(sourceLocationsOption.getOpt())).getAbsoluteFile();
            if (!file.exists()) {
                System.out.println(
                        "The specified source locations property file does not exist: " + file.getAbsolutePath());
                System.exit(1);
            }

            InputStream is = null;
            try {
                is = new FileInputStream(file);
                sourceLocations = new SourceLocations(is, file.getParent());
            } catch (Throwable t) {
                throw new LilyRTException("Problem reading source locations property file.", t);
            } finally {
                IOUtils.closeQuietly(is);
            }
        } else {
            sourceLocations = new SourceLocations();
        }

        LilyRuntimeSettings settings = new LilyRuntimeSettings();
        settings.setConfManager(new ConfManagerImpl(confDirs));
        settings.setDisabledModuleIds(disabledModuleIds);
        settings.setRepository(artifactRepository);
        settings.setSourceLocations(sourceLocations);
        settings.setEnableArtifactSharing(!cmd.hasOption(disableClassSharingOption.getOpt()));

        LilyRuntime runtime = new LilyRuntime(settings);

        if (cmd.hasOption(modeOption.getOpt())) {
            String optionValue = cmd.getOptionValue(modeOption.getOpt());
            Mode mode = Mode.byName(optionValue);
            runtime.setMode(mode);
        }

        if (cmd.hasOption(versionOption.getOpt())) {
            System.out.println(runtime.buildModel().moduleInfo(System.getProperty("lilyruntime.info.module")));
            System.exit(0);
        }

        try {
            runtime.start();
            Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHandler(runtime)));
            printStartedMessage();
        } catch (Throwable e) {
            e.printStackTrace();
            System.err.println("Startup failed. Will try to shutdown and exit.");
            try {
                runtime.stop();
            } finally {
                System.exit(1);
            }
        }

    }

    private void printStartedMessage() {
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
        String now = dateFormat.format(new Date());
        infolog.info("Lily Runtime started [" + now + "]");
    }

    private void printHelp(Options cliOptions) {
        HelpFormatter help = new HelpFormatter();
        help.printHelp("lily-runtime", cliOptions, true);
    }

    private Set<String> getDisabledModuleIds(String spec) {
        if (spec == null) {
            return Collections.emptySet();
        }

        Set<String> ids = new HashSet<String>();

        String[] items = spec.split(",");
        for (String item : items) {
            item = item.trim();
            if (item.length() > 0) {
                ids.add(item);
            }
        }

        return ids;
    }

    // This method is duplicated in LilyRuntimeCliLauncher, so if you modify it
    // here, it is likely useful to copy you modifications there too.
    private File findLocalMavenRepository() {
        String homeDir = System.getProperty("user.home");
        File mavenSettingsFile = new File(homeDir + "/.m2/settings.xml");
        if (mavenSettingsFile.exists()) {
            try {
                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                dbf.setNamespaceAware(true);
                DocumentBuilder db = dbf.newDocumentBuilder();
                Document document = db.parse(mavenSettingsFile);
                XPath xpath = XPathFactory.newInstance().newXPath();
                SimpleNamespaceContext nc = new SimpleNamespaceContext();
                nc.addPrefix("m", "http://maven.apache.org/POM/4.0.0");
                xpath.setNamespaceContext(nc);

                String localRepository = xpath.evaluate("string(/m:settings/m:localRepository)", document);
                if (localRepository != null && localRepository.length() > 0) {
                    return new File(localRepository);
                }

                // Usage of the POM namespace in settings.xml is optional, so also try without namespace
                localRepository = xpath.evaluate("string(/settings/localRepository)", document);
                if (localRepository != null && localRepository.length() > 0) {
                    return new File(localRepository);
                }
            } catch (Exception e) {
                System.err.println("Error reading Maven settings file at " + mavenSettingsFile.getAbsolutePath());
                e.printStackTrace();
                System.exit(1);
            }
        }
        return new File(homeDir + "/.m2/repository");
    }

    public static class ShutdownHandler implements Runnable {
        private final LilyRuntime runtime;

        public ShutdownHandler(LilyRuntime runtime) {
            this.runtime = runtime;
        }

        public void run() {
            runtime.stop();
        }
    }
}