/*
* (C) Copyright 2010-2016 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* Julien Carsique
* Florent Guillaume
* Ronan DANIELLOU
*/
package org.nuxeo.launcher;
import static org.nuxeo.launcher.connect.ConnectRegistrationBroker.REGISTRATION_COMPANY;
import static org.nuxeo.launcher.connect.ConnectRegistrationBroker.REGISTRATION_COMPANY_REGEX;
import static org.nuxeo.launcher.connect.ConnectRegistrationBroker.REGISTRATION_DESCRIPTION;
import static org.nuxeo.launcher.connect.ConnectRegistrationBroker.REGISTRATION_EMAIL;
import static org.nuxeo.launcher.connect.ConnectRegistrationBroker.REGISTRATION_PASSWORD;
import static org.nuxeo.launcher.connect.ConnectRegistrationBroker.REGISTRATION_PASSWORDND;
import static org.nuxeo.launcher.connect.ConnectRegistrationBroker.REGISTRATION_PROJECT;
import static org.nuxeo.launcher.connect.ConnectRegistrationBroker.REGISTRATION_PROJECT_REGEX;
import static org.nuxeo.launcher.connect.ConnectRegistrationBroker.REGISTRATION_TERMSNCONDITIONS;
import java.io.Console;
import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.SocketTimeoutException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.GZIPOutputStream;
import javax.json.Json;
import javax.json.stream.JsonGenerator;
import javax.validation.constraints.NotNull;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
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.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.impl.SimpleLog;
import org.apache.commons.validator.routines.EmailValidator;
import org.nuxeo.common.Environment;
import org.nuxeo.common.codec.Crypto;
import org.nuxeo.common.codec.CryptoProperties;
import org.nuxeo.connect.connector.NuxeoClientInstanceType;
import org.nuxeo.connect.data.ConnectProject;
import org.nuxeo.connect.identity.LogicalInstanceIdentifier.NoCLID;
import org.nuxeo.connect.registration.RegistrationException;
import org.nuxeo.connect.tools.report.client.ReportConnector;
import org.nuxeo.connect.update.PackageException;
import org.nuxeo.connect.update.Version;
import org.nuxeo.launcher.config.ConfigurationException;
import org.nuxeo.launcher.config.ConfigurationGenerator;
import org.nuxeo.launcher.connect.ConnectBroker;
import org.nuxeo.launcher.connect.ConnectRegistrationBroker;
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.launcher.process.MacProcessManager;
import org.nuxeo.launcher.process.ProcessManager;
import org.nuxeo.launcher.process.PureJavaProcessManager;
import org.nuxeo.launcher.process.SolarisProcessManager;
import org.nuxeo.launcher.process.UnixProcessManager;
import org.nuxeo.launcher.process.WindowsProcessManager;
import org.nuxeo.log4j.Log4JHelper;
import org.nuxeo.log4j.ThreadedStreamGobbler;
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.json.impl.writer.JsonXmlStreamWriter;
/**
* @author jcarsique
* @since 5.4.2
*/
public abstract class NuxeoLauncher {
/**
* @since 7.4
*/
protected static final String OUTPUT_UNSET_VALUE = "<unset>";
/**
* @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 = "Start graphical user interface (default is true on Windows and false on other platforms).";
/**
* @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.\n"
+ "<categories>: comma-separated Java categories to debug (default: \"org.nuxeo.launcher\").";
/**
* @since 7.4
*/
private static final String OPTION_DEBUG_CATEGORY_ARG_NAME = "categories";
/**
* @since 5.6
*/
protected static final String OPTION_DEBUG_CATEGORY = "dc";
private static final String OPTION_DEBUG_CATEGORY_DESC = "Deprecated: see categories on '--debug' option.";
/**
* @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 + ").\n"
+ "In non interactive mode, '--accept=true' also sets '--relax=true' if needed.";
/**
* @since 5.9.1
*/
protected static final String OPTION_SNAPSHOT = "snapshot";
private static final String OPTION_SNAPSHOT_DESC = "Allow use of SNAPSHOT Nuxeo Packages.\n"
+ "This option is implicit:\n" //
+ "\t- on SNAPSHOT distributions (daily builds),\n"
+ "\t- if the command explicitly requests a SNAPSHOT package.";
/**
* @since 5.9.1
*/
protected static final String OPTION_FORCE = "force";
private static final String OPTION_FORCE_DESC = "Deprecated: use '--strict' option instead.";
/**
* @since 7.4
*/
protected static final String OPTION_STRICT = "strict";
private static final String OPTION_STRICT_DESC = "Abort in error the start command when a component cannot "
+ "be activated or if a server is already running.";
/**
* @since 5.6
*/
protected static final String OPTION_HIDE_DEPRECATION = "hide-deprecation-warnings";
protected static final String OPTION_HIDE_DEPRECATION_DESC = "Hide deprecation warnings.";
/**
* @since 6.0
*/
protected static final String OPTION_IGNORE_MISSING = "ignore-missing";
protected static final String OPTION_IGNORE_MISSING_DESC = "Ignore unknown packages on mp-add, mp-install and mp-set commands.";
/**
* @since 6.0
*/
protected static final String OPTION_CLID = "clid";
private static final String OPTION_CLID_DESC = "Use the provided instance CLID file";
/**
* @since 7.4
*/
protected static final String OPTION_ENCRYPT = "encrypt";
private static final String OPTION_ENCRYPT_ARG_NAME = "algorithm";
private static final String OPTION_ENCRYPT_DESC = String.format(
"Activate key value symmetric encryption.\n"
+ "The algorithm can be configured: <%s> is a cipher transformation of the form: \"algorithm/mode/padding\" or \"algorithm\".\n"
+ "Default value is \"%s\" (Advanced Encryption Standard, Electronic Cookbook Mode, PKCS5-style padding).",
OPTION_ENCRYPT, Crypto.DEFAULT_ALGO);
/**
* @since 7.4
*/
protected static final String OPTION_SET = "set";
private static final String OPTION_SET_ARG_NAME = "template";
private static final String OPTION_SET_DESC = String.format(
"Set the value for a given key.\n"
+ "The value is stored in {{%s}} by default unless a template name is provided; if so, it is then stored in the template's {{%s}} file.\n"
+ "If the value is empty (''), then the property is unset.\n"
+ "This option is implicit if no '--get' or '--get-regexp' option is used and there are exactly two parameters (key value).",
ConfigurationGenerator.NUXEO_CONF, ConfigurationGenerator.NUXEO_DEFAULT_CONF);
/**
* @since 7.4
*/
protected static final String OPTION_GET = "get";
private static final String OPTION_GET_DESC = "Get the value for a given key. Returns error code 6 if the key was not found.\n"
+ "This option is implicit if '--set' option is not used and there are more or less than two parameters.";
/**
* @since 7.4
*/
protected static final String OPTION_GET_REGEXP = "get-regexp";
private static final String OPTION_GET_REGEXP_DESC = "Get the value for all keys matching the given regular expression(s).";
/**
* @since 8.3
*/
protected static final String OPTION_GZIP_OUTPUT = "gzip";
private static final String OPTION_GZIP_DESC = "Compress the output.";
/**
* @since 8.3
*/
protected static final String OPTION_OUTPUT = "output";
private static final String OPTION_OUTPUT_DESC = "Write output in specified file.";
/**
* @since 8.3
*/
protected static final String OPTION_PRETTY_PRINT = "pretty-print";
private static final String OPTION_PRETTY_PRINT_DESC = "Pretty print the output.";
// 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 options = initParserOptions();;
private static final String JAVA_OPTS_PROPERTY = "launcher.java.opts";
private static final String JAVA_OPTS_DEFAULT = "-Xms512m -Xmx1024m";
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";
/**
* 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_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", "mp-set", "config", "encrypt", "decrypt", OPTION_HELP,
"register", "register-trial", "connect-report" };
private static final String[] COMMANDS_NO_RUNNING_SERVER = { "pack", "mp-init", "mp-purge", "mp-add", "mp-install",
"mp-uninstall", "mp-request", "mp-remove", "mp-hotfix", "mp-upgrade", "mp-reset", "mp-update", "mp-set" };
/**
* @since 7.4
*/
protected boolean commandRequiresNoRunningServer() {
return Arrays.asList(COMMANDS_NO_RUNNING_SERVER).contains(command);
}
/**
* @since 7.4
*/
protected boolean commandRequiresNoGUI() {
return Arrays.asList(COMMANDS_NO_GUI).contains(command);
}
/**
* 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;
private static final String OPTION_HELP_DESC_ENV = "\nENVIRONMENT VARIABLES\n"
+ " NUXEO_HOME\t\tPath to server root directory.\n" //
+ " NUXEO_CONF\t\tPath to {{nuxeo.conf}} file.\n"
+ " PATH\n"
+ "\tJAVA\t\t\tPath to the {{java}} executable.\n"
+ " JAVA_HOME\t\tPath to the Java home directory. Can also be defined in {{nuxeo.conf}}.\n"
+ " JAVA_OPTS\t\tOptional values passed to the JVM. Can also be defined in {{nuxeo.conf}}.\n"
+ " REQUIRED_JAVA_VERSION\tNuxeo requirement on Java version.\n" //
+ "\nJAVA USAGE\n"
+ String.format(" java [-D%s=\"JVM options\"]"
+ " [-D%s=\"/path/to/nuxeo\"] [-D%s=\"/path/to/nuxeo.conf\"]"
+ " [-Djvmcheck=nofail] -jar \"path/to/nuxeo-launcher.jar\" \\\n"
+ " \t[options] <command> [command parameters]\n\n", JAVA_OPTS_PROPERTY,
Environment.NUXEO_HOME, ConfigurationGenerator.NUXEO_CONF)
+ String.format(" %s\tParameters for the server JVM (default are %s).\n", JAVA_OPTS_PROPERTY,
JAVA_OPTS_DEFAULT)
+ String.format(" %s\t\tNuxeo server root path (default is parent of called script).\n",
Environment.NUXEO_HOME)
+ String.format(" %s\t\tPath to {{%1$s}} file (default is \"$NUXEO_HOME/bin/%1$s\").\n",
ConfigurationGenerator.NUXEO_CONF)
+ " jvmcheck\t\tIf set to \"nofail\", ignore JVM version validation errors.\n";
private static final String OPTION_HELP_DESC_COMMANDS = "\nCOMMANDS\n"
+ " help\t\t\tPrint this message.\n"
+ " gui\t\t\tDeprecated: use '--gui' option instead.\n"
+ " start\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.\n"
+ " stop\t\t\tStop any Nuxeo server started with the same {{nuxeo.conf}} file.\n"
+ " restart\t\t\tRestart Nuxeo server.\n"
+ " config\t\t\tGet and set template or global parameters.\n"
+ " encrypt\t\t\tOutput encrypted value for a given parameter.\n"
+ " decrypt\t\t\tOutput decrypted value for a given parameter.\n"
+ " configure\t\tConfigure Nuxeo server with parameters from {{nuxeo.conf}}.\n"
+ " wizard\t\t\tStart the wizard.\n"
+ " console\t\t\tStart Nuxeo server in a console mode. Ctrl-C will stop it.\n"
+ " status\t\t\tPrint server running status.\n"
+ " startbg\t\t\tStart Nuxeo server in background, without waiting for effective start. Useful for starting Nuxeo as a service.\n"
+ " restartbg\t\tRestart Nuxeo server with a call to \"startbg\" after \"stop\".\n"
+ " pack\t\t\tBuild a static archive.\n"
+ " showconf\t\tDisplay the instance configuration.\n"
+ " connect-report\t\tDump a JSON report about the running server (which being used by Nuxeo support).\n"
+ " mp-list\t\t\tList local Nuxeo Packages.\n"
+ " mp-listall\t\tList all Nuxeo Packages.\n"
+ " mp-init\t\t\tPre-cache Nuxeo Packages locally available in the distribution.\n"
+ " mp-update\t\tUpdate cache of Nuxeo Packages list.\n"
+ " mp-add\t\t\tAdd Nuxeo Package(s) to local cache. You must provide the package file(s), name(s) or ID(s) as parameter.\n"
+ " mp-install\t\tRun Nuxeo 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.\n"
+ " mp-uninstall\t\tUninstall Nuxeo Package(s). You must provide the package name(s) or ID(s) as parameter (see \"mp-list\" command).\n"
+ " mp-remove\t\tRemove Nuxeo Package(s) from the local cache. You must provide the package name(s) or ID(s) as parameter (see \"mp-list\" command).\n"
+ " mp-reset\t\tReset all packages to DOWNLOADED state. May be useful after a manual server upgrade.\n"
+ " mp-set\t\t\tInstall a list of Nuxeo Packages and remove those not in the list.\n"
+ " mp-request\t\tInstall and uninstall Nuxeo Package(s) in one command. You must provide a *quoted* list of package names or IDs prefixed with + (install) or - (uninstall).\n"
+ " mp-purge\t\tUninstall and remove all packages from the local cache.\n"
+ " mp-hotfix\t\tInstall all the available hotfixes for the current platform but do not upgrade already installed ones (requires a registered instance).\n"
+ " mp-upgrade\t\tGet all the available upgrades for the Nuxeo Packages currently installed (requires a registered instance).\n"
+ " mp-show\t\t\tShow Nuxeo Package(s) information. You must provide the package file(s), name(s) or ID(s) as parameter.\n"
+ " register\t\tRegister your instance with an existing Connect account. You must provide the credentials, the project name or ID, its type and a description.\n"
+ " register-trial\t\tRegister your instance with a new trial Connect account. You must provide an email and a password as credentials, the company name, a project name and a description.\n"
+ "\nThe following commands are always executed in console/headless mode (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\", \"mp-set\", \"config\", \"encrypt\", \"decrypt\", \"help\".\n"
+ "\nThe following commands cannot be executed on a running server: \"pack\", \"mp-init\", \"mp-purge\", "
+ "\"mp-add\", \"mp-install\", \"mp-uninstall\", \"mp-request\", \"mp-remove\", \"mp-hotfix\", \"mp-upgrade\", "
+ "\"mp-reset\".\n"
+ "\nThe following commands can only be executed on a running server: \"connect-report\"\n"
+ "\nCommand parameters may need to be prefixed with '--' to separate them from option arguments when confusion arises.";
private static final String OPTION_HELP_USAGE = " nuxeoctl <command> [options] [--] [command parameters]\n\n";
private static final String OPTION_HELP_HEADER = "SYNOPSIS\n"
+ " nuxeoctl encrypt [--encrypt <algorithm>] [<clearValue>..] [-d [<categories>]|-q]\n"
+ " Output encrypted value for <clearValue>.\n"
+ " If <clearValue> is not provided, it is read from stdin.\n\n"
+ " nuxeoctl decrypt '<cryptedValue>'.. [-d [<categories>]|-q]\n" //
+ " Output decrypted value for <cryptedValue>. The secret key is read from stdin.\n\n"
+ " nuxeoctl config [<key> <value>].. <key> [<value>] [--encrypt [<algorithm>]] [--set [<template>]] [-d [<categories>]|-q]\n"
+ " Set template or global parameters.\n"
+ " If <value> is not provided and the --set 'option' is used, then the value is read from stdin.\n\n"
+ " nuxeoctl config [--get] <key>.. [-d [<categories>]|-q]\n"
+ " Get value for the given key(s).\n\n"
+ " nuxeoctl config [--get-regexp] <regexp>.. [-d [<categories>]|-q]\n"
+ " Get value for the keys matching the given regular expression(s).\n\n"
+ " nuxeoctl help|status|showconf [-d [<categories>]|-q]\n\n"
+ " nuxeoctl configure [-d [<categories>]|-q|-hdw]\n\n"
+ " nuxeoctl wizard [-d [<categories>]|-q|--clid <arg>|--gui <true|false|yes|no>]\n\n"
+ " nuxeoctl stop [-d [<categories>]|-q|--gui <true|false|yes|no>]\n\n"
+ " nuxeoctl start|restart|console|startbg|restartbg [-d [<categories>]|-q|--clid <arg>|--gui <true|false|yes|no>|--strict|-hdw]\n\n"
+ " nuxeoctl mp-show [command parameters] [-d [<categories>]|-q|--clid <arg>|--xml|--json]\n\n"
+ " nuxeoctl mp-list|mp-listall|mp-init|mp-update [command parameters] [-d [<categories>]|-q|--clid <arg>|--xml|--json]\n\n"
+ " nuxeoctl mp-reset|mp-purge|mp-hotfix|mp-upgrade [command parameters] [-d [<categories>]|-q|--clid <arg>|--xml|--json|--accept <true|false|yes|no|ask>]\n\n"
+ " nuxeoctl mp-add|mp-install|mp-uninstall|mp-remove|mp-set|mp-request [command parameters] [-d [<categories>]|-q|--clid <arg>|--xml|--json|--nodeps|--relax <true|false|yes|no|ask>|--accept <true|false|yes|no|ask>|-s|-im]\n\n"
+ " nuxeoctl register [<username> [<project> [<type> <description>] [<pwd>]]]\n\n"
+ " nuxeoctl register-trial [<email> <company> <project> <description> [<pwd>]]\n\n"
+ " nuxeoctl pack <target> [-d [<categories>]|-q]\n\n" //
+ " nuxeoctl connect-report [--output <file>|--gzip <*true|false|yes|no>|--pretty-print <true|*false|yes|no>]\n\n"
+ "OPTIONS";
private static final String OPTION_HELP_FOOTER =
"\nSee online documentation \"ADMINDOC/nuxeoctl and Control Panel Usage\": https://doc.nuxeo.com/x/FwNc";
private static final int PAGE_SIZE = 20;
public static final String CONNECT_TC_URL = "https://www.nuxeo.com/legal/nuxeo-trial-terms-conditions";
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 String[] params;
protected String command;
public String getCommand() {
return command;
}
/**
* @since 7.4
*/
public boolean commandIs(String aCommand) {
return StringUtils.equalsIgnoreCase(command, aCommand);
}
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 static boolean strict = false;
private boolean xmlOutput = false;
private boolean jsonOutput = false;
private ConnectBroker connectBroker = null;
private String clid = null;
private ConnectRegistrationBroker connectRegistrationBroker = null;
private InstanceInfo info;
CommandLine cmdLine;
private boolean ignoreMissing = false;
/**
* @since 5.5
* @return true if quiet mode is active
*/
public boolean isQuiet() {
return quiet;
}
private static Map<String, NuxeoLauncherGUI> 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(Environment.SERVER_STATUS_KEY));
processManager = getOSProcessManager();
processRegex = "^(?!/bin/sh).*" + Pattern.quote(configurationGenerator.getNuxeoConf().getPath()) + ".*"
+ Pattern.quote(getServerPrint()) + ".*$";
// Set OS-specific decorations
if (SystemUtils.IS_OS_MAC) {
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "NuxeoCtl");
}
}
private ProcessManager getOSProcessManager() {
if (SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_AIX) {
UnixProcessManager unixProcessManager = new UnixProcessManager();
return unixProcessManager;
} else if (SystemUtils.IS_OS_MAC) {
return new MacProcessManager();
} else if (SystemUtils.IS_OS_SUN_OS) {
return new SolarisProcessManager();
} else if (SystemUtils.IS_OS_WINDOWS) {
WindowsProcessManager windowsProcessManager = new WindowsProcessManager();
return windowsProcessManager.isUsable() ? windowsProcessManager : new PureJavaProcessManager();
} else {
return new PureJavaProcessManager();
}
}
/**
* 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<String> startCommand = new ArrayList<>();
startCommand.add(getJavaExecutable().getPath());
startCommand.addAll(getJavaOptsProperty(Function.identity()));
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());
log.debug("Server command: " + pb.command());
nuxeoProcess = pb.start();
Thread.sleep(1000);
boolean processExited = false;
// Check if process exited early
if (nuxeoProcess == null) {
log.error(String.format("Server start failed with command: %s", pb.command()));
if (SystemUtils.IS_OS_WINDOWS && configurationGenerator.getNuxeoHome().getPath().contains(" ")) {
// NXP-17679
log.error("The server path must not contain spaces under Windows.");
}
return;
}
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 List<String> getJavaOptsProperty(Function<String,String> mapper) {
return Arrays
.stream(System.getProperty(JAVA_OPTS_PROPERTY, JAVA_OPTS_DEFAULT)
.split("[ ]+(?=([^\"]*\"[^\"]*\")*[^\"]*$)"))
.map(opt -> StrSubstitutor.replace(opt, configurationGenerator.getUserConfig()))
.map(mapper)
.collect(Collectors.toList());
}
/**
* 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<ThreadedStreamGobbler> logProcessStreams(Process process, boolean logProcessOutput) {
ArrayList<ThreadedStreamGobbler> 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<String> getOSCommand(List<String> roughCommand) {
if (SystemUtils.IS_OS_UNIX) {
return getUnixCommand(roughCommand);
}
if (SystemUtils.IS_OS_WINDOWS) {
return getWindowsCommand(roughCommand);
}
throw new IllegalStateException("Unkown os, can't launch server");
}
private List<String> getWindowsCommand(List<String> roughCommand) {
ArrayList<String> osCommand = new ArrayList<>();
for (String commandToken : roughCommand) {
if (StringUtils.isBlank(commandToken)) {
continue;
}
osCommand.add("\"" + commandToken + "\"");
}
return osCommand;
}
private List<String> getUnixCommand(List<String> roughCommand) {
ArrayList<String> osCommand = new ArrayList<>();
String linearizedCommand = new String();
for (String commandToken : roughCommand) {
if (StringUtils.isBlank(commandToken)) {
continue;
}
if (commandToken.contains(" ")) {
commandToken = commandToken.replaceAll(" ", "\\\\ ");
}
linearizedCommand += " " + commandToken;
}
osCommand.add("/bin/sh");
osCommand.add("-c");
osCommand.add(linearizedCommand);
return osCommand;
}
protected abstract Collection<? extends String> getServerProperties();
protected abstract void setServerStartCommand(List<String> 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<? extends String> getNuxeoProperties() {
ArrayList<String> 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));
nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_MP_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 Options initParserOptions() {
Options options = new Options();
// help option
options.addOption(Option.builder("h").longOpt(OPTION_HELP).desc(OPTION_HELP_DESC).build());
// Quiet option
options.addOption(Option.builder("q").longOpt(OPTION_QUIET).desc(OPTION_QUIET_DESC).build());
{ // Debug options (mutually exclusive)
OptionGroup debugOptions = new OptionGroup();
// Debug option
debugOptions.addOption(Option.builder("d")
.longOpt(OPTION_DEBUG)
.desc(OPTION_DEBUG_DESC)
.hasArgs()
.argName(OPTION_DEBUG_CATEGORY_ARG_NAME)
.optionalArg(true)
.valueSeparator(',')
.build());
// Debug category option
debugOptions.addOption(Option.builder(OPTION_DEBUG_CATEGORY)
.desc(OPTION_DEBUG_CATEGORY_DESC)
.hasArgs()
.argName(OPTION_DEBUG_CATEGORY_ARG_NAME)
.optionalArg(true)
.valueSeparator(',')
.build());
options.addOptionGroup(debugOptions);
}
// For help output purpose only: that option is managed and
// swallowed by the nuxeoctl Shell script
options.addOption(Option.builder()
.longOpt("debug-launcher")
.desc("Linux-only. Activate Java debugging mode on the Launcher.")
.build());
// Instance CLID option
options.addOption(Option.builder().longOpt(OPTION_CLID).desc(OPTION_CLID_DESC).hasArg().build());
{ // Output options (mutually exclusive)
OptionGroup outputOptions = new OptionGroup();
// XML option
outputOptions.addOption(Option.builder().longOpt(OPTION_XML).desc(OPTION_XML_DESC).build());
// JSON option
outputOptions.addOption(Option.builder().longOpt(OPTION_JSON).desc(OPTION_JSON_DESC).build());
options.addOptionGroup(outputOptions);
}
// GUI option
options.addOption(Option.builder()
.longOpt(OPTION_GUI)
.desc(OPTION_GUI_DESC)
.hasArg()
.argName("true|false|yes|no")
.build());
// Package management option
options.addOption(Option.builder().longOpt(OPTION_NODEPS).desc(OPTION_NODEPS_DESC).build());
// Relax on target platform option
options.addOption(Option.builder()
.longOpt(OPTION_RELAX)
.desc(OPTION_RELAX_DESC)
.hasArg()
.argName("true|false|yes|no|ask")
.build());
// Accept option
options.addOption(Option.builder()
.longOpt(OPTION_ACCEPT)
.desc(OPTION_ACCEPT_DESC)
.hasArg()
.argName("true|false|yes|no|ask")
.build());
// Allow SNAPSHOT option
options.addOption(Option.builder("s").longOpt(OPTION_SNAPSHOT).desc(OPTION_SNAPSHOT_DESC).build());
// Force option
options.addOption(Option.builder("f").longOpt(OPTION_FORCE).desc(OPTION_FORCE_DESC).build());
// Strict option
options.addOption(Option.builder().longOpt(OPTION_STRICT).desc(OPTION_STRICT_DESC).build());
// Ignore missing option
options.addOption(Option.builder("im")
.longOpt(OPTION_IGNORE_MISSING)
.desc(OPTION_IGNORE_MISSING_DESC)
.build());
// Hide deprecation warnings option
options.addOption(Option.builder("hdw")
.longOpt(OPTION_HIDE_DEPRECATION)
.desc(OPTION_HIDE_DEPRECATION_DESC)
.build());
// Encrypt option
options.addOption(Option.builder()
.longOpt(OPTION_ENCRYPT)
.desc(OPTION_ENCRYPT_DESC)
.hasArg()
.argName(OPTION_ENCRYPT_ARG_NAME)
.optionalArg(true)
.build());
// Output options
options.addOption(Option.builder()
.longOpt(OPTION_GZIP_OUTPUT)
.desc(OPTION_GZIP_DESC)
.hasArg()
.argName("true|false")
.optionalArg(true)
.build());
options.addOption(Option.builder()
.longOpt(OPTION_PRETTY_PRINT)
.desc(OPTION_PRETTY_PRINT_DESC)
.hasArg()
.argName("true|false")
.optionalArg(true)
.build());
options.addOption(Option.builder()
.longOpt(OPTION_OUTPUT)
.desc(OPTION_OUTPUT_DESC)
.hasArg()
.argName("file")
.optionalArg(true)
.build());
{ // Config options (mutually exclusive)
OptionGroup configOptions = new OptionGroup();
// Set option
configOptions.addOption(Option.builder()
.longOpt(OPTION_SET)
.desc(OPTION_SET_DESC)
.hasArg()
.argName(OPTION_SET_ARG_NAME)
.optionalArg(true)
.build());
configOptions.addOption(Option.builder()
.longOpt(OPTION_GET)
.desc(OPTION_GET_DESC)
.build());
configOptions.addOption(Option.builder()
.longOpt(OPTION_GET_REGEXP)
.desc(OPTION_GET_REGEXP_DESC)
.build());
options.addOptionGroup(configOptions);
}
return options;
}
/**
* @since 5.6
*/
protected static CommandLine parseOptions(String[] args) throws ParseException {
CommandLineParser parser = new DefaultParser();
CommandLine cmdLine = null;
cmdLine = parser.parse(options, args);
if (cmdLine.hasOption(OPTION_HELP)) {
cmdLine.getArgList().add(OPTION_HELP);
setQuiet();
} else if (cmdLine.getArgList().isEmpty()) {
throw new ParseException("Missing command.");
}
// 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)) {
setDebug(cmdLine.getOptionValues(OPTION_DEBUG), "org.nuxeo.launcher");
}
if (cmdLine.hasOption(OPTION_DEBUG_CATEGORY)) {
setDebug(cmdLine.getOptionValues(OPTION_DEBUG_CATEGORY), "org.nuxeo.launcher");
}
if (cmdLine.hasOption(OPTION_FORCE) || cmdLine.hasOption(OPTION_STRICT)) {
setStrict(true);
}
return cmdLine;
}
public static void main(String[] args) {
NuxeoLauncher launcher = null;
try {
launcher = createLauncher(args);
if (launcher.commandRequiresNoGUI()) {
launcher.useGui = false;
}
if (launcher.useGui && launcher.getGUI() == null) {
launcher.setGUI(new NuxeoLauncherGUI(launcher));
}
launch(launcher);
} catch (ParseException e) {
log.error("Invalid command line. " + e.getMessage());
log.debug(e, e);
printShortHelp();
System.exit(launcher == null || launcher.errorValue == EXIT_CODE_OK ? EXIT_CODE_INVALID
: launcher.errorValue);
} catch (IOException | PackageException | ConfigurationException | GeneralSecurityException e) {
log.error(e.getMessage());
log.debug(e, e);
System.exit(launcher == null || launcher.errorValue == EXIT_CODE_OK ? EXIT_CODE_INVALID
: launcher.errorValue);
} catch (Exception e) {
log.error("Cannot execute command. " + e.getMessage(), e);
log.debug(e, e);
System.exit(EXIT_CODE_ERROR);
}
}
/**
* @since 5.5
* @param launcher
* @throws PackageException
* @throws IOException
* @throws ConfigurationException
* @throws ParseException
* @throws GeneralSecurityException
*/
public static void launch(final NuxeoLauncher launcher) throws IOException, PackageException,
ConfigurationException, ParseException, GeneralSecurityException {
boolean commandSucceeded = true;
if (launcher.commandIs(null)) {
return;
}
if (launcher.commandRequiresNoRunningServer()) {
launcher.checkNoRunningServer();
}
if (launcher.commandIs(OPTION_HELP)) {
printLongHelp();
} else if (launcher.commandIs("status")) {
String statusMsg = launcher.status();
launcher.errorValue = launcher.getStatus();
if (!quiet) {
log.warn(statusMsg);
if (launcher.isStarted()) {
log.info("Go to " + launcher.getURL());
log.info(launcher.getStartupSummary());
}
}
} else if (launcher.commandIs("startbg")) {
commandSucceeded = launcher.doStart();
} else if (launcher.commandIs("start")) {
if (launcher.useGui) {
launcher.getGUI().start();
} else {
commandSucceeded = launcher.doStartAndWait();
}
} else if (launcher.commandIs("console")) {
launcher.executor.execute(new Runnable() {
@Override
public void run() {
launcher.addShutdownHook();
try {
if (!launcher.doStart(true)) {
launcher.removeShutdownHook();
System.exit(1);
} else if (!quiet) {
log.info("Go to " + launcher.getURL());
}
} catch (PackageException e) {
log.error("Could not initialize the packaging subsystem", e);
launcher.removeShutdownHook();
System.exit(EXIT_CODE_ERROR);
}
}
});
} else if (launcher.commandIs("stop")) {
if (launcher.useGui) {
launcher.getGUI().stop();
} else {
launcher.stop();
}
} else if (launcher.commandIs("restartbg")) {
launcher.stop();
commandSucceeded = launcher.doStart();
} else if (launcher.commandIs("restart")) {
launcher.stop();
commandSucceeded = launcher.doStartAndWait();
} else if (launcher.commandIs("wizard")) {
commandSucceeded = launcher.startWizard();
} else if (launcher.commandIs("configure")) {
launcher.configure();
} else if (launcher.commandIs("pack")) {
launcher.pack();
} else if (launcher.commandIs("mp-list")) {
launcher.pkgList();
} else if (launcher.commandIs("mp-listall")) {
launcher.pkgListAll();
} else if (launcher.commandIs("mp-init")) {
commandSucceeded = launcher.pkgInit();
} else if (launcher.commandIs("mp-purge")) {
commandSucceeded = launcher.pkgPurge();
} else if (launcher.commandIs("mp-add")) {
if (launcher.cmdLine.hasOption(OPTION_NODEPS)) {
commandSucceeded = launcher.pkgAdd(launcher.params);
} else {
commandSucceeded = launcher.pkgRequest(Arrays.asList(launcher.params), null, null, null);
}
} else if (launcher.commandIs("mp-install")) {
if (launcher.cmdLine.hasOption(OPTION_NODEPS)) {
commandSucceeded = launcher.pkgInstall(launcher.params);
} else {
commandSucceeded = launcher.pkgRequest(null, Arrays.asList(launcher.params), null, null);
}
} else if (launcher.commandIs("mp-uninstall")) {
if (launcher.cmdLine.hasOption(OPTION_NODEPS)) {
commandSucceeded = launcher.pkgUninstall(launcher.params);
} else {
commandSucceeded = launcher.pkgRequest(null, null, Arrays.asList(launcher.params), null);
}
} else if (launcher.commandIs("mp-remove")) {
if (launcher.cmdLine.hasOption(OPTION_NODEPS)) {
commandSucceeded = launcher.pkgRemove(launcher.params);
} else {
commandSucceeded = launcher.pkgRequest(null, null, null, Arrays.asList(launcher.params));
}
} else if (launcher.commandIs("mp-request")) {
if (launcher.cmdLine.hasOption(OPTION_NODEPS)) {
throw new ParseException("The command mp-request is not available with the --nodeps option");
} else {
commandSucceeded = launcher.pkgCompoundRequest(Arrays.asList(launcher.params));
}
} else if (launcher.commandIs("mp-set")) {
commandSucceeded = launcher.pkgSetRequest(Arrays.asList(launcher.params),
launcher.cmdLine.hasOption(OPTION_NODEPS));
} else if (launcher.commandIs("mp-hotfix")) {
commandSucceeded = launcher.pkgHotfix();
} else if (launcher.commandIs("mp-upgrade")) {
commandSucceeded = launcher.pkgUpgrade();
} else if (launcher.commandIs("mp-reset")) {
commandSucceeded = launcher.pkgReset();
} else if (launcher.commandIs("mp-update")) {
commandSucceeded = launcher.pkgRefreshCache();
} else if (launcher.commandIs("showconf")) {
launcher.showConfig();
} else if (launcher.commandIs("mp-show")) {
commandSucceeded = launcher.pkgShow(launcher.params);
} else if (launcher.commandIs("encrypt")) {
launcher.encrypt();
} else if (launcher.commandIs("decrypt")) {
launcher.decrypt();
} else if (launcher.commandIs("config")) {
launcher.config();
} else if (launcher.commandIs("register")) {
commandSucceeded = launcher.registerRemoteInstance();
} else if (launcher.commandIs("register-trial")) {
commandSucceeded = launcher.registerTrial();
} else if (launcher.commandIs("connect-report")) {
boolean gzip = Boolean.valueOf(launcher.cmdLine.getOptionValue(OPTION_GZIP_OUTPUT, "true")).booleanValue();
boolean prettyprinting = Boolean.valueOf(launcher.cmdLine.getOptionValue(OPTION_PRETTY_PRINT, "false"));
Path outputpath;
if (launcher.cmdLine.hasOption(OPTION_OUTPUT)) {
outputpath = Paths.get(launcher.cmdLine.getOptionValue(OPTION_OUTPUT));
} else {
Path dir = Paths.get(launcher.configurationGenerator.getUserConfig().getProperty(Environment.NUXEO_TMP_DIR));
outputpath = dir.resolve("nuxeo-connect-tools-report.json".concat(
gzip ? ".gz" : ""));
}
log.info("Dumping connect report in " + outputpath);
try (OutputStream output = openOutput(outputpath, gzip)) {
commandSucceeded = launcher.dumpConnectReport(output,prettyprinting);
}
} else {
log.error("Unknown command " + launcher.command);
printLongHelp();
launcher.errorValue = EXIT_CODE_INVALID;
}
if (launcher.xmlOutput && launcher.command.startsWith("mp-")) {
launcher.printXMLOutput();
}
commandSucceeded = commandSucceeded && launcher.errorValue == EXIT_CODE_OK;
if (!commandSucceeded && !quiet || debug) {
launcher.cset.log(commandSucceeded && debug);
}
if (!commandSucceeded) {
System.exit(launcher.errorValue);
}
}
protected static OutputStream openOutput(Path path, boolean gzip) throws IOException {
OutputStream output = Files.newOutputStream(path, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
if (gzip) {
output = new GZIPOutputStream(output);
}
return output;
}
/**
* Prompts for a valid email address according to RFC 822 standards. The
* remote service may apply stricter constraints on email validation such as
* some black listed domains.
*
* @return the user input. Never null.
* @throws ConfigurationException
* If the user input is read from stdin and is {@code null} or
* does not match the {@code regex}
* @since 8.3
*/
public String promptEmail() throws IOException, ConfigurationException {
EmailValidator validator = EmailValidator.getInstance();
final String message = "Email Address: ";
final String error = "Invalid email address.";
return prompt(message, validator::isValid, error);
}
/**
* @since 8.3
*/
public String promptDescription() throws ConfigurationException, IOException {
return prompt("Description: ", null, null);
}
/**
* Prompt for a value read from the console or stdin.
*
* @param message
* message to display at prompt
* @param predicate
* a predicate that must match a correct user input. Ignored if
* {@code null}.
* @param error
* an error message to display or raise when the user input is
* {@code null} or does not match the {@code regex}
* @return the user input. Never null.
* @throws ConfigurationException
* If the user input is read from stdin and is {@code null} or
* does not match the {@code regex}
* @since 8.3
*/
public String prompt(String message, Predicate<String> predicate, String error) throws IOException,
ConfigurationException {
boolean doRegexMatch = predicate != null;
String value;
Console console = System.console();
if (console != null) {
value = console.readLine(message);
while (value == null || doRegexMatch && !predicate.test(value)) {
console.printf(error + "\n", value);
value = console.readLine(message);
}
} else { // try reading from stdin
value = IOUtils.toString(System.in);
if (value == null || doRegexMatch && !predicate.test(value)) {
throw new ConfigurationException(error);
}
}
return value;
}
/**
* @param message
* message to display at prompt
* @since 8.3
*/
public char[] promptPassword(String message) throws IOException {
Console console = System.console();
if (console != null) {
return console.readPassword(message);
} else { // try reading from stdin
return IOUtils.toCharArray(System.in);
}
}
/**
* @param confirmation
* if true, password is asked twice.
* @since 8.3
*/
public char[] promptPassword(boolean confirmation) throws IOException, ConfigurationException {
char[] pwd = promptPassword("Please enter your password: ");
if (confirmation) {
char[] pwdVerification = promptPassword("Please re-enter your password: ");
if (!Arrays.equals(pwd, pwdVerification)) {
throw new ConfigurationException("Passwords do not match.");
}
}
return pwd;
}
/**
* @return a {@link NuxeoClientInstanceType}. Never {@code null}.
* @since 8.3
*/
public NuxeoClientInstanceType promptInstanceType() throws IOException, ConfigurationException {
NuxeoClientInstanceType type;
Console console = System.console();
if (console == null) {
String typeStr = IOUtils.toString(System.in);
type = NuxeoClientInstanceType.fromString(typeStr);
if (type == null) {
throw new ConfigurationException("Unknown type: " + typeStr);
}
return type;
}
do {
String s = console.readLine("Instance type (dev|preprod|prod): [dev] ");
if (StringUtils.isBlank(s)) {
type = NuxeoClientInstanceType.DEV;
} else {
type = NuxeoClientInstanceType.fromString(s);
}
} while (type == null);
return type;
}
/**
* @since 8.3
*/
public boolean promptAcceptTerms() {
Console console = System.console();
if (console != null) {
String terms = console.readLine("Read and accept the Nuxeo Trial Terms and Conditions - " + CONNECT_TC_URL
+ " (yes/no)? [yes] ");
if (StringUtils.isEmpty(terms)) {
terms = "yes";
}
terms = terms.trim().toLowerCase();
return "y".equalsIgnoreCase(terms) || "yes".equalsIgnoreCase(terms);
} else {
log.info("Read Nuxeo Trial Terms and Conditions - " + CONNECT_TC_URL);
return true;
}
}
/**
* @param projects
* available projects the user must choose one amongst.
* @return a project. Never null.
* @throws ConfigurationException
* If {@code projects} is empty or if there is not such a
* project named as the parameter read from stdin.
* @since 8.3
*/
public ConnectProject promptProject(@NotNull List<ConnectProject> projects) throws ConfigurationException,
IOException, PackageException {
if (projects.isEmpty()) {
throw new ConfigurationException("You don't have access to any project.");
}
if (projects.size() == 1) {
return projects.get(0);
}
String projectName;
Console console = System.console();
if (console == null) {
projectName = IOUtils.toString(System.in);
ConnectProject project = getConnectRegistrationBroker().getProjectByName(projectName, projects);
if (project == null) {
throw new ConfigurationException("Unknown project: " + projectName);
}
return project;
}
System.out.println("Available projects:");
int i = 0;
boolean hasNextPage = true;
while (true) {
if (i > 0 && !SystemUtils.IS_OS_WINDOWS) {
// Remove last line to only have projects
System.out.print("\33[1A\33[2K");
}
int fromIndex = i * PAGE_SIZE;
int toIndex = (i + 1) * PAGE_SIZE;
if (toIndex >= projects.size()) {
toIndex = projects.size();
hasNextPage = false;
}
projects.subList(fromIndex, toIndex).forEach(
project -> System.out.println("\t- " + project.getSymbolicName()));
if (toIndex < projects.size()) {
int pageLeft = (int) Math.ceil((projects.size() - (i * PAGE_SIZE)) / PAGE_SIZE);
System.out.print(String.format("Project name (press Enter for next page; %d pages left): ", pageLeft));
} else {
System.out.print("Project name: ");
}
if (hasNextPage) {
i++;
}
projectName = console.readLine();
if (StringUtils.isNotEmpty(projectName)) {
ConnectProject project = getConnectRegistrationBroker().getProjectByName(projectName, projects);
if (project != null) {
return project;
}
System.err.println("Unknown project: " + projectName);
i = 0;
hasNextPage = true;
}
}
}
/**
* Register the instance, generating the CLID.
*
* <pre>
* {@code
* nuxeoctl register [<username> [<project> [<type> <description>] [pwd]]]
* 0/1 param: [<username>]
* 2/3 params: <username> <project> [pwd]
* 4/5 params: <username> <project> <type> <description> [pwd]
* }
* </pre>
*
* Missing parameters are read from stdin.
*
* @return true if succeed
* @since 8.3
*/
public boolean registerRemoteInstance() throws IOException, ConfigurationException, PackageException {
if (params.length > 5) {
throw new ConfigurationException("Wrong number of arguments.");
}
String username;
if (params.length > 0) {
username = params[0];
} else {
username = prompt("Username: ", s -> StringUtils.isNotBlank(s), "Username cannot be empty.");
}
char[] password;
if (params.length == 3 || params.length == 5) {
password = params[params.length - 1].toCharArray();
} else {
password = promptPassword(false);
}
ConnectProject project;
List<ConnectProject> projs = getConnectRegistrationBroker().getAvailableProjects(username, password);
if (params.length > 1) {
String projectName = params[1];
project = getConnectRegistrationBroker().getProjectByName(projectName, projs);
if (project == null) {
throw new ConfigurationException("Unknown project: " + projectName);
}
} else {
project = promptProject(projs);
}
NuxeoClientInstanceType type;
String description;
if (params.length > 3) {
type = NuxeoClientInstanceType.fromString(params[2]);
if (type == null) {
throw new ConfigurationException("Unknown type: " + params[2]);
}
description = params[3];
} else {
type = promptInstanceType();
description = promptDescription();
}
getConnectRegistrationBroker().registerRemote(username, password, project.getUuid(), type, description);
log.info(String.format("Server registered to %s for project %s\nType: %s\nDescription: %s", username, project,
type, description));
return true;
}
/**
* @since 8.3
*/
public boolean registerTrial() throws IOException, ConfigurationException, PackageException {
CommandInfo commandInfo = cset.newCommandInfo("register-trial");
if (params.length != 0 && params.length != 4 && params.length != 5) {
throw new ConfigurationException("Wrong number of arguments.");
}
Map<String, String> registration = new HashMap<>();
if (params.length > 3) {
String email = params[0];
if (!EmailValidator.getInstance().isValid(email)) {
throw new ConfigurationException("Invalid email address.");
}
registration.put(REGISTRATION_EMAIL, email);
String company = params[1];
if (!Pattern.matches(REGISTRATION_COMPANY_REGEX, company)) {
throw new ConfigurationException("Company field is mandatory.");
}
registration.put(REGISTRATION_COMPANY, company);
String projectName = params[2];
if (!Pattern.matches(REGISTRATION_PROJECT_REGEX, projectName)) {
throw new ConfigurationException("Project name can only contain alphanumeric characters and dashes.");
}
registration.put(REGISTRATION_PROJECT, projectName);
registration.put(REGISTRATION_DESCRIPTION, params[3]);
} else {
registration.put(REGISTRATION_EMAIL, promptEmail());
registration.put(REGISTRATION_COMPANY,
prompt("Company: ", s -> s.matches(REGISTRATION_COMPANY_REGEX), "Company field is mandatory."));
registration.put(
REGISTRATION_PROJECT,
prompt("Project name: ", s -> s.matches(REGISTRATION_PROJECT_REGEX),
"Project name can only contain alphanumeric characters and dashes."));
registration.put("description", promptDescription());
}
if (params.length == 5) {
registration.put(REGISTRATION_PASSWORD, params[4]);
registration.put(REGISTRATION_PASSWORDND, params[4]);
} else {
// TODO NXP-19258 remove password_verif
char[] pwd = promptPassword(true);
registration.put(REGISTRATION_PASSWORD, new String(pwd));
registration.put(REGISTRATION_PASSWORDND, new String(pwd));
}
if (!promptAcceptTerms()) {
log.error("You must accept the Nuxeo Trial Terms and Conditions to register your instance.");
errorValue = EXIT_CODE_INVALID;
commandInfo.exitCode = 1;
return false;
}
registration.put(REGISTRATION_TERMSNCONDITIONS, "true");
try {
getConnectRegistrationBroker().registerTrial(registration);
} catch (RegistrationException e) {
commandInfo.newMessage(e);
e.getErrors().forEach(err -> commandInfo.newMessage(SimpleLog.LOG_LEVEL_ERROR, err.getMessage()));
errorValue = EXIT_CODE_NOT_CONFIGURED;
commandInfo.exitCode = 1;
return false;
}
log.info(String.format(
"Trial registered to %s for project %s\nDescription: %s\n"
+ "Please ensure you have validated your registration with the confirmation email before starting the server.",
registration.get(REGISTRATION_EMAIL), registration.get(REGISTRATION_PROJECT),
registration.get(REGISTRATION_DESCRIPTION)));
return true;
}
/**
* @throws ConfigurationException
* @throws GeneralSecurityException
* @since 7.4
*/
protected void encrypt() throws ConfigurationException, GeneralSecurityException {
Crypto crypto = configurationGenerator.getCrypto();
String algorithm = cmdLine.getOptionValue(OPTION_ENCRYPT, null);
if (params.length == 0) {
Console console = System.console();
String encryptedString;
if (console != null) {
encryptedString = crypto.encrypt(algorithm,
Crypto.getBytes(console.readPassword("Please enter the value to encrypt: ")));
} else { // try reading from stdin
try {
encryptedString = crypto.encrypt(algorithm, IOUtils.toByteArray(System.in));
} catch (IOException e) {
log.debug(e, e);
errorValue = EXIT_CODE_ERROR;
return;
}
}
System.out.println(encryptedString);
} else {
for (String strToEncrypt : params) {
String encryptedString = crypto.encrypt(algorithm, strToEncrypt.getBytes());
System.out.println(encryptedString);
}
}
}
/**
* @throws ConfigurationException
* @since 7.4
*/
protected void decrypt() throws ConfigurationException {
Crypto crypto = configurationGenerator.getCrypto();
boolean validKey = false;
Console console = System.console();
if (console != null) {
validKey = crypto.verifyKey(console.readPassword("Please enter the secret key: "));
} else { // try reading from stdin
try {
validKey = crypto.verifyKey(IOUtils.toByteArray(System.in));
} catch (IOException e) {
log.debug(e, e);
errorValue = EXIT_CODE_ERROR;
return;
}
}
if (!validKey) {
errorValue = EXIT_CODE_INVALID;
return;
}
for (String strToDecrypt : params) {
System.out.println(Crypto.getChars(crypto.decrypt(strToDecrypt)));
}
}
/**
* @throws ConfigurationException
* @throws IOException
* @throws GeneralSecurityException
* @since 7.4
*/
protected void config() throws ConfigurationException, IOException, GeneralSecurityException {
if (cmdLine.hasOption(OPTION_SET) || !cmdLine.hasOption(OPTION_GET) && !cmdLine.hasOption(OPTION_GET_REGEXP)
&& params.length == 2) {
setConfigProperties();
} else { // OPTION_GET || OPTION_GET_REGEXP || !OPTION_SET &&
// params.length != 2
getConfigProperties();
}
}
/**
* @since 7.4
*/
protected void getConfigProperties() {
boolean isRegexp = cmdLine.hasOption(OPTION_GET_REGEXP);
CryptoProperties userConfig = configurationGenerator.getUserConfig();
List<String> keys;
if (isRegexp) {
keys = new ArrayList<>();
for (Object key : userConfig.keySet()) {
for (String param : params) {
Pattern pattern = Pattern.compile(param, Pattern.CASE_INSENSITIVE);
if (pattern.matcher((String) key).find()) {
keys.add((String) key);
}
}
}
if (keys.isEmpty()) {
errorValue = EXIT_CODE_NOT_CONFIGURED;
}
} else {
keys = Arrays.asList(params);
}
Crypto crypto = userConfig.getCrypto();
boolean keyChecked = false; // Secret key is asked only once
boolean raw = true;
StringBuilder sb = new StringBuilder();
final String newLine = System.getProperty("line.separator");
for (String key : keys) {
String value = userConfig.getProperty(key, raw);
if (value == null) {
errorValue = EXIT_CODE_NOT_CONFIGURED;
sb.append(OUTPUT_UNSET_VALUE + newLine);
} else {
if (raw && !keyChecked && Crypto.isEncrypted(value)) {
keyChecked = true;
boolean validKey;
Console console = System.console();
if (console != null) {
validKey = crypto.verifyKey(console.readPassword("Please enter the secret key: "));
} else { // try reading from stdin
try {
validKey = crypto.verifyKey(IOUtils.toByteArray(System.in));
} catch (IOException e) {
log.debug(e, e);
errorValue = EXIT_CODE_ERROR;
return;
}
}
if (validKey) {
raw = false;
value = new String(crypto.decrypt(value));
} else {
errorValue = EXIT_CODE_INVALID;
}
}
if (isRegexp) {
sb.append(key + "=");
}
sb.append(value + newLine);
}
}
System.out.print(sb.toString());
}
/**
* @throws IOException
* @throws GeneralSecurityException
* @since 7.4
*/
protected void setConfigProperties() throws ConfigurationException, IOException, GeneralSecurityException {
Crypto crypto = configurationGenerator.getCrypto();
boolean doEncrypt = cmdLine.hasOption(OPTION_ENCRYPT);
String algorithm = cmdLine.getOptionValue(OPTION_ENCRYPT, null);
Map<String, String> changedParameters = new HashMap<>();
for (Iterator<String> iterator = Arrays.asList(params).iterator(); iterator.hasNext();) {
String key = iterator.next();
String value;
if (iterator.hasNext()) {
value = iterator.next();
if (doEncrypt) {
value = crypto.encrypt(algorithm, value.getBytes());
} else if (Environment.CRYPT_KEY.equals(key) || Environment.CRYPT_KEYSTORE_PASS.equals(key)) {
value = Base64.encodeBase64String(value.getBytes());
}
} else {
Console console = System.console();
if (console != null) {
final String fmt = "Please enter the value for %s: ";
if (doEncrypt) {
value = crypto.encrypt(algorithm, Crypto.getBytes(console.readPassword(fmt, key)));
} else if (Environment.CRYPT_KEY.equals(key) || Environment.CRYPT_KEYSTORE_PASS.equals(key)) {
value = Base64.encodeBase64String(Crypto.getBytes(console.readPassword(fmt, key)));
} else {
value = console.readLine(fmt, key);
}
} else { // try reading from stdin
try {
if (doEncrypt) {
value = crypto.encrypt(algorithm, IOUtils.toByteArray(System.in));
} else if (Environment.CRYPT_KEY.equals(key) || Environment.CRYPT_KEYSTORE_PASS.equals(key)) {
value = Base64.encodeBase64String(IOUtils.toByteArray(System.in));
} else {
value = IOUtils.toString(System.in);
}
} catch (IOException e) {
log.debug(e, e);
errorValue = EXIT_CODE_ERROR;
return;
}
}
}
changedParameters.put(key, value);
}
String template = cmdLine.getOptionValue(OPTION_SET);
Map<String, String> oldValues;
if (template == null) {
oldValues = configurationGenerator.setProperties(changedParameters);
} else {
oldValues = configurationGenerator.setProperties(template, changedParameters);
}
log.debug("Old values: " + oldValues);
}
/**
* Since 5.5
*/
protected boolean pack() {
try {
configurationGenerator.setProperty(PARAM_UPDATECENTER_DISABLED, "true");
List<String> startCommand = new ArrayList<>();
startCommand.add(getJavaExecutable().getPath());
startCommand.addAll(getJavaOptsProperty(Function.identity()));
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.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<ThreadedStreamGobbler> sgArray = logProcessStreams(process, true);
Thread.sleep(100);
process.waitFor();
waitForProcessStreams(sgArray);
} catch (IOException | InterruptedException e) {
errorValue = EXIT_CODE_ERROR;
log.error("Could not start process", e);
} catch (ConfigurationException e) {
errorValue = EXIT_CODE_ERROR;
log.error(e);
}
return errorValue == EXIT_CODE_OK;
}
protected 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 {
boolean started = doStartAndWait(false);
if (started && !quiet) {
log.info("Go to " + getURL());
}
return started;
}
/**
* @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 {
boolean started = doStart(false);
if (started && !quiet) {
log.info("Go to " + getURL());
}
return started;
}
/**
* 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);
if (strict) {
errorValue = EXIT_CODE_ERROR;
log.error("Shutting down because of unstarted component in strict mode...");
stop();
return false;
}
}
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
*/
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()) {
if (!getConnectBroker().executePending(configurationGenerator.getInstallFile(), true, true,
ignoreMissing)) {
errorValue = EXIT_CODE_ERROR;
log.error(String.format(
"Start interrupted due to failure on pending actions. You can resume with a new start;"
+ " or you can restore the file '%s', optionally using the '--%s' option.",
configurationGenerator.getInstallFile().getName(), OPTION_IGNORE_MISSING));
return false;
}
// 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");
try (FileWriter writer = new FileWriter(pidFile)) {
writer.write(pid);
}
}
} 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) {
if (strict) {
// assume program is not configured because of http port binding
// conflict
errorValue = EXIT_CODE_NOT_CONFIGURED;
}
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) {
try {
printXMLOutput(jaxbContext, objectToOutput, System.out);
} catch (JAXBException | XMLStreamException | FactoryConfigurationError e) {
log.error("Output serialization failed: " + e.getMessage(), e);
errorValue = EXIT_CODE_NOT_RUNNING;
}
}
/**
* @since 8.3
*/
protected void printXMLOutput(JAXBContext context, Object object, OutputStream out)
throws XMLStreamException, FactoryConfigurationError, JAXBException {
XMLStreamWriter writer = jsonOutput ? jsonWriter(context, out) : xmlWriter(context, out);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(object, writer);
}
protected XMLStreamWriter jsonWriter(JAXBContext context, OutputStream out) {
JSONConfiguration config = JSONConfiguration.mapped().rootUnwrapping(true).attributeAsElement("key", "value").build();
config = JSONConfiguration.createJSONConfigurationWithFormatted(config, true);
return JsonXmlStreamWriter.createWriter(new OutputStreamWriter(out), config, "");
}
protected XMLStreamWriter xmlWriter(JAXBContext context, OutputStream out) throws XMLStreamException, FactoryConfigurationError {
return XMLOutputFactory.newInstance().createXMLStreamWriter(out);
}
/**
* Stop stream gobblers contained in the given ArrayList
*
* @since 5.5
* @see #logProcessStreams(Process, boolean)
*/
public void waitForProcessStreams(ArrayList<ThreadedStreamGobbler> 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;
}
@Override
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, waiting 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<String> 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<ThreadedStreamGobbler> 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.canFindPid()) {
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<String> 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 {
try {
checkNoRunningServer();
configurationGenerator.checkJavaVersion();
configurationGenerator.run();
overrideJavaTmpDir = Boolean.parseBoolean(configurationGenerator.getUserConfig().getProperty(
OVERRIDE_JAVA_TMPDIR_PARAM, "true"));
} catch (ConfigurationException e) {
errorValue = EXIT_CODE_NOT_CONFIGURED;
throw e;
}
}
/**
* @return Default max wait depending on server (ie JBoss takes much more
* time than Tomcat)
*/
private String getDefaultMaxWait() {
return 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 #getStatus()
*/
public String status() {
try {
if (processManager instanceof PureJavaProcessManager) {
status = STATUS_CODE_UNKNOWN;
return "Can't check server status on your OS.";
}
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 ( Tomcat or
* Jetty).
* @throws ConfigurationException
* If server cannot be identified
* @throws PackageException
* @throws IOException
* @since 5.5
*/
public static NuxeoLauncher createLauncher(String[] args) throws ConfigurationException, ParseException, IOException, PackageException {
CommandLine cmdLine = parseOptions(args);
ConfigurationGenerator cg = new ConfigurationGenerator(quiet, debug);
if (cmdLine.hasOption(OPTION_HIDE_DEPRECATION)) {
cg.hideDeprecationWarnings(true);
}
NuxeoLauncher launcher;
if (cg.isJetty) {
launcher = new NuxeoJettyLauncher(cg);
} else if (cg.isTomcat) {
launcher = new NuxeoTomcatLauncher(cg);
} else {
throw new ConfigurationException("Unknown server!");
}
launcher.connectBroker = new ConnectBroker(launcher.configurationGenerator.getEnv());
launcher.setArgs(cmdLine);
launcher.initConnectBroker();
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.
* @throws ConfigurationException
*/
private void setArgs(CommandLine cmdLine) throws ConfigurationException {
this.cmdLine = cmdLine;
extractCommandAndParams(cmdLine.getArgs());
// Use GUI?
if (cmdLine.hasOption(OPTION_GUI)) {
useGui = Boolean.valueOf(ConnectBroker.parseAnswer(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 (SystemUtils.IS_OS_WINDOWS) {
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();
}
if (cmdLine.hasOption(OPTION_CLID)) {
try {
getConnectBroker().setCLID(cmdLine.getOptionValue(OPTION_CLID));
} catch (NoCLID | IOException | PackageException e) {
throw new ConfigurationException(e);
}
}
if (cmdLine.hasOption(OPTION_IGNORE_MISSING)) {
ignoreMissing = true;
}
}
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;
}
}
/**
* Set launcher 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 7.4
*/
protected static void setDebug(String[] categories, String defaultCategory) {
debug = true;
if (categories == null) {
categories = new String[] { defaultCategory };
}
Log4JHelper.setDebug(categories, true, true, new String[] { Log4JHelper.CONSOLE_APPENDER_NAME, "FILE" });
}
/**
* @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);
}
/**
* @param isStrict
* if {@code true}, set the launcher strict option
* @since 7.4
* @see #OPTION_STRICT_DESC
*/
protected static void setStrict(boolean isStrict) {
strict = isStrict;
}
protected void setXMLOutput() {
xmlOutput = true;
}
protected void setJSONOutput() {
jsonOutput = true;
setXMLOutput();
}
public static void printShortHelp() {
System.out.println();
HelpFormatter help = new HelpFormatter();
help.setSyntaxPrefix("USAGE\n");
help.setOptionComparator(null);
help.setWidth(1000);
help.printHelp(OPTION_HELP_USAGE, "OPTIONS", options, null);
System.out.println(OPTION_HELP_DESC_COMMANDS);
}
public static void printLongHelp() {
System.out.println();
HelpFormatter help = new HelpFormatter();
help.setSyntaxPrefix("USAGE\n");
help.setOptionComparator(null);
help.setWidth(1000);
help.printHelp(OPTION_HELP_USAGE, OPTION_HELP_HEADER, options, null);
System.out.println(OPTION_HELP_DESC_ENV);
System.out.println(OPTION_HELP_DESC_COMMANDS);
System.out.println(OPTION_HELP_FOOTER);
}
/**
* 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;
}
}
/**
* Provides this instance info
*
* @since 8.3
*
*/
public InstanceInfo getInfo() {
return info;
}
/**
* @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 void initConnectBroker() throws IOException, PackageException {
if (cmdLine.hasOption(OPTION_ACCEPT)) {
connectBroker.setAccept(cmdLine.getOptionValue(OPTION_ACCEPT));
}
if (cmdLine.hasOption(OPTION_RELAX)) {
connectBroker.setRelax(cmdLine.getOptionValue(OPTION_RELAX));
}
if (cmdLine.hasOption(OPTION_SNAPSHOT)) {
connectBroker.setAllowSNAPSHOT(true);
}
List<CommandInfo> csetCommands = cset.commands;
cset = connectBroker.getCommandSet();
cset.commands.addAll(0, csetCommands);
try {
clid = connectBroker.getCLID();
} catch (NoCLID cause) {
;
}
info = configurationGenerator.getServerConfigurator().getInfo(clid, connectBroker.getPkgList());
if (new Version(info.distribution.version).isSnapshot()) {
connectBroker.setAllowSNAPSHOT(true);
}
}
protected ConnectBroker getConnectBroker() throws IOException, PackageException {
return connectBroker;
}
protected ConnectRegistrationBroker getConnectRegistrationBroker() throws IOException, PackageException {
if (connectRegistrationBroker == null) {
getConnectBroker(); // Ensure ConnectBroker is instantiated too.
connectRegistrationBroker = new ConnectRegistrationBroker();
}
return connectRegistrationBroker;
}
/**
* 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), ignoreMissing);
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,
!cmdLine.hasOption(OPTION_NODEPS), ignoreMissing);
}
cmdOK = cmdOK && getConnectBroker().pkgInstall(Arrays.asList(pkgIDs), ignoreMissing);
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 {
printInstanceXMLOutput(instance, System.out);
} catch (JAXBException | XMLStreamException | FactoryConfigurationError e) {
log.error("Output serialization failed: " + e.getMessage());
log.debug(e, e);
errorValue = EXIT_CODE_NOT_RUNNING;
}
}
protected void printInstanceXMLOutput(InstanceInfo instance, OutputStream out)
throws JAXBException, XMLStreamException, FactoryConfigurationError {
JAXBContext jaxbContext = JAXBContext.newInstance(InstanceInfo.class, DistributionInfo.class,
PackageInfo.class, ConfigurationInfo.class, KeyValueInfo.class);
printXMLOutput(jaxbContext, instance, out);
}
/**
* @throws PackageException
* @throws IOException
* @throws ConfigurationException
* @since 5.6
*/
protected void showConfig() throws IOException, PackageException, ConfigurationException {
log.info("***** Nuxeo instance configuration *****");
log.info("NUXEO_CONF: " + info.NUXEO_CONF);
log.info("NUXEO_HOME: " + info.NUXEO_HOME);
if (info.clid != null) {
log.info("Instance CLID: " + info.clid);
}
// distribution.properties
log.info("** Distribution");
log.info("- name: " + info.distribution.name);
log.info("- server: " + info.distribution.server);
log.info("- version: " + info.distribution.version);
log.info("- date: " + info.distribution.date);
log.info("- packaging: " + info.distribution.packaging);
// packages
log.info("** Packages:");
for (PackageInfo pkg : info.packages) {
log.info(String.format("- %s (version: %s - id: %s - state: %s)", pkg.name, pkg.version,
pkg.id, pkg.state.getLabel()));
}
// nuxeo.conf
log.info("** Templates:");
log.info("Database template: " + info.config.dbtemplate);
for (String template : info.config.pkgtemplates) {
log.info("Package template: " + template);
}
for (String template : info.config.usertemplates) {
log.info("User template: " + template);
}
for (String template : info.config.basetemplates) {
log.info("Base template: " + template);
}
log.info("** Settings from nuxeo.conf:");
for (KeyValueInfo keyval : info.config.keyvals) {
log.info(String.format("%s=%s", keyval.key, keyval.value));
}
log.info("****************************************");
if (xmlOutput) {
printInstanceXMLOutput(info);
}
}
/**
* @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<String> pkgsToAdd, List<String> pkgsToInstall, List<String> pkgsToUninstall,
List<String> pkgsToRemove) throws IOException, PackageException {
boolean cmdOK = true;
if (configurationGenerator.isInstallInProgress()) {
cmdOK = getConnectBroker().executePending(configurationGenerator.getInstallFile(), true, true,
ignoreMissing);
}
cmdOK = cmdOK
&& getConnectBroker().pkgRequest(pkgsToAdd, pkgsToInstall, pkgsToUninstall, pkgsToRemove, true,
ignoreMissing);
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 {@code true} if command succeed
* @throws PackageException
* @throws IOException
* @since 5.6
*/
protected boolean pkgPurge() throws PackageException, IOException {
return getConnectBroker().pkgPurge();
}
/**
* Install the hotfixes available for the instance
*
* @return {@code true} if command succeed
* @throws PackageException
* @throws IOException
* @since 5.6
*/
protected boolean pkgHotfix() throws IOException, PackageException {
return getConnectBroker().pkgHotfix();
}
/**
* Upgrade the Nuxeo Packages (addons) available for the instance
*
* @return {@code true} if command succeed
* @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<String> request) throws IOException, PackageException {
List<String> add = new ArrayList<>();
List<String> install = new ArrayList<>();
List<String> 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);
}
protected boolean pkgSetRequest(List<String> request, boolean nodeps) throws IOException, PackageException {
boolean cmdOK;
if (nodeps) {
cmdOK = getConnectBroker().pkgSet(request, ignoreMissing);
} else {
cmdOK = getConnectBroker().pkgRequest(null, request, null, null, false, ignoreMissing);
}
if (!cmdOK) {
errorValue = EXIT_CODE_ERROR;
}
return cmdOK;
}
/**
* 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;
}
protected boolean dumpConnectReport(OutputStream out, boolean prettyprint) {
class MapBuilder<K, V> {
final Map<K, V> store = new HashMap<>();
MapBuilder<K, V> with(K key, V value) {
store.put(key, value);
return this;
}
Map<K, V> build() {
return store;
}
}
try (JsonGenerator generator =
Json.createGeneratorFactory(new MapBuilder<String, Object>().with(JsonGenerator.PRETTY_PRINTING, prettyprint).build())
.createGenerator(out)) {
generator.writeStartObject();
ReportConnector.of().feed(generator);
generator.writeEnd();
} catch (IOException | InterruptedException | ExecutionException cause) {
log.error("Cannot dump connect report", cause);
errorValue = EXIT_CODE_ERROR;
return false;
}
return true;
}
}