/* * (C) Copyright 2010-2012 Nuxeo SA (http://nuxeo.com/) and contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl.html * * This library 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 GNU * Lesser General Public License for more details. * * Contributors: * Julien Carsique * */ package org.nuxeo.launcher; import java.io.File; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.regex.Pattern; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.MissingArgumentException; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.OptionGroup; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import org.apache.commons.cli.UnrecognizedOptionException; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.impl.SimpleLog; import org.artofsolving.jodconverter.process.MacProcessManager; import org.artofsolving.jodconverter.process.ProcessManager; import org.artofsolving.jodconverter.process.PureJavaProcessManager; import org.artofsolving.jodconverter.process.UnixProcessManager; import org.artofsolving.jodconverter.process.WindowsProcessManager; import org.artofsolving.jodconverter.util.PlatformUtils; import org.json.JSONException; import org.json.XML; import org.nuxeo.common.Environment; import org.nuxeo.connect.identity.LogicalInstanceIdentifier.NoCLID; import org.nuxeo.connect.update.LocalPackage; import org.nuxeo.connect.update.PackageException; import org.nuxeo.connect.update.PackageState; import org.nuxeo.launcher.config.ConfigurationException; import org.nuxeo.launcher.config.ConfigurationGenerator; import org.nuxeo.launcher.connect.ConnectBroker; import org.nuxeo.launcher.daemon.DaemonThreadFactory; import org.nuxeo.launcher.gui.NuxeoLauncherGUI; import org.nuxeo.launcher.info.CommandInfo; import org.nuxeo.launcher.info.CommandSetInfo; import org.nuxeo.launcher.info.ConfigurationInfo; import org.nuxeo.launcher.info.DistributionInfo; import org.nuxeo.launcher.info.InstanceInfo; import org.nuxeo.launcher.info.KeyValueInfo; import org.nuxeo.launcher.info.MessageInfo; import org.nuxeo.launcher.info.PackageInfo; import org.nuxeo.launcher.monitoring.StatusServletClient; import org.nuxeo.log4j.Log4JHelper; import org.nuxeo.log4j.ThreadedStreamGobbler; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; /** * @author jcarsique * @since 5.4.2 */ public abstract class NuxeoLauncher { /** * @since 5.6 */ protected static final String OPTION_NODEPS = "nodeps"; private static final String OPTION_NODEPS_DESC = "Ignore package dependencies and constraints."; /** * @since 5.6 */ protected static final String OPTION_GUI = "gui"; private static final String OPTION_GUI_DESC = "Use graphical user interface (default depends on OS)."; /** * @since 5.6 */ protected static final String OPTION_JSON = "json"; private static final String OPTION_JSON_DESC = "Output JSON for mp-commands."; /** * @since 5.6 */ protected static final String OPTION_XML = "xml"; private static final String OPTION_XML_DESC = "Output XML for mp-commands."; /** * @since 5.6 */ protected static final String OPTION_DEBUG = "debug"; private static final String OPTION_DEBUG_DESC = "Activate debug messages. " + "See 'category' option."; /** * @since 5.6 */ protected static final String OPTION_DEBUG_CATEGORY = "dc"; private static final String OPTION_DEBUG_CATEGORY_DESC = "Comma separated root categories for 'debug' option (default: \"org.nuxeo.launcher\")."; /** * @since 5.6 */ protected static final String OPTION_QUIET = "quiet"; private static final String OPTION_QUIET_DESC = "Suppress information messages."; /** * @since 5.6 */ protected static final String OPTION_HELP = "help"; private static final String OPTION_HELP_DESC = "Show detailed help."; /** * @since 5.6 */ protected static final String OPTION_RELAX = "relax"; private static final String OPTION_RELAX_DESC = "Allow relax constraint on current platform (default: " + ConnectBroker.OPTION_RELAX_DEFAULT + ")."; /** * @since 5.6 */ protected static final String OPTION_ACCEPT = "accept"; private static final String OPTION_ACCEPT_DESC = "Accept, refuse or ask confirmation for all changes (default: " + ConnectBroker.OPTION_ACCEPT_DEFAULT + ")."; /** * @since 5.6 */ protected static final String OPTION_HIDE_DEPRECATION = "hide-deprecation-warnings"; protected static final String OPTION_HIDE_DEPRECATION_DESC = "Hide deprecation warnings. Not advised on production platforms."; // Fallback to avoid an error when the log dir is not initialized static { if (System.getProperty(Environment.NUXEO_LOG_DIR) == null) { System.setProperty(Environment.NUXEO_LOG_DIR, "."); } } /** * @since 5.6 */ private static final String DEFAULT_NUXEO_CONTEXT_PATH = "/nuxeo"; static final Log log = LogFactory.getLog(NuxeoLauncher.class); private static Options launcherOptions = null; private static final String JAVA_OPTS_PROPERTY = "launcher.java.opts"; private static final String JAVA_OPTS_DEFAULT = "-Xms512m -Xmx1024m -XX:MaxPermSize=512m"; private static final String OVERRIDE_JAVA_TMPDIR_PARAM = "launcher.override.java.tmpdir"; protected boolean overrideJavaTmpDir; private static final String START_MAX_WAIT_PARAM = "launcher.start.max.wait"; private static final String STOP_MAX_WAIT_PARAM = "launcher.stop.max.wait"; /** * Default maximum time to wait for server startup summary in logs (in * seconds). */ private static final String START_MAX_WAIT_DEFAULT = "300"; private static final String START_MAX_WAIT_JBOSS_DEFAULT = "900"; /** * Default maximum time to wait for effective stop (in seconds) */ private static final String STOP_MAX_WAIT_DEFAULT = "60"; /** * Number of try to cleanly stop server before killing process */ private static final int STOP_NB_TRY = 5; private static final int STOP_SECONDS_BEFORE_NEXT_TRY = 2; private static final long STREAM_MAX_WAIT = 3000; private static final String PACK_JBOSS_CLASS = "org.nuxeo.runtime.deployment.preprocessor.PackZip"; private static final String PACK_TOMCAT_CLASS = "org.nuxeo.runtime.deployment.preprocessor.PackWar"; private static final String PARAM_UPDATECENTER_DISABLED = "nuxeo.updatecenter.disabled"; private static final String[] COMMANDS_NO_GUI = { "configure", "mp-init", "mp-purge", "mp-add", "mp-install", "mp-uninstall", "mp-request", "mp-remove", "mp-hotfix", "mp-upgrade", "mp-reset", "mp-list", "mp-listall", "mp-update", "status", "showconf", "mp-show" }; private static final String[] COMMANDS_NO_RUNNING_SERVER = { "mp-init", "mp-purge", "mp-add", "mp-install", "mp-uninstall", "mp-request", "mp-remove", "mp-hotfix", "mp-upgrade", "mp-reset", "mp-update" }; /** * Program is running or service is OK. * * @since 5.7 */ public static final int STATUS_CODE_ON = 0; /** * Program is not running. * * @since 5.7 */ public static final int STATUS_CODE_OFF = 3; /** * Program or service status is unknown. * * @since 5.7 */ public static final int STATUS_CODE_UNKNOWN = 4; /** * @since 5.7 */ public static final int EXIT_CODE_OK = 0; /** * Generic or unspecified error. * * @since 5.7 */ public static final int EXIT_CODE_ERROR = 1; /** * Invalid or excess argument(s). * * @since 5.7 */ public static final int EXIT_CODE_INVALID = 2; /** * Unimplemented feature. * * @since 5.7 */ public static final int EXIT_CODE_UNIMPLEMENTED = 3; /** * User had insufficient privilege. * * @since 5.7 */ public static final int EXIT_CODE_UNAUTHORIZED = 4; /** * Program is not installed. * * @since 5.7 */ public static final int EXIT_CODE_NOT_INSTALLED = 5; /** * Program is not configured. * * @since 5.7 */ public static final int EXIT_CODE_NOT_CONFIGURED = 6; /** * Program is not running. * * @since 5.7 */ public static final int EXIT_CODE_NOT_RUNNING = 7; protected ConfigurationGenerator configurationGenerator; public final ConfigurationGenerator getConfigurationGenerator() { return configurationGenerator; } protected ProcessManager processManager; protected Process nuxeoProcess; private String processRegex; protected String pid; private ExecutorService executor = Executors.newSingleThreadExecutor(new DaemonThreadFactory( "NuxeoProcessThread", false)); private ShutdownThread shutdownHook; protected static String[] params; protected String command; public String getCommand() { return command; } public CommandSetInfo cset = new CommandSetInfo(); private boolean useGui = false; /** * @since 5.5 */ public boolean isUsingGui() { return useGui; } private boolean reloadConfiguration = false; private int status = STATUS_CODE_UNKNOWN; private int errorValue = EXIT_CODE_OK; private StatusServletClient statusServletClient; private static boolean quiet = false; private static boolean debug = false; private boolean xmlOutput = false; private boolean jsonOutput = false; private ConnectBroker connectBroker = null; private CommandLine cmdLine; /** * @since 5.5 * @return true if quiet mode is active */ public boolean isQuiet() { return quiet; } private static Map guis; /** * @since 5.5 */ public NuxeoLauncherGUI getGUI() { if (guis == null) { return null; } return guis.get(configurationGenerator.getNuxeoConf().toString()); } /** * @since 5.5 */ public void setGUI(NuxeoLauncherGUI gui) { if (guis == null) { guis = new HashMap(); } guis.put(configurationGenerator.getNuxeoConf().toString(), gui); } public NuxeoLauncher(ConfigurationGenerator configurationGenerator) { this.configurationGenerator = configurationGenerator; init(); } /** * @since 5.6 */ public void init() { if (!configurationGenerator.init(true)) { throw new IllegalStateException("Initialization failed"); } statusServletClient = new StatusServletClient(configurationGenerator); statusServletClient.setKey(configurationGenerator.getUserConfig().getProperty( ConfigurationGenerator.PARAM_STATUS_KEY)); processManager = getOSProcessManager(); processRegex = "^(?!/bin/sh).*" + Pattern.quote(configurationGenerator.getNuxeoConf().getPath()) + ".*" + Pattern.quote(getServerPrint()) + ".*$"; // Set OS-specific decorations if (PlatformUtils.isMac()) { System.setProperty( "com.apple.mrj.application.apple.menu.about.name", "NuxeoCtl"); } } private ProcessManager getOSProcessManager() { if (PlatformUtils.isLinux() || isAix()) { UnixProcessManager unixProcessManager = new UnixProcessManager(); return unixProcessManager; } else if (PlatformUtils.isMac()) { return new MacProcessManager(); } else if (PlatformUtils.isWindows()) { WindowsProcessManager windowsProcessManager = new WindowsProcessManager(); return windowsProcessManager.isUsable() ? windowsProcessManager : new PureJavaProcessManager(); } else { // NOTE: UnixProcessManager can't be trusted to work on Solaris // because of the 80-char limit on ps output there return new PureJavaProcessManager(); } } private boolean isAix() { String osName = System.getProperty("os.name"); if (osName != null) { return osName.equalsIgnoreCase("aix"); } return false; } /** * Do not directly call this method without a call to * {@link #checkNoRunningServer()} * * @see #doStart() * @throws IOException In case of issue with process. * @throws InterruptedException If any thread has interrupted the current * thread. */ protected void start(boolean logProcessOutput) throws IOException, InterruptedException { List startCommand = new ArrayList(); startCommand.add(getJavaExecutable().getPath()); startCommand.addAll(Arrays.asList(getJavaOptsProperty().split(" "))); startCommand.add("-cp"); startCommand.add(getClassPath()); startCommand.addAll(getNuxeoProperties()); startCommand.addAll(getServerProperties()); setServerStartCommand(startCommand); for (String param : params) { startCommand.add(param); } ProcessBuilder pb = new ProcessBuilder(getOSCommand(startCommand)); pb.directory(configurationGenerator.getNuxeoHome()); // pb = pb.redirectErrorStream(true); log.debug("Server command: " + pb.command()); nuxeoProcess = pb.start(); Thread.sleep(1000); boolean processExited = false; // Check if process exited early try { int exitValue = nuxeoProcess.exitValue(); if (exitValue != 0) { log.error(String.format("Server start failed (%d).", exitValue)); } processExited = true; } catch (IllegalThreadStateException e) { // Normal case } logProcessStreams(nuxeoProcess, processExited || logProcessOutput); if (!processExited) { if (getPid() != null) { log.warn("Server started with process ID " + pid + "."); } else { log.warn("Sent server start command but could not get process ID."); } } } /** * Gets the java options with Nuxeo properties substituted. * * It enables usage of property like ${nuxeo.log.dir} inside JAVA_OPTS. * * @return the java options string. */ protected String getJavaOptsProperty() { String ret = System.getProperty(JAVA_OPTS_PROPERTY, JAVA_OPTS_DEFAULT); String properties[] = { Environment.NUXEO_HOME_DIR, Environment.NUXEO_LOG_DIR, Environment.NUXEO_DATA_DIR, Environment.NUXEO_TMP_DIR }; for (String property : properties) { String value = configurationGenerator.getUserConfig().getProperty( property); if (value != null && !value.isEmpty()) { ret = ret.replace("${" + property + "}", value); } } return ret; } /** * Check if some server is already running (from another thread) and throw a * Runtime exception if it finds one. That method will work where * {@link #isRunning()} won't. * * @throws IllegalThreadStateException Thrown if a server is already * running. */ public void checkNoRunningServer() throws IllegalStateException { try { String existingPid = getPid(); if (existingPid != null) { errorValue = EXIT_CODE_OK; throw new IllegalStateException( "A server is running with process ID " + existingPid); } } catch (IOException e) { log.warn("Could not check existing process: " + e.getMessage()); } } /** * @return (since 5.5) Array list with created stream gobbler threads. */ public ArrayList logProcessStreams(Process process, boolean logProcessOutput) { ArrayList sgArray = new ArrayList(); ThreadedStreamGobbler inputSG, errorSG; if (logProcessOutput) { inputSG = new ThreadedStreamGobbler(process.getInputStream(), System.out); errorSG = new ThreadedStreamGobbler(process.getErrorStream(), System.err); } else { inputSG = new ThreadedStreamGobbler(process.getInputStream(), SimpleLog.LOG_LEVEL_OFF); errorSG = new ThreadedStreamGobbler(process.getErrorStream(), SimpleLog.LOG_LEVEL_OFF); } inputSG.start(); errorSG.start(); sgArray.add(inputSG); sgArray.add(errorSG); return sgArray; } protected abstract String getServerPrint(); /** * Will wrap, if necessary, the command within a Shell command * * @param roughCommand Java command which will be run * @return wrapped command depending on the OS */ private List getOSCommand(List roughCommand) { String linearizedCommand = new String(); ArrayList osCommand = new ArrayList(); if (PlatformUtils.isLinux() || PlatformUtils.isMac()) { for (Iterator iterator = roughCommand.iterator(); iterator.hasNext();) { String commandToken = iterator.next(); if (commandToken.contains(" ")) { commandToken = commandToken.replaceAll(" ", "\\\\ "); } linearizedCommand += " " + commandToken; } osCommand.add("/bin/sh"); osCommand.add("-c"); osCommand.add(linearizedCommand); // osCommand.add("&"); return osCommand; // return roughCommand; } else if (PlatformUtils.isWindows()) { // for (Iterator iterator = roughCommand.iterator(); // iterator.hasNext();) { // String commandToken = iterator.next(); // if (commandToken.endsWith("java")) { // commandToken = "^\"" + commandToken + "^\""; // } else if (commandToken.contains(" ")) { // commandToken = commandToken.replaceAll(" ", "^ "); // } // linearizedCommand += " " + commandToken; // } // osCommand.add("cmd"); // osCommand.add("/C"); // osCommand.add(linearizedCommand); // return osCommand; return roughCommand; } else { return roughCommand; } } protected abstract Collection getServerProperties(); protected abstract void setServerStartCommand(List command); private File getJavaExecutable() { File javaExec = new File(System.getProperty("java.home"), "bin" + File.separator + "java"); return javaExec; } protected abstract String getClassPath(); /** * @since 5.6 */ protected abstract String getShutdownClassPath(); protected Collection getNuxeoProperties() { ArrayList nuxeoProperties = new ArrayList(); nuxeoProperties.add(String.format("-D%s=%s", Environment.NUXEO_HOME, configurationGenerator.getNuxeoHome().getPath())); nuxeoProperties.add(String.format("-D%s=%s", ConfigurationGenerator.NUXEO_CONF, configurationGenerator.getNuxeoConf().getPath())); nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_LOG_DIR)); nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_DATA_DIR)); nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_TMP_DIR)); if (!DEFAULT_NUXEO_CONTEXT_PATH.equals(configurationGenerator.getUserConfig().getProperty( Environment.NUXEO_CONTEXT_PATH))) { nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_CONTEXT_PATH)); } if (overrideJavaTmpDir) { nuxeoProperties.add("-Djava.io.tmpdir=" + configurationGenerator.getUserConfig().getProperty( Environment.NUXEO_TMP_DIR)); } return nuxeoProperties; } private String getNuxeoProperty(String property) { return "-D" + property + "=" + configurationGenerator.getUserConfig().getProperty(property); } protected String addToClassPath(String cp, String filename) { File classPathEntry = new File(configurationGenerator.getNuxeoHome(), filename); if (!classPathEntry.exists()) { classPathEntry = new File(filename); } if (!classPathEntry.exists()) { throw new RuntimeException( "Tried to add inexistent classpath entry: " + filename); } cp += System.getProperty("path.separator") + classPathEntry.getPath(); return cp; } /** * @since 5.6 */ protected static void initParserOptions() { if (launcherOptions == null) { launcherOptions = new Options(); // help option OptionBuilder.withLongOpt(OPTION_HELP); OptionBuilder.withDescription(OPTION_HELP_DESC); launcherOptions.addOption(OptionBuilder.create("h")); // Quiet option OptionBuilder.withLongOpt(OPTION_QUIET); OptionBuilder.withDescription(OPTION_QUIET_DESC); launcherOptions.addOption(OptionBuilder.create("q")); // Debug option OptionBuilder.withLongOpt(OPTION_DEBUG); OptionBuilder.withDescription(OPTION_DEBUG_DESC); launcherOptions.addOption(OptionBuilder.create("d")); // Debug category option OptionBuilder.withDescription(OPTION_DEBUG_CATEGORY_DESC); OptionBuilder.hasArg(); launcherOptions.addOption(OptionBuilder.create(OPTION_DEBUG_CATEGORY)); OptionGroup outputOptions = new OptionGroup(); // Hide deprecation warnings option OptionBuilder.withLongOpt(OPTION_HIDE_DEPRECATION); OptionBuilder.withDescription(OPTION_HIDE_DEPRECATION_DESC); outputOptions.addOption(OptionBuilder.create()); // XML option OptionBuilder.withLongOpt(OPTION_XML); OptionBuilder.withDescription(OPTION_XML_DESC); outputOptions.addOption(OptionBuilder.create()); // JSON option OptionBuilder.withLongOpt(OPTION_JSON); OptionBuilder.withDescription(OPTION_JSON_DESC); outputOptions.addOption(OptionBuilder.create()); launcherOptions.addOptionGroup(outputOptions); // GUI option OptionBuilder.withLongOpt(OPTION_GUI); OptionBuilder.hasArg(); OptionBuilder.withArgName("true|false"); OptionBuilder.withDescription(OPTION_GUI_DESC); launcherOptions.addOption(OptionBuilder.create()); // Package management option OptionBuilder.withLongOpt(OPTION_NODEPS); OptionBuilder.withDescription(OPTION_NODEPS_DESC); launcherOptions.addOption(OptionBuilder.create()); // Relax on target platform option OptionBuilder.withLongOpt(OPTION_RELAX); OptionBuilder.hasArg(); OptionBuilder.withArgName("true|false|ask"); OptionBuilder.withDescription(OPTION_RELAX_DESC); launcherOptions.addOption(OptionBuilder.create()); // Accept option OptionBuilder.withLongOpt(OPTION_ACCEPT); OptionBuilder.hasArg(); OptionBuilder.withArgName("true|false|ask"); OptionBuilder.withDescription(OPTION_ACCEPT_DESC); launcherOptions.addOption(OptionBuilder.create()); } } /** * @since 5.6 */ protected static CommandLine parseOptions(String[] args) throws ParseException { initParserOptions(); CommandLineParser parser = new PosixParser(); CommandLine cmdLine = null; Boolean stopAfterParsing = true; try { cmdLine = parser.parse(launcherOptions, args); if (cmdLine.hasOption(OPTION_HELP) || cmdLine.getArgList().contains(OPTION_HELP)) { printLongHelp(); } else if (cmdLine.getArgList().isEmpty()) { printShortHelp(); } else { stopAfterParsing = false; } } catch (UnrecognizedOptionException e) { log.error(e.getMessage()); printShortHelp(); } catch (MissingArgumentException e) { log.error(e.getMessage()); printShortHelp(); } catch (ParseException e) { log.error("Error while parsing command line: " + e.getMessage()); printShortHelp(); } finally { if (stopAfterParsing) { throw new ParseException("Invalid command line"); } } return cmdLine; } public static void main(String[] args) { try { final NuxeoLauncher launcher = createLauncher(args); if (Arrays.asList(COMMANDS_NO_GUI).contains(launcher.command)) { launcher.useGui = false; } if (launcher.useGui && launcher.getGUI() == null) { launcher.setGUI(new NuxeoLauncherGUI(launcher)); } launch(launcher); } catch (ParseException e) { System.exit(1); } catch (Exception e) { log.error("Cannot execute command. " + e.getMessage()); log.debug(e, e); System.exit(1); } } /** * @since 5.5 * @param launcher * @param launcherGUI * @param command * @throws PackageException * @throws IOException */ public static void launch(final NuxeoLauncher launcher) throws IOException, PackageException { int exitStatus = EXIT_CODE_OK; boolean commandSucceeded = true; if (launcher.command == null) { return; } if (Arrays.asList(COMMANDS_NO_RUNNING_SERVER).contains(launcher.command)) { launcher.checkNoRunningServer(); } if ("status".equalsIgnoreCase(launcher.command)) { log.warn(launcher.status()); if (launcher.isStarted()) { log.info(launcher.getStartupSummary()); } exitStatus = launcher.status; } else if ("startbg".equalsIgnoreCase(launcher.command)) { commandSucceeded = launcher.doStart(); } else if ("start".equalsIgnoreCase(launcher.command)) { if (launcher.useGui) { launcher.getGUI().start(); } else { commandSucceeded = launcher.doStartAndWait(); } } else if ("console".equalsIgnoreCase(launcher.command)) { launcher.executor.execute(new Runnable() { @Override public void run() { launcher.addShutdownHook(); try { if (!launcher.doStart(true)) { launcher.removeShutdownHook(); System.exit(1); } } catch (PackageException e) { log.error( "Could not initialize the packaging subsystem", e); launcher.removeShutdownHook(); System.exit(EXIT_CODE_ERROR); } } }); } else if ("stop".equalsIgnoreCase(launcher.command)) { if (launcher.useGui) { launcher.getGUI().stop(); } else { launcher.stop(); } } else if ("restartbg".equalsIgnoreCase(launcher.command)) { launcher.stop(); commandSucceeded = launcher.doStart(); } else if ("restart".equalsIgnoreCase(launcher.command)) { launcher.stop(); commandSucceeded = launcher.doStartAndWait(); } else if ("wizard".equalsIgnoreCase(launcher.command)) { commandSucceeded = launcher.startWizard(); } else if ("configure".equalsIgnoreCase(launcher.command)) { try { launcher.configure(); } catch (ConfigurationException e) { commandSucceeded = false; launcher.errorValue = EXIT_CODE_NOT_CONFIGURED; log.error("Could not run configuration: " + e.getMessage()); log.debug(e, e); } } else if ("pack".equalsIgnoreCase(launcher.command)) { commandSucceeded = launcher.pack(); } else if ("mp-list".equalsIgnoreCase(launcher.command)) { launcher.pkgList(); } else if ("mp-listall".equalsIgnoreCase(launcher.command)) { launcher.pkgListAll(); } else if ("mp-init".equalsIgnoreCase(launcher.command)) { commandSucceeded = launcher.pkgInit(); } else if ("mp-purge".equalsIgnoreCase(launcher.command)) { commandSucceeded = launcher.pkgPurge(); } else if ("mp-add".equalsIgnoreCase(launcher.command)) { if (launcher.hasOption(OPTION_NODEPS)) { commandSucceeded = launcher.pkgAdd(params); } else { commandSucceeded = launcher.pkgRequest(Arrays.asList(params), null, null, null); } } else if ("mp-install".equalsIgnoreCase(launcher.command)) { if (launcher.hasOption(OPTION_NODEPS)) { commandSucceeded = launcher.pkgInstall(params); } else { commandSucceeded = launcher.pkgRequest(null, Arrays.asList(params), null, null); } } else if ("mp-uninstall".equalsIgnoreCase(launcher.command)) { if (launcher.hasOption(OPTION_NODEPS)) { commandSucceeded = launcher.pkgUninstall(params); } else { commandSucceeded = launcher.pkgRequest(null, null, Arrays.asList(params), null); } } else if ("mp-remove".equalsIgnoreCase(launcher.command)) { if (launcher.hasOption(OPTION_NODEPS)) { commandSucceeded = launcher.pkgRemove(params); } else { commandSucceeded = launcher.pkgRequest(null, null, null, Arrays.asList(params)); } } else if ("mp-request".equalsIgnoreCase(launcher.command)) { if (launcher.hasOption(OPTION_NODEPS)) { log.error("This command is not available with the --nodeps option"); commandSucceeded = false; } else { commandSucceeded = launcher.pkgCompoundRequest(Arrays.asList(params)); } } else if ("mp-hotfix".equalsIgnoreCase(launcher.command)) { commandSucceeded = launcher.pkgHotfix(); } else if ("mp-upgrade".equalsIgnoreCase(launcher.command)) { commandSucceeded = launcher.pkgUpgrade(); } else if ("mp-reset".equalsIgnoreCase(launcher.command)) { commandSucceeded = launcher.pkgReset(); } else if ("mp-update".equalsIgnoreCase(launcher.command)) { commandSucceeded = launcher.pkgRefreshCache(); } else if ("showconf".equalsIgnoreCase(launcher.command)) { commandSucceeded = launcher.showConfig(); } else if ("mp-show".equalsIgnoreCase(launcher.command)) { commandSucceeded = launcher.pkgShow(params); } else { printLongHelp(); commandSucceeded = false; } if (launcher.command.startsWith("mp-")) { launcher.printXMLOutput(); } if (!commandSucceeded) { if (!quiet && !debug) { log.error("\nSome commands failed:"); launcher.cset.log(); } exitStatus = launcher.errorValue; } if (debug) { log.debug("\nCommands debug dump:"); launcher.cset.log(true); } if (exitStatus != EXIT_CODE_OK) { System.exit(exitStatus); } } private boolean hasOption(String option) { return cmdLine.hasOption(option); } /** * Since 5.5 */ private boolean pack() { try { checkNoRunningServer(); configurationGenerator.setProperty(PARAM_UPDATECENTER_DISABLED, "true"); List startCommand = new ArrayList(); startCommand.add(getJavaExecutable().getPath()); startCommand.addAll(Arrays.asList(getJavaOptsProperty().split(" "))); startCommand.add("-cp"); String classpath = getClassPath(); classpath = addToClassPath(classpath, "bin" + File.separator + "nuxeo-launcher.jar"); classpath = getClassPath( classpath, configurationGenerator.getServerConfigurator().getServerLibDir()); classpath = getClassPath( classpath, configurationGenerator.getServerConfigurator().getNuxeoLibDir()); classpath = getClassPath( classpath, new File(configurationGenerator.getRuntimeHome(), "bundles")); startCommand.add(classpath); startCommand.addAll(getNuxeoProperties()); if (configurationGenerator.isJBoss) { startCommand.add(PACK_JBOSS_CLASS); } else if (configurationGenerator.isTomcat) { startCommand.add(PACK_TOMCAT_CLASS); } else { errorValue = EXIT_CODE_ERROR; return false; } startCommand.add(configurationGenerator.getRuntimeHome().getPath()); for (String param : params) { startCommand.add(param); } ProcessBuilder pb = new ProcessBuilder(getOSCommand(startCommand)); pb.directory(configurationGenerator.getNuxeoHome()); log.debug("Pack command: " + pb.command()); Process process = pb.start(); ArrayList sgArray = logProcessStreams( process, true); Thread.sleep(100); process.waitFor(); waitForProcessStreams(sgArray); } catch (IOException e) { errorValue = EXIT_CODE_ERROR; log.error("Could not start process", e); } catch (InterruptedException e) { errorValue = EXIT_CODE_ERROR; log.error("Could not start process", e); } catch (IllegalStateException e) { errorValue = EXIT_CODE_ERROR; log.error( "The server must not be running while running pack command", e); } catch (ConfigurationException e) { errorValue = EXIT_CODE_ERROR; log.error(e); } return errorValue == 0; } private boolean startWizard() throws PackageException { if (!configurationGenerator.getServerConfigurator().isWizardAvailable()) { log.error("Sorry, the wizard is not available within that server."); return false; } if (isRunning()) { log.error("Server already running. " + "Please stop it before calling \"wizard\" command " + "or use the Admin Center instead of the wizard."); return false; } if (reloadConfiguration) { configurationGenerator = new ConfigurationGenerator(quiet, debug); configurationGenerator.init(); reloadConfiguration = false; } configurationGenerator.getUserConfig().setProperty( ConfigurationGenerator.PARAM_WIZARD_DONE, "false"); return doStart(); } /** * @throws PackageException * @see #doStartAndWait(boolean) */ public boolean doStartAndWait() throws PackageException { return doStartAndWait(false); } /** * @see #stop(boolean) */ public void stop() { stop(false); } /** * Call {@link #doStart(boolean)} with false as parameter. * * @see #doStart(boolean) * @return true if the server started successfully * @throws PackageException */ public boolean doStart() throws PackageException { return doStart(false); } /** * Whereas {@link #doStart()} considers the server as started when the * process is running, {@link #doStartAndWait()} waits for effective start * by watching the logs * * @param logProcessOutput Must process output stream must be logged or not. * * @return true if the server started successfully * @throws PackageException */ public boolean doStartAndWait(boolean logProcessOutput) throws PackageException { boolean commandSucceeded = false; if (doStart(logProcessOutput)) { addShutdownHook(); try { if (configurationGenerator.isWizardRequired() || waitForEffectiveStart()) { commandSucceeded = true; } removeShutdownHook(); } catch (InterruptedException e) { // do nothing } } return commandSucceeded; } protected void removeShutdownHook() { try { Runtime.getRuntime().removeShutdownHook(shutdownHook); log.debug("Removed shutdown hook"); } catch (IllegalStateException e) { // the virtual machine is already in the process of shutting down } } /** * @return true if Nuxeo is ready * @throws InterruptedException */ protected boolean waitForEffectiveStart() throws InterruptedException { long startTime = new Date().getTime(); int startMaxWait = Integer.parseInt(configurationGenerator.getUserConfig().getProperty( START_MAX_WAIT_PARAM, getDefaultMaxWait())); log.debug("Will wait for effective start during " + startMaxWait + " seconds."); final StringBuilder startSummary = new StringBuilder(); final String newLine = System.getProperty("line.separator"); boolean isReady = false; long deltaTime = 0; // Wait for status servlet ready do { try { isReady = statusServletClient.init(); } catch (SocketTimeoutException e) { if (!quiet) { System.out.print("."); } } deltaTime = (new Date().getTime() - startTime) / 1000; } while (!isReady && deltaTime < startMaxWait && isRunning()); isReady = false; // Wait for effective start reported from status servlet do { isReady = isStarted(); if (!isReady) { if (!quiet) { System.out.print("."); } Thread.sleep(1000); } deltaTime = (new Date().getTime() - startTime) / 1000; } while (!isReady && deltaTime < startMaxWait && isRunning()); if (isReady) { startSummary.append(newLine + getStartupSummary()); long duration = (new Date().getTime() - startTime) / 1000; startSummary.append("Started in " + String.format("%dmin%02ds", new Long(duration / 60), new Long(duration % 60))); if (wasStartupFine()) { if (!quiet) { System.out.println(startSummary); } } else { System.err.println(startSummary); } return true; } else if (deltaTime >= startMaxWait) { if (!quiet) { System.out.println(); } log.error("Starting process is taking too long - giving up."); } errorValue = EXIT_CODE_ERROR; return false; } /** * Must be called after {@link #getStartupSummary()} * * @since 5.5 * @return last detected status of running Nuxeo server */ public boolean wasStartupFine() { return statusServletClient.isStartupFine(); } /** * @since 5.5 * @return Nuxeo startup summary * @throws SocketTimeoutException if Nuxeo server is not responding */ public String getStartupSummary() { try { return statusServletClient.getStartupSummary(); } catch (SocketTimeoutException e) { log.warn("Failed to contact Nuxeo for getting startup summary", e); return ""; } } /** * Starts the server in background. * * @return true if server successfully started * @throws PackageException */ public boolean doStart(boolean logProcessOutput) throws PackageException { errorValue = EXIT_CODE_OK; boolean serverStarted = false; try { if (reloadConfiguration) { configurationGenerator = new ConfigurationGenerator(quiet, debug); configurationGenerator.init(); } else { // Ensure reload on next start reloadConfiguration = true; } configure(); configurationGenerator.verifyInstallation(); if (configurationGenerator.isWizardRequired()) { if (!configurationGenerator.isForceGeneration()) { log.error("Cannot start setup wizard with " + ConfigurationGenerator.PARAM_FORCE_GENERATION + "=false. Either set it to true or once, either set " + ConfigurationGenerator.PARAM_WIZARD_DONE + "=true to skip the wizard."); errorValue = EXIT_CODE_NOT_CONFIGURED; return false; } String paramsStr = ""; for (String param : params) { paramsStr += " " + param; } System.setProperty( ConfigurationGenerator.PARAM_WIZARD_RESTART_PARAMS, paramsStr); configurationGenerator.prepareWizardStart(); } else { configurationGenerator.cleanupPostWizard(); } log.debug("Check if install in progress..."); if (configurationGenerator.isInstallInProgress()) { getConnectBroker().executePending( configurationGenerator.getInstallFile(), true, true); // configuration will be reloaded, keep wizard value System.setProperty( ConfigurationGenerator.PARAM_WIZARD_DONE, configurationGenerator.getUserConfig().getProperty( ConfigurationGenerator.PARAM_WIZARD_DONE, "true")); return doStart(logProcessOutput); } start(logProcessOutput); serverStarted = isRunning(); if (pid != null) { File pidFile = new File(configurationGenerator.getPidDir(), "nuxeo.pid"); FileWriter writer = new FileWriter(pidFile); writer.write(pid); writer.close(); } } catch (ConfigurationException e) { errorValue = EXIT_CODE_NOT_CONFIGURED; log.error("Could not run configuration: " + e.getMessage()); log.debug(e, e); } catch (IOException e) { errorValue = EXIT_CODE_ERROR; log.error("Could not start process: " + e.getMessage()); log.debug(e, e); } catch (InterruptedException e) { errorValue = EXIT_CODE_ERROR; log.error("Could not start process: " + e.getMessage()); log.debug(e, e); } catch (IllegalStateException e) { log.error(e.getMessage()); } return serverStarted; } /** * @since 5.6 */ protected void printXMLOutput() { try { JAXBContext jaxbContext = JAXBContext.newInstance( CommandSetInfo.class, CommandInfo.class, PackageInfo.class, MessageInfo.class); printXMLOutput(jaxbContext, cset); } catch (JAXBException e) { log.error("Output serialization failed: " + e.getMessage(), e); errorValue = EXIT_CODE_NOT_RUNNING; } } /** * @since 5.6 */ protected void printXMLOutput(JAXBContext jaxbContext, Object objectToOutput) { if (!xmlOutput) { return; } try { Writer xml = new StringWriter(); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(objectToOutput, xml); if (!jsonOutput) { System.out.println(xml.toString()); } else { try { System.out.println(XML.toJSONObject(xml.toString()).toString( 2)); } catch (JSONException e) { log.error(String.format( "XML to JSON conversion failed: %s\nOutput was:\n%s", e.getMessage(), xml.toString())); } } } catch (JAXBException e) { log.error("Output serialization failed: " + e.getMessage(), e); errorValue = EXIT_CODE_NOT_RUNNING; } } /** * Stop stream gobblers contained in the given ArrayList * * @throws InterruptedException * * @since 5.5 * @see #logProcessStreams(Process, boolean) */ public void waitForProcessStreams(ArrayList sgArray) { for (ThreadedStreamGobbler streamGobbler : sgArray) { try { streamGobbler.join(STREAM_MAX_WAIT); } catch (InterruptedException e) { streamGobbler.interrupt(); } } } /** * @since 5.5 * @param classpath * @param baseDir * @return classpath with all jar files in baseDir * @throws IOException */ protected String getClassPath(String classpath, File baseDir) throws IOException { File[] files = getFilename(baseDir, ".*"); for (File file : files) { classpath += System.getProperty("path.separator") + file.getPath(); } return classpath; } /** * @since 5.5 * @param baseDir * @param filePattern * @return filename matching filePattern in baseDir */ protected File[] getFilename(File baseDir, final String filePattern) { File[] files = baseDir.listFiles(new FilenameFilter() { @Override public boolean accept(File basedir, String filename) { return filename.matches(filePattern + "(-[0-9].*)?\\.jar"); } }); return files; } protected class ShutdownThread extends Thread { private NuxeoLauncher launcher; public ShutdownThread(NuxeoLauncher launcher) { super(); this.launcher = launcher; } public void run() { log.debug("Shutting down..."); if (launcher.isRunning()) { launcher.stop(); } log.debug("Shutdown complete."); } } protected void addShutdownHook() { log.debug("Add shutdown hook"); shutdownHook = new ShutdownThread(this); Runtime.getRuntime().addShutdownHook(shutdownHook); } /** * Stops the server. * * Will try to call specific class for a clean stop, retry * {@link #STOP_NB_TRY}, waiting {@link #STOP_SECONDS_BEFORE_NEXT_TRY} * between each try, then kill the process if still running. */ public void stop(boolean logProcessOutput) { long startTime = new Date().getTime(); long deltaTime; try { if (!isRunning()) { log.warn("Server is not running."); return; } if (!quiet) { System.out.print("\nStopping server..."); } int nbTry = 0; boolean retry = false; int stopMaxWait = Integer.parseInt(configurationGenerator.getUserConfig().getProperty( STOP_MAX_WAIT_PARAM, STOP_MAX_WAIT_DEFAULT)); do { List stopCommand = new ArrayList(); stopCommand.add(getJavaExecutable().getPath()); stopCommand.add("-cp"); stopCommand.add(getShutdownClassPath()); stopCommand.addAll(getNuxeoProperties()); stopCommand.addAll(getServerProperties()); setServerStopCommand(stopCommand); for (String param : params) { stopCommand.add(param); } ProcessBuilder pb = new ProcessBuilder( getOSCommand(stopCommand)); pb.directory(configurationGenerator.getNuxeoHome()); // pb = pb.redirectErrorStream(true); log.debug("Server command: " + pb.command()); try { Process stopProcess = pb.start(); ArrayList sgArray = logProcessStreams( stopProcess, logProcessOutput); stopProcess.waitFor(); waitForProcessStreams(sgArray); boolean wait = true; while (wait) { try { if (stopProcess.exitValue() == 0) { // Successful call for server stop retry = false; } else { // Failed to call for server stop retry = ++nbTry < STOP_NB_TRY; if (!quiet) { System.out.print("."); } Thread.sleep(STOP_SECONDS_BEFORE_NEXT_TRY * 1000); } wait = false; } catch (IllegalThreadStateException e) { // Stop call is still running wait = true; if (!quiet) { System.out.print("."); } Thread.sleep(1000); } } // Exit if there's no way to check for server stop if (processManager instanceof PureJavaProcessManager) { log.warn("Can't check server status on your OS."); return; } // Wait a few seconds for effective stop deltaTime = 0; do { if (!quiet) { System.out.print("."); } Thread.sleep(1000); deltaTime = (new Date().getTime() - startTime) / 1000; } while (!retry && getPid() != null && deltaTime < stopMaxWait); } catch (InterruptedException e) { log.error(e); } } while (retry); if (getPid() == null) { log.warn("Server stopped."); } else { log.info("No answer from server, try to kill process " + pid + "..."); processManager.kill(nuxeoProcess, pid); if (getPid() == null) { log.warn("Server forcibly stopped."); } } } catch (IOException e) { log.error("Could not manage process!", e); } } protected abstract void setServerStopCommand(List command); private String getPid() throws IOException { pid = processManager.findPid(processRegex); log.debug("regexp: " + processRegex + " pid:" + pid); return pid; } /** * Configure the server after checking installation * * @throws ConfigurationException If an installation error is detected or if * configuration fails */ public void configure() throws ConfigurationException { checkNoRunningServer(); configurationGenerator.checkJavaVersion(); configurationGenerator.run(); overrideJavaTmpDir = Boolean.parseBoolean(configurationGenerator.getUserConfig().getProperty( OVERRIDE_JAVA_TMPDIR_PARAM, "true")); } /** * @return Default max wait depending on server (ie JBoss takes much more * time than Tomcat) */ private String getDefaultMaxWait() { return configurationGenerator.isJBoss ? START_MAX_WAIT_JBOSS_DEFAULT : START_MAX_WAIT_DEFAULT; } /** * Return process status (running or not) as String, depending on OS * capability to manage processes. Set status value following * "http://refspecs.freestandards.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html" * * @see #status */ public String status() { if (processManager instanceof PureJavaProcessManager) { status = STATUS_CODE_UNKNOWN; return "Can't check server status on your OS."; } try { if (getPid() == null) { status = STATUS_CODE_OFF; return "Server is not running."; } else { status = STATUS_CODE_ON; return "Server is running with process ID " + getPid() + "."; } } catch (IOException e) { status = STATUS_CODE_UNKNOWN; return "Could not check existing process (" + e.getMessage() + ")."; } } /** * Last status value set by {@link #status()}. */ public int getStatus() { return status; } /** * Last error value set by any method. Exit code values are following the * Linux Standard Base Core Specification 4.1. */ public int getErrorValue() { return errorValue; } /** * @throws ParseException * @return a NuxeoLauncher instance specific to current server (JBoss, * Tomcat or Jetty). * @throws ConfigurationException If server cannot be identified * @since 5.5 */ public static NuxeoLauncher createLauncher(String[] args) throws ConfigurationException, ParseException { CommandLine cmdLine = parseOptions(args); // Common options to the Launcher and the ConfigurationGenerator if (cmdLine.hasOption(OPTION_QUIET) || cmdLine.hasOption(OPTION_XML) || cmdLine.hasOption(OPTION_JSON)) { setQuiet(); } if (cmdLine.hasOption(OPTION_DEBUG) || cmdLine.hasOption(OPTION_DEBUG_CATEGORY)) { setDebug(cmdLine.getOptionValue(OPTION_DEBUG_CATEGORY, "org.nuxeo.launcher")); } NuxeoLauncher launcher; ConfigurationGenerator cg = new ConfigurationGenerator(quiet, debug); if (cmdLine.hasOption(OPTION_HIDE_DEPRECATION)) { cg.hideDeprecationWarnings(true); } if (cg.isJBoss) { launcher = new NuxeoJBossLauncher(cg); } else if (cg.isJetty) { launcher = new NuxeoJettyLauncher(cg); } else if (cg.isTomcat) { launcher = new NuxeoTomcatLauncher(cg); } else { throw new ConfigurationException("Unknown server!"); } launcher.setArgs(cmdLine); return launcher; } /** * Sets from program arguments the launcher command and additional * parameters. * * @param cmdLine Program arguments; may be used by launcher implementation. * Must not be null or empty. */ private void setArgs(CommandLine cmdLine) { this.cmdLine = cmdLine; extractCommandAndParams(cmdLine.getArgs()); // Use GUI? if (cmdLine.hasOption(OPTION_GUI)) { useGui = Boolean.valueOf(cmdLine.getOptionValue(OPTION_GUI)); log.debug("GUI: " + cmdLine.getOptionValue(OPTION_GUI) + " -> " + new Boolean(useGui).toString()); } else if (OPTION_GUI.equalsIgnoreCase(command)) { useGui = true; // Shift params and extract command if there is one extractCommandAndParams(params); } else { if (PlatformUtils.isWindows()) { useGui = true; log.debug("GUI: option not set - platform is Windows -> start GUI"); } else { useGui = false; log.debug("GUI: option not set - platform is not Windows -> do not start GUI"); } } // Output format if (cmdLine.hasOption(OPTION_XML)) { setXMLOutput(); } if (cmdLine.hasOption(OPTION_JSON)) { setJSONOutput(); } } private void extractCommandAndParams(String[] args) { if (args.length > 0) { command = args[0]; log.debug("Launcher command: " + command); // Command parameters if (args.length > 1) { params = Arrays.copyOfRange(args, 1, args.length); if (log.isDebugEnabled()) { log.debug("Command parameters: " + ArrayUtils.toString(params)); } } else { params = new String[0]; } } else { command = null; } } /** * @param beQuiet if true, launcher will be in quiet mode * @since 5.5 */ protected static void setQuiet() { quiet = true; Log4JHelper.setQuiet(Log4JHelper.CONSOLE_APPENDER_NAME); } /** * @param categories Root categories to switch DEBUG on. * @since 5.6 */ protected static void setDebug(String categories) { setDebug(categories, true); } /** * @param categories Root categories to switch DEBUG on or off * @param activateDebug Set DEBUG on or off. * @since 5.6 */ protected static void setDebug(String categories, boolean activateDebug) { debug = activateDebug; Log4JHelper.setDebug(categories, activateDebug, true, new String[] { Log4JHelper.CONSOLE_APPENDER_NAME, "FILE" }); } /** * @param activateDebug if true, will activate the DEBUG logs * @since 5.5 */ protected static void setDebug(boolean activateDebug) { setDebug("org.nuxeo", activateDebug); } protected void setXMLOutput() { xmlOutput = true; } protected void setJSONOutput() { jsonOutput = true; setXMLOutput(); } public static void printShortHelp() { HelpFormatter help = new HelpFormatter(); help.setSyntaxPrefix("Usage: "); help.printHelp("nuxeoctl [options] [command parameters]", launcherOptions); } public static void printLongHelp() { printShortHelp(); log.error("\n\nJava usage:\n\tjava [-D" + JAVA_OPTS_PROPERTY + "=\"JVM options\"] [-D" + Environment.NUXEO_HOME + "=\"/path/to/nuxeo\"] [-D" + ConfigurationGenerator.NUXEO_CONF + "=\"/path/to/nuxeo.conf\"] [-Djvmcheck=nofail] -jar \"path/to/nuxeo-launcher.jar\"" + " \\ \n\t\t[options] [command parameters]"); log.error("\n\t" + JAVA_OPTS_PROPERTY + "\tParameters for the server JVM (default are " + JAVA_OPTS_DEFAULT + ")."); log.error("\t" + Environment.NUXEO_HOME + "\t\tNuxeo server root path (default is parent of called script)."); log.error("\t" + ConfigurationGenerator.NUXEO_CONF + "\t\tPath to nuxeo.conf file (default is $NUXEO_HOME/bin/nuxeo.conf)."); log.error("\tjvmcheck\t\tIf set to \"nofail\", ignore JVM version validation errors."); log.error("\n\nCommands list:"); log.error("\thelp\t\t\tPrint this message."); log.error("\tgui\t\t\tStart the user graphical interface."); log.error("\tstart\t\t\tStart Nuxeo server in background, waiting for effective start. " + "Useful for batch executions requiring the server being immediately available after the script returned."); log.error("\tstop\t\t\tStop any Nuxeo server started with the same nuxeo.conf file."); log.error("\trestart\t\t\tRestart Nuxeo server."); log.error("\tconfigure\t\tConfigure Nuxeo server with parameters from nuxeo.conf."); log.error("\twizard\t\t\tEnable the wizard (force the wizard to be played again in case the wizard configuration has already been done)."); log.error("\tconsole\t\t\tStart Nuxeo server in a console mode. Ctrl-C will stop it."); log.error("\tstatus\t\t\tPrint server status (running or not)."); log.error("\tstartbg\t\t\tStart Nuxeo server in background, without waiting for effective start. Useful for starting Nuxeo as a service."); log.error("\trestartbg\t\tRestart Nuxeo server with a call to \"startbg\" after \"stop\"."); log.error("\tpack \t\tBuild a static archive (the \"pack\" Shell script is deprecated)."); log.error("\tshowconf\t\tDisplay the instance configuration."); log.error("\tmp-list\t\t\tList local Marketplace packages."); log.error("\tmp-listall\t\tList all Marketplace packages (requires a registered instance)."); log.error("\tmp-init\t\t\tPre-cache Marketplace packages locally available in the distribution."); log.error("\tmp-update\t\tUpdate cache of marketplace packages list."); log.error("\tmp-add\t\t\tAdd Marketplace package(s) to local cache. You must provide the package file(s), name(s) or ID(s) as parameter."); log.error("\tmp-install\t\tRun Marketplace package installation. " + "It is automatically called at startup if {{installAfterRestart.log}} file exists in data directory. " + "Else you must provide the package file(s), name(s) or ID(s) as parameter."); log.error("\tmp-uninstall\t\tUninstall Marketplace package(s). " + "You must provide the package name(s) or ID(s) as parameter (see \"mp-list\" command)."); log.error("\tmp-request\t\tInstall + uninstall Marketplace package(s) in one command. " + "You must provide a *quoted* list of package names or IDs prefixed with + (install) or - (uninstall)."); log.error("\tmp-remove\t\tRemove Marketplace package(s). " + "You must provide the package name(s) or ID(s) as parameter (see \"mp-list\" command)."); log.error("\tmp-reset\t\tReset all packages to DOWNLOADED state. May be useful after a manual server upgrade."); log.error("\tmp-purge\t\tUninstall and remove all packages from the local cache."); log.error("\tmp-hotfix\t\tInstall all the available hotfixes for the current platform (requires a registered instance)."); log.error("\tmp-upgrade\t\tGet all the available upgrades for the Marketplace packages currently installed (requires a registered instance)."); log.error("\tmp-show\t\t\tShow Marketplace package(s) information. You must provide the package file(s), name(s) or ID(s) as parameter."); } /** * Work best with current nuxeoProcess. If nuxeoProcess is null or has * exited, then will try to get process ID (so, result in that case depends * on OS capabilities). * * @return true if current process is running or if a running PID is found */ public boolean isRunning() { if (nuxeoProcess != null) { try { nuxeoProcess.exitValue(); // Previous process has exited nuxeoProcess = null; } catch (IllegalThreadStateException exception) { return true; } } try { return (getPid() != null); } catch (IOException e) { log.error(e); return false; } } /** * @since 5.5 * @return true if Nuxeo finished starting */ public boolean isStarted() { boolean isStarted; if (configurationGenerator.isWizardRequired()) { isStarted = isRunning(); } else { try { isStarted = isRunning() && statusServletClient.isStarted(); } catch (SocketTimeoutException e) { isStarted = false; } } return isStarted; } /** * @return Server log file */ public File getLogFile() { return new File(configurationGenerator.getLogDir(), "server.log"); } /** * @return Server URL */ public String getURL() { return configurationGenerator.getUserConfig().getProperty( ConfigurationGenerator.PARAM_NUXEO_URL); } protected ConnectBroker getConnectBroker() throws IOException, PackageException { if (connectBroker == null) { connectBroker = new ConnectBroker(configurationGenerator.getEnv()); if (cmdLine.hasOption(OPTION_ACCEPT)) { connectBroker.setAccept(cmdLine.getOptionValue(OPTION_ACCEPT, ConnectBroker.OPTION_ACCEPT_DEFAULT)); } if (cmdLine.hasOption(OPTION_RELAX)) { connectBroker.setRelax(cmdLine.getOptionValue(OPTION_RELAX)); } cset = connectBroker.getCommandSet(); } return connectBroker; } /** * List all local packages. * * @throws IOException * @throws PackageException */ protected void pkgList() throws IOException, PackageException { getConnectBroker().listPending(configurationGenerator.getInstallFile()); getConnectBroker().pkgList(); } /** * List all packages including remote ones. * * @since 5.6 * @throws IOException * @throws PackageException */ protected void pkgListAll() throws IOException, PackageException { getConnectBroker().listPending(configurationGenerator.getInstallFile()); getConnectBroker().pkgListAll(); } protected boolean pkgAdd(String[] pkgNames) throws IOException, PackageException { boolean cmdOK = getConnectBroker().pkgAdd(Arrays.asList(pkgNames)); if (!cmdOK) { errorValue = EXIT_CODE_ERROR; } return cmdOK; } protected boolean pkgInstall(String[] pkgIDs) throws IOException, PackageException { boolean cmdOK = true; if (configurationGenerator.isInstallInProgress()) { cmdOK = getConnectBroker().executePending( configurationGenerator.getInstallFile(), true, !hasOption(OPTION_NODEPS)); } cmdOK = cmdOK && getConnectBroker().pkgInstall(Arrays.asList(pkgIDs)); if (!cmdOK) { errorValue = EXIT_CODE_ERROR; } return cmdOK; } protected boolean pkgUninstall(String[] pkgIDs) throws IOException, PackageException { boolean cmdOK = getConnectBroker().pkgUninstall(Arrays.asList(pkgIDs)); if (!cmdOK) { errorValue = EXIT_CODE_ERROR; } return cmdOK; } protected boolean pkgRemove(String[] pkgIDs) throws IOException, PackageException { boolean cmdOK = getConnectBroker().pkgRemove(Arrays.asList(pkgIDs)); if (!cmdOK) { errorValue = EXIT_CODE_ERROR; } return cmdOK; } protected boolean pkgReset() throws IOException, PackageException { boolean cmdOK = getConnectBroker().pkgReset(); if (!cmdOK) { errorValue = EXIT_CODE_ERROR; } return cmdOK; } /** * @since 5.6 */ protected void printInstanceXMLOutput(InstanceInfo instance) { try { JAXBContext jaxbContext = JAXBContext.newInstance( InstanceInfo.class, DistributionInfo.class, PackageInfo.class, ConfigurationInfo.class, KeyValueInfo.class); printXMLOutput(jaxbContext, instance); } catch (JAXBException e) { log.error("Output serialization failed: " + e.getMessage()); log.debug(e, e); errorValue = EXIT_CODE_NOT_RUNNING; } } /** * @throws PackageException * @throws IOException * @since 5.6 */ protected boolean showConfig() throws IOException, PackageException { InstanceInfo nxInstance = new InstanceInfo(); log.info("***** Nuxeo instance configuration *****"); nxInstance.NUXEO_CONF = configurationGenerator.getNuxeoConf().getPath(); log.info("NUXEO_CONF: " + nxInstance.NUXEO_CONF); nxInstance.NUXEO_HOME = configurationGenerator.getNuxeoHome().getPath(); log.info("NUXEO_HOME: " + nxInstance.NUXEO_HOME); // CLID try { nxInstance.clid = getConnectBroker().getCLID(); log.info("Instance CLID: " + nxInstance.clid); } catch (NoCLID e) { // leave nxInstance.clid unset } catch (Exception e) { // something went wrong in the NuxeoConnectClient initialization errorValue = EXIT_CODE_UNAUTHORIZED; log.error("Could not initialize NuxeoConnectClient", e); return false; } // distribution.properties DistributionInfo nxDistrib; File distFile = new File(configurationGenerator.getConfigDir(), "distribution.properties"); if (!distFile.exists()) { // fallback in the file in templates distFile = new File(configurationGenerator.getNuxeoHome(), "templates"); distFile = new File(distFile, "common"); distFile = new File(distFile, "config"); distFile = new File(distFile, "distribution.properties"); } try { nxDistrib = new DistributionInfo(distFile); } catch (IOException e) { nxDistrib = new DistributionInfo(); } nxInstance.distribution = nxDistrib; log.info("** Distribution"); log.info("- name: " + nxDistrib.name); log.info("- server: " + nxDistrib.server); log.info("- version: " + nxDistrib.version); log.info("- date: " + nxDistrib.date); log.info("- packaging: " + nxDistrib.packaging); // packages List pkgs = getConnectBroker().getPkgList(); log.info("** Packages:"); List pkgTemplates = new ArrayList(); for (LocalPackage pkg : pkgs) { nxInstance.packages.add(new PackageInfo(pkg)); log.info(String.format("- %s (version: %s - id: %s - state: %s)", pkg.getName(), pkg.getVersion(), pkg.getId(), PackageState.getByValue(pkg.getState()).getLabel())); // store template(s) added by this package try { File installFile = pkg.getInstallFile(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document dom = db.parse(installFile); NodeList nodes = dom.getDocumentElement().getElementsByTagName( "config"); for (int i = 0; i < nodes.getLength(); i++) { Element node = (Element) nodes.item(i); if (node.hasAttribute("addtemplate")) { pkgTemplates.add(node.getAttribute("addtemplate")); } } } catch (Exception e) { log.warn("Could not parse install file for " + pkg.getName(), e); } } // nuxeo.conf ConfigurationInfo nxConfig = new ConfigurationInfo(); nxConfig.dbtemplate = configurationGenerator.extractDatabaseTemplateName(); log.info("** Templates:"); log.info("Database template: " + nxConfig.dbtemplate); String userTemplates = configurationGenerator.getUserTemplates(); StringTokenizer st = new StringTokenizer(userTemplates, ","); while (st.hasMoreTokens()) { String template = st.nextToken(); if (template.equals(nxConfig.dbtemplate)) { continue; } if (pkgTemplates.contains(template)) { nxConfig.pkgtemplates.add(template); log.info("Package template: " + template); } else { File testBase = new File(configurationGenerator.getNuxeoHome(), ConfigurationGenerator.TEMPLATES + File.separator + template); if (testBase.exists()) { nxConfig.basetemplates.add(template); log.info("Base template: " + template); } else { nxConfig.usertemplates.add(template); log.info("User template: " + template); } } } log.info("** Settings from nuxeo.conf:"); Properties userConfig = configurationGenerator.getUserConfig(); @SuppressWarnings("rawtypes") Enumeration nxConfEnum = userConfig.keys(); while (nxConfEnum.hasMoreElements()) { String key = (String) nxConfEnum.nextElement(); String value = userConfig.getProperty(key); if (key.equals("JAVA_OPTS")) { value = getJavaOptsProperty(); } KeyValueInfo kv = new KeyValueInfo(key, value); nxConfig.keyvals.add(kv); if (!key.contains("password") && !key.equals(ConfigurationGenerator.PARAM_STATUS_KEY)) { log.info(key + "=" + value); } else { log.info(key + "=********"); } } nxInstance.config = nxConfig; log.info("****************************************"); printInstanceXMLOutput(nxInstance); return true; } /** * @since 5.6 * @param pkgsToAdd * @param pkgsToInstall * @param pkgsToUninstall * @param pkgsToRemove * @return true if request execution was fine * @throws IOException * @throws PackageException */ protected boolean pkgRequest(List pkgsToAdd, List pkgsToInstall, List pkgsToUninstall, List pkgsToRemove) throws IOException, PackageException { boolean cmdOK = true; if (configurationGenerator.isInstallInProgress()) { cmdOK = getConnectBroker().executePending( configurationGenerator.getInstallFile(), true, true); } cmdOK = cmdOK && getConnectBroker().pkgRequest(pkgsToAdd, pkgsToInstall, pkgsToUninstall, pkgsToRemove); if (!cmdOK) { errorValue = EXIT_CODE_ERROR; } return cmdOK; } /** * Update the cached list of remote packages * * @since 5.6 * @return true * @throws IOException * @throws PackageException */ protected boolean pkgRefreshCache() throws IOException, PackageException { getConnectBroker().refreshCache(); return true; } /** * Add packages from the distribution to the local cache * * @throws PackageException * @throws IOException * @since 5.6 * */ protected boolean pkgInit() throws IOException, PackageException { return getConnectBroker().addDistributionPackages(); } /** * Uninstall and remove all packages from the local cache * * @return * * @throws PackageException * @throws IOException * @since 5.6 * */ protected boolean pkgPurge() throws PackageException, IOException { return getConnectBroker().pkgPurge(); } /** * Install the hotfixes available for the instance * * @return * * @throws PackageException * @throws IOException * @since 5.6 */ protected boolean pkgHotfix() throws IOException, PackageException { return getConnectBroker().pkgHotfix(); } /** * Upgrade the marketplace packages (addons) available for the instance * * @return * * @throws PackageException * @throws IOException * @since 5.6 */ protected boolean pkgUpgrade() throws IOException, PackageException { return getConnectBroker().pkgUpgrade(); } /** * Combined install/uninstall request * * @param request Space separated list of package names or IDs prefixed with * + (install) or - (uninstall) * @throws IOException * @throws PackageException * @since 5.6 */ protected boolean pkgCompoundRequest(List request) throws IOException, PackageException { List add = new ArrayList(); List install = new ArrayList(); List uninstall = new ArrayList(); for (String param : request) { for (String subparam : param.split("[ ,]")) { if (subparam.charAt(0) == '-') { uninstall.add(subparam.substring(1)); } else if (subparam.charAt(0) == '+') { install.add(subparam.substring(1)); } else { add.add(subparam); } } } return pkgRequest(add, install, uninstall, null); } /** * dpkg-like command which returns package location, version, dependencies, * conflicts, ... * * @param packages List of packages identified by their ID, name or local * filename. * * @return false if unable to show package information. * @throws PackageException * @throws IOException * @since 5.7 */ protected boolean pkgShow(String[] packages) throws IOException, PackageException { boolean cmdOK = getConnectBroker().pkgShow(Arrays.asList(packages)); if (!cmdOK) { errorValue = EXIT_CODE_ERROR; } return cmdOK; } }