/* * 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(); } } }