001/* 002 * (C) Copyright 2010-2015 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Julien Carsique 016 * Florent Guillaume 017 */ 018package org.nuxeo.launcher; 019 020import java.io.Console; 021import java.io.File; 022import java.io.FileWriter; 023import java.io.FilenameFilter; 024import java.io.IOException; 025import java.io.StringWriter; 026import java.io.Writer; 027import java.net.SocketTimeoutException; 028import java.security.GeneralSecurityException; 029import java.util.ArrayList; 030import java.util.Arrays; 031import java.util.Collection; 032import java.util.Collections; 033import java.util.Date; 034import java.util.HashMap; 035import java.util.Iterator; 036import java.util.List; 037import java.util.Map; 038import java.util.StringTokenizer; 039import java.util.TreeSet; 040import java.util.concurrent.ExecutorService; 041import java.util.concurrent.Executors; 042import java.util.regex.Matcher; 043import java.util.regex.Pattern; 044 045import javax.xml.bind.JAXBContext; 046import javax.xml.bind.JAXBException; 047import javax.xml.bind.Marshaller; 048import javax.xml.parsers.DocumentBuilder; 049import javax.xml.parsers.DocumentBuilderFactory; 050 051import org.apache.commons.cli.CommandLine; 052import org.apache.commons.cli.CommandLineParser; 053import org.apache.commons.cli.DefaultParser; 054import org.apache.commons.cli.HelpFormatter; 055import org.apache.commons.cli.Option; 056import org.apache.commons.cli.OptionGroup; 057import org.apache.commons.cli.Options; 058import org.apache.commons.cli.ParseException; 059import org.apache.commons.codec.binary.Base64; 060import org.apache.commons.io.IOUtils; 061import org.apache.commons.lang.ArrayUtils; 062import org.apache.commons.lang.StringUtils; 063import org.apache.commons.lang.text.StrSubstitutor; 064import org.apache.commons.lang3.SystemUtils; 065import org.apache.commons.logging.Log; 066import org.apache.commons.logging.LogFactory; 067import org.apache.commons.logging.impl.SimpleLog; 068import org.artofsolving.jodconverter.process.MacProcessManager; 069import org.artofsolving.jodconverter.process.ProcessManager; 070import org.artofsolving.jodconverter.process.PureJavaProcessManager; 071import org.artofsolving.jodconverter.process.UnixProcessManager; 072import org.artofsolving.jodconverter.process.WindowsProcessManager; 073import org.artofsolving.jodconverter.util.PlatformUtils; 074import org.json.JSONException; 075import org.json.XML; 076import org.w3c.dom.Document; 077import org.w3c.dom.Element; 078import org.w3c.dom.NodeList; 079 080import org.nuxeo.common.Environment; 081import org.nuxeo.common.codec.Crypto; 082import org.nuxeo.common.codec.CryptoProperties; 083import org.nuxeo.connect.identity.LogicalInstanceIdentifier.NoCLID; 084import org.nuxeo.connect.update.LocalPackage; 085import org.nuxeo.connect.update.PackageException; 086import org.nuxeo.connect.update.Version; 087import org.nuxeo.launcher.config.ConfigurationException; 088import org.nuxeo.launcher.config.ConfigurationGenerator; 089import org.nuxeo.launcher.connect.ConnectBroker; 090import org.nuxeo.launcher.daemon.DaemonThreadFactory; 091import org.nuxeo.launcher.gui.NuxeoLauncherGUI; 092import org.nuxeo.launcher.info.CommandInfo; 093import org.nuxeo.launcher.info.CommandSetInfo; 094import org.nuxeo.launcher.info.ConfigurationInfo; 095import org.nuxeo.launcher.info.DistributionInfo; 096import org.nuxeo.launcher.info.InstanceInfo; 097import org.nuxeo.launcher.info.KeyValueInfo; 098import org.nuxeo.launcher.info.MessageInfo; 099import org.nuxeo.launcher.info.PackageInfo; 100import org.nuxeo.launcher.monitoring.StatusServletClient; 101import org.nuxeo.log4j.Log4JHelper; 102import org.nuxeo.log4j.ThreadedStreamGobbler; 103 104/** 105 * @author jcarsique 106 * @since 5.4.2 107 */ 108public abstract class NuxeoLauncher { 109 110 /** 111 * @since 7.4 112 */ 113 protected static final String OUTPUT_UNSET_VALUE = "<unset>"; 114 115 /** 116 * @since 5.6 117 */ 118 protected static final String OPTION_NODEPS = "nodeps"; 119 120 private static final String OPTION_NODEPS_DESC = "Ignore package dependencies and constraints."; 121 122 /** 123 * @since 5.6 124 */ 125 protected static final String OPTION_GUI = "gui"; 126 127 private static final String OPTION_GUI_DESC = "Start graphical user interface (default is true on Windows and false on other platforms)."; 128 129 /** 130 * @since 5.6 131 */ 132 protected static final String OPTION_JSON = "json"; 133 134 private static final String OPTION_JSON_DESC = "Output JSON for mp-* commands."; 135 136 /** 137 * @since 5.6 138 */ 139 protected static final String OPTION_XML = "xml"; 140 141 private static final String OPTION_XML_DESC = "Output XML for mp-* commands."; 142 143 /** 144 * @since 5.6 145 */ 146 protected static final String OPTION_DEBUG = "debug"; 147 148 private static final String OPTION_DEBUG_DESC = "Activate debug messages.\n" 149 + "<categories>: comma-separated Java categories to debug (default: \"org.nuxeo.launcher\")."; 150 151 /** 152 * @since 7.4 153 */ 154 private static final String OPTION_DEBUG_CATEGORY_ARG_NAME = "categories"; 155 156 /** 157 * @since 5.6 158 */ 159 protected static final String OPTION_DEBUG_CATEGORY = "dc"; 160 161 private static final String OPTION_DEBUG_CATEGORY_DESC = "Deprecated: see categories on '--debug' option."; 162 163 /** 164 * @since 5.6 165 */ 166 protected static final String OPTION_QUIET = "quiet"; 167 168 private static final String OPTION_QUIET_DESC = "Suppress information messages."; 169 170 /** 171 * @since 5.6 172 */ 173 protected static final String OPTION_HELP = "help"; 174 175 private static final String OPTION_HELP_DESC = "Show detailed help."; 176 177 /** 178 * @since 5.6 179 */ 180 protected static final String OPTION_RELAX = "relax"; 181 182 private static final String OPTION_RELAX_DESC = "Allow relax constraint on current platform (default: " 183 + ConnectBroker.OPTION_RELAX_DEFAULT + ")."; 184 185 /** 186 * @since 5.6 187 */ 188 protected static final String OPTION_ACCEPT = "accept"; 189 190 private static final String OPTION_ACCEPT_DESC = "Accept, refuse or ask confirmation for all changes (default: " 191 + ConnectBroker.OPTION_ACCEPT_DEFAULT + ").\n" 192 + "In non interactive mode, '--accept=true' also sets '--relax=true' if needed."; 193 194 /** 195 * @since 5.9.1 196 */ 197 protected static final String OPTION_SNAPSHOT = "snapshot"; 198 199 private static final String OPTION_SNAPSHOT_DESC = "Allow use of SNAPSHOT Marketplace packages.\n" 200 + "This option is implicit:\n" // 201 + "\t- on SNAPSHOT distributions (daily builds),\n" 202 + "\t- if the command explicitly requests a SNAPSHOT package."; 203 204 /** 205 * @since 5.9.1 206 */ 207 protected static final String OPTION_FORCE = "force"; 208 209 private static final String OPTION_FORCE_DESC = "Deprecated: use '--strict' option instead."; 210 211 /** 212 * @since 7.4 213 */ 214 protected static final String OPTION_STRICT = "strict"; 215 216 private static final String OPTION_STRICT_DESC = "Abort in error the start command when a component cannot " 217 + "be activated or if a server is already running."; 218 219 /** 220 * @since 5.6 221 */ 222 protected static final String OPTION_HIDE_DEPRECATION = "hide-deprecation-warnings"; 223 224 protected static final String OPTION_HIDE_DEPRECATION_DESC = "Hide deprecation warnings."; 225 226 /** 227 * @since 6.0 228 */ 229 protected static final String OPTION_IGNORE_MISSING = "ignore-missing"; 230 231 protected static final String OPTION_IGNORE_MISSING_DESC = "Ignore unknown packages on mp-add, mp-install and mp-set commands."; 232 233 /** 234 * @since 6.0 235 */ 236 protected static final String OPTION_CLID = "clid"; 237 238 private static final String OPTION_CLID_DESC = "Use the provided instance CLID file"; 239 240 /** 241 * @since 7.4 242 */ 243 protected static final String OPTION_ENCRYPT = "encrypt"; 244 245 private static final String OPTION_ENCRYPT_ARG_NAME = "algorithm"; 246 247 private static final String OPTION_ENCRYPT_DESC = String.format( 248 "Activate key value symmetric encryption.\n" 249 + "The algorithm can be configured: <%s> is a cipher transformation of the form: \"algorithm/mode/padding\" or \"algorithm\".\n" 250 + "Default value is \"%s\" (Advanced Encryption Standard, Electronic Cookbook Mode, PKCS5-style padding).", 251 OPTION_ENCRYPT, Crypto.DEFAULT_ALGO); 252 253 /** 254 * @since 7.4 255 */ 256 protected static final String OPTION_SET = "set"; 257 258 private static final String OPTION_SET_DESC = String.format( 259 "Set the value for a given key.\n" 260 + "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" 261 + "If the value is empty (''), then the property is unset.\n" 262 + "This option is implicit if no '--get' or '--get-regexp' option is used and there are exactly two parameters (key value).", 263 ConfigurationGenerator.NUXEO_CONF, ConfigurationGenerator.NUXEO_DEFAULT_CONF); 264 265 /** 266 * @since 7.4 267 */ 268 protected static final String OPTION_GET = "get"; 269 270 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" 271 + "This option is implicit if '--set' option is not used and there are more or less than two parameters."; 272 273 /** 274 * @since 7.4 275 */ 276 protected static final String OPTION_GET_REGEXP = "get-regexp"; 277 278 private static final String OPTION_GET_REGEXP_DESC = "Get the value for all keys matching the given regular expression(s)."; 279 280 // Fallback to avoid an error when the log dir is not initialized 281 static { 282 if (System.getProperty(Environment.NUXEO_LOG_DIR) == null) { 283 System.setProperty(Environment.NUXEO_LOG_DIR, "."); 284 } 285 } 286 287 /** 288 * @since 5.6 289 */ 290 private static final String DEFAULT_NUXEO_CONTEXT_PATH = "/nuxeo"; 291 292 static final Log log = LogFactory.getLog(NuxeoLauncher.class); 293 294 private static Options launcherOptions = null; 295 296 private static final String JAVA_OPTS_PROPERTY = "launcher.java.opts"; 297 298 private static final String JAVA_OPTS_DEFAULT = "-Xms512m -Xmx1024m"; 299 300 private static final String OVERRIDE_JAVA_TMPDIR_PARAM = "launcher.override.java.tmpdir"; 301 302 protected boolean overrideJavaTmpDir; 303 304 private static final String START_MAX_WAIT_PARAM = "launcher.start.max.wait"; 305 306 private static final String STOP_MAX_WAIT_PARAM = "launcher.stop.max.wait"; 307 308 /** 309 * Default maximum time to wait for server startup summary in logs (in seconds). 310 */ 311 private static final String START_MAX_WAIT_DEFAULT = "300"; 312 313 /** 314 * Default maximum time to wait for effective stop (in seconds) 315 */ 316 private static final String STOP_MAX_WAIT_DEFAULT = "60"; 317 318 /** 319 * Number of try to cleanly stop server before killing process 320 */ 321 private static final int STOP_NB_TRY = 5; 322 323 private static final int STOP_SECONDS_BEFORE_NEXT_TRY = 2; 324 325 private static final long STREAM_MAX_WAIT = 3000; 326 327 private static final String PACK_TOMCAT_CLASS = "org.nuxeo.runtime.deployment.preprocessor.PackWar"; 328 329 private static final String PARAM_UPDATECENTER_DISABLED = "nuxeo.updatecenter.disabled"; 330 331 private static final String[] COMMANDS_NO_GUI = { "configure", "mp-init", "mp-purge", "mp-add", "mp-install", 332 "mp-uninstall", "mp-request", "mp-remove", "mp-hotfix", "mp-upgrade", "mp-reset", "mp-list", "mp-listall", 333 "mp-update", "status", "showconf", "mp-show", "mp-set", "config", "encrypt", "decrypt", OPTION_HELP }; 334 335 private static final String[] COMMANDS_NO_RUNNING_SERVER = { "pack", "mp-init", "mp-purge", "mp-add", "mp-install", 336 "mp-uninstall", "mp-request", "mp-remove", "mp-hotfix", "mp-upgrade", "mp-reset", "mp-update", "mp-set" }; 337 338 /** 339 * @since 7.4 340 */ 341 protected boolean commandRequiresNoRunningServer() { 342 return Arrays.asList(COMMANDS_NO_RUNNING_SERVER).contains(command); 343 } 344 345 /** 346 * @since 7.4 347 */ 348 protected boolean commandRequiresNoGUI() { 349 return Arrays.asList(COMMANDS_NO_GUI).contains(command); 350 } 351 352 /** 353 * Program is running or service is OK. 354 * 355 * @since 5.7 356 */ 357 public static final int STATUS_CODE_ON = 0; 358 359 /** 360 * Program is not running. 361 * 362 * @since 5.7 363 */ 364 public static final int STATUS_CODE_OFF = 3; 365 366 /** 367 * Program or service status is unknown. 368 * 369 * @since 5.7 370 */ 371 public static final int STATUS_CODE_UNKNOWN = 4; 372 373 /** 374 * @since 5.7 375 */ 376 public static final int EXIT_CODE_OK = 0; 377 378 /** 379 * Generic or unspecified error. 380 * 381 * @since 5.7 382 */ 383 public static final int EXIT_CODE_ERROR = 1; 384 385 /** 386 * Invalid or excess argument(s). 387 * 388 * @since 5.7 389 */ 390 public static final int EXIT_CODE_INVALID = 2; 391 392 /** 393 * Unimplemented feature. 394 * 395 * @since 5.7 396 */ 397 public static final int EXIT_CODE_UNIMPLEMENTED = 3; 398 399 /** 400 * User had insufficient privilege. 401 * 402 * @since 5.7 403 */ 404 public static final int EXIT_CODE_UNAUTHORIZED = 4; 405 406 /** 407 * Program is not installed. 408 * 409 * @since 5.7 410 */ 411 public static final int EXIT_CODE_NOT_INSTALLED = 5; 412 413 /** 414 * Program is not configured. 415 * 416 * @since 5.7 417 */ 418 public static final int EXIT_CODE_NOT_CONFIGURED = 6; 419 420 /** 421 * Program is not running. 422 * 423 * @since 5.7 424 */ 425 public static final int EXIT_CODE_NOT_RUNNING = 7; 426 427 private static final String OPTION_HELP_DESC_ENV = "\nENVIRONMENT VARIABLES\n" 428 + " NUXEO_HOME\t\tPath to server root directory.\n" // 429 + " NUXEO_CONF\t\tPath to {{nuxeo.conf}} file.\n" 430 + " PATH\n" 431 + "\tJAVA\t\t\tPath to the {{java}} executable.\n" 432 + " JAVA_HOME\t\tPath to the Java home directory. Can also be defined in {{nuxeo.conf}}.\n" 433 + " JAVA_OPTS\t\tOptional values passed to the JVM. Can also be defined in {{nuxeo.conf}}.\n" 434 + " REQUIRED_JAVA_VERSION\tNuxeo requirement on Java version.\n" // 435 + "\nJAVA USAGE\n" 436 + String.format(" java [-D%s=\"JVM options\"]" 437 + " [-D%s=\"/path/to/nuxeo\"] [-D%s=\"/path/to/nuxeo.conf\"]" 438 + " [-Djvmcheck=nofail] -jar \"path/to/nuxeo-launcher.jar\" \\\n" 439 + " \t[options] <command> [command parameters]\n\n", JAVA_OPTS_PROPERTY, 440 Environment.NUXEO_HOME, ConfigurationGenerator.NUXEO_CONF) 441 + String.format(" %s\tParameters for the server JVM (default are %s).\n", JAVA_OPTS_PROPERTY, 442 JAVA_OPTS_DEFAULT) 443 + String.format(" %s\t\tNuxeo server root path (default is parent of called script).\n", 444 Environment.NUXEO_HOME) 445 + String.format(" %s\t\tPath to {{%1$s}} file (default is \"$NUXEO_HOME/bin/%1$s\").\n", 446 ConfigurationGenerator.NUXEO_CONF) 447 + " jvmcheck\t\tIf set to \"nofail\", ignore JVM version validation errors.\n"; 448 449 private static final String OPTION_HELP_DESC_COMMANDS = "\nCOMMANDS\n" 450 + " help\t\t\tPrint this message.\n" 451 + " gui\t\t\tDeprecated: use '--gui' option instead.\n" 452 + " 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" 453 + " stop\t\t\tStop any Nuxeo server started with the same {{nuxeo.conf}} file.\n" 454 + " restart\t\t\tRestart Nuxeo server.\n" 455 + " config\t\t\tGet and set template or global parameters.\n" 456 + " encrypt\t\t\tOutput encrypted value for a given parameter.\n" 457 + " decrypt\t\t\tOutput decrypted value for a given parameter.\n" 458 + " configure\t\tConfigure Nuxeo server with parameters from {{nuxeo.conf}}.\n" 459 + " wizard\t\t\tStart the wizard.\n" 460 + " console\t\t\tStart Nuxeo server in a console mode. Ctrl-C will stop it.\n" 461 + " status\t\t\tPrint server running status.\n" 462 + " startbg\t\t\tStart Nuxeo server in background, without waiting for effective start. Useful for starting Nuxeo as a service.\n" 463 + " restartbg\t\tRestart Nuxeo server with a call to \"startbg\" after \"stop\".\n" 464 + " pack\t\t\tBuild a static archive.\n" 465 + " showconf\t\tDisplay the instance configuration.\n" 466 + " mp-list\t\t\tList local Marketplace packages.\n" 467 + " mp-listall\t\tList all Marketplace Packages.\n" 468 + " mp-init\t\t\tPre-cache Marketplace packages locally available in the distribution.\n" 469 + " mp-update\t\tUpdate cache of marketplace packages list.\n" 470 + " mp-add\t\t\tAdd Marketplace package(s) to local cache. You must provide the package file(s), name(s) or ID(s) as parameter.\n" 471 + " mp-install\t\tRun Marketplace package installation. It is automatically called at startup if {{installAfterRestart.log}} file exists in data directory. Else you must provide the package file(s), name(s) or ID(s) as parameter.\n" 472 + " mp-uninstall\t\tUninstall Marketplace package(s). You must provide the package name(s) or ID(s) as parameter (see \"mp-list\" command).\n" 473 + " mp-remove\t\tRemove Marketplace package(s) from the local cache. You must provide the package name(s) or ID(s) as parameter (see \"mp-list\" command).\n" 474 + " mp-reset\t\tReset all packages to DOWNLOADED state. May be useful after a manual server upgrade.\n" 475 + " mp-set\t\t\tInstall a list of Marketplace Packages and remove those not in the list.\n" 476 + " mp-request\t\tInstall and uninstall Marketplace Package(s) in one command. You must provide a *quoted* list of package names or IDs prefixed with + (install) or - (uninstall).\n" 477 + " mp-purge\t\tUninstall and remove all packages from the local cache.\n" 478 + " mp-hotfix\t\tInstall all the available hotfixes for the current platform (requires a registered instance).\n" 479 + " mp-upgrade\t\tGet all the available upgrades for the Marketplace packages currently installed (requires a registered instance).\n" 480 + " mp-show\t\t\tShow Marketplace package(s) information. You must provide the package file(s), name(s) or ID(s) as parameter.\n" 481 + "\nThe following commands are always executed in console/headless mode (no GUI): " 482 + "\"configure\", \"mp-init\", \"mp-purge\", \"mp-add\", \"mp-install\", \"mp-uninstall\", \"mp-request\", " 483 + "\"mp-remove\", \"mp-hotfix\", \"mp-upgrade\", \"mp-reset\", \"mp-list\", \"mp-listall\", \"mp-update\", " 484 + "\"status\", \"showconf\", \"mp-show\", \"mp-set\", \"config\", \"encrypt\", \"decrypt\", \"help\".\n" 485 + "\nThe following commands cannot be executed on a running server: \"pack\", \"mp-init\", \"mp-purge\", " 486 + "\"mp-add\", \"mp-install\", \"mp-uninstall\", \"mp-request\", \"mp-remove\", \"mp-hotfix\", \"mp-upgrade\", " 487 + "\"mp-reset\"."; 488 489 private static final String OPTION_HELP_USAGE = " nuxeoctl <command> [command parameters] [options]\n\n"; 490 491 private static final String OPTION_HELP_HEADER = "SYNOPSIS\n" 492 + " nuxeoctl encrypt [--encrypt <algorithm>] [<clearValue>..] [-d [<categories>]|-q]\n" 493 + " Output encrypted value for <clearValue>.\n" 494 + " If <clearValue> is not provided, it is read from stdin.\n\n" 495 + " nuxeoctl decrypt '<cryptedValue>'.. [-d [<categories>]|-q]\n" // 496 + " Output decrypted value for <cryptedValue>. The secret key is read from stdin.\n\n" 497 + " nuxeoctl config [--encrypt [<algorithm>]] [--set [<template>]] [<key> <value>].. <key> [<value>] [-d [<categories>]|-q]\n" 498 + " Set template or global parameters.\n" 499 + " If <value> is not provided and the --set 'option' is used, then the value is read from stdin.\n\n" 500 + " nuxeoctl config [--get] <key>.. [-d [<categories>]|-q]\n" 501 + " Get value for the given key(s).\n\n" 502 + " nuxeoctl config [--get-regexp] <regexp>.. [-d [<categories>]|-q]\n" 503 + " Get value for the keys matching the given regular expression(s).\n\n" 504 + " nuxeoctl [help|status|showconf] [-d [<categories>]|-q]\n\n" 505 + " nuxeoctl [configure] [-d [<categories>]|-q|-hdw]\n\n" 506 + " nuxeoctl [wizard] [-d [<categories>]|-q|--clid <arg>|--gui <true|false|yes|no>]\n\n" 507 + " nuxeoctl [stop] [-d [<categories>]|-q|--gui <true|false|yes|no>]\n\n" 508 + " nuxeoctl [start|restart|console|startbg|restartbg] [-d [<categories>]|-q|--clid <arg>|--gui <true|false|yes|no>|--strict|-hdw]\n\n" 509 + " nuxeoctl [mp-show] [command parameters] [-d [<categories>]|-q|--clid <arg>|--xml|--json]\n\n" 510 + " nuxeoctl [mp-list|mp-listall|mp-init|mp-update] [command parameters] [-d [<categories>]|-q|--clid <arg>|--xml|--json]\n\n" 511 + " 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" 512 + " 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" 513 + " nuxeoctl pack <target> [-d [<categories>]|-q]\n\n" + "OPTIONS"; 514 515 private static final String OPTION_HELP_FOOTER = "\nSee online documentation \"ADMINDOC/nuxeoctl and Control Panel Usage\": https://doc.nuxeo.com/x/FwNc"; 516 517 protected ConfigurationGenerator configurationGenerator; 518 519 public final ConfigurationGenerator getConfigurationGenerator() { 520 return configurationGenerator; 521 } 522 523 protected ProcessManager processManager; 524 525 protected Process nuxeoProcess; 526 527 private String processRegex; 528 529 protected String pid; 530 531 private ExecutorService executor = Executors.newSingleThreadExecutor(new DaemonThreadFactory("NuxeoProcessThread", 532 false)); 533 534 private ShutdownThread shutdownHook; 535 536 protected String[] params; 537 538 protected String command; 539 540 public String getCommand() { 541 return command; 542 } 543 544 /** 545 * @since 7.4 546 */ 547 public boolean commandIs(String aCommand) { 548 return StringUtils.equalsIgnoreCase(command, aCommand); 549 } 550 551 public CommandSetInfo cset = new CommandSetInfo(); 552 553 private boolean useGui = false; 554 555 /** 556 * @since 5.5 557 */ 558 public boolean isUsingGui() { 559 return useGui; 560 } 561 562 private boolean reloadConfiguration = false; 563 564 private int status = STATUS_CODE_UNKNOWN; 565 566 private int errorValue = EXIT_CODE_OK; 567 568 private StatusServletClient statusServletClient; 569 570 private static boolean quiet = false; 571 572 private static boolean debug = false; 573 574 private static boolean strict = false; 575 576 private boolean xmlOutput = false; 577 578 private boolean jsonOutput = false; 579 580 private ConnectBroker connectBroker = null; 581 582 private CommandLine cmdLine; 583 584 private boolean ignoreMissing = false; 585 586 /** 587 * @since 5.5 588 * @return true if quiet mode is active 589 */ 590 public boolean isQuiet() { 591 return quiet; 592 } 593 594 private static Map<String, NuxeoLauncherGUI> guis; 595 596 /** 597 * @since 5.5 598 */ 599 public NuxeoLauncherGUI getGUI() { 600 if (guis == null) { 601 return null; 602 } 603 return guis.get(configurationGenerator.getNuxeoConf().toString()); 604 } 605 606 /** 607 * @since 5.5 608 */ 609 public void setGUI(NuxeoLauncherGUI gui) { 610 if (guis == null) { 611 guis = new HashMap<>(); 612 } 613 guis.put(configurationGenerator.getNuxeoConf().toString(), gui); 614 } 615 616 public NuxeoLauncher(ConfigurationGenerator configurationGenerator) { 617 this.configurationGenerator = configurationGenerator; 618 init(); 619 } 620 621 /** 622 * @since 5.6 623 */ 624 public void init() { 625 if (!configurationGenerator.init(true)) { 626 throw new IllegalStateException("Initialization failed"); 627 } 628 statusServletClient = new StatusServletClient(configurationGenerator); 629 statusServletClient.setKey(configurationGenerator.getUserConfig().getProperty(Environment.SERVER_STATUS_KEY)); 630 processManager = getOSProcessManager(); 631 processRegex = "^(?!/bin/sh).*" + Pattern.quote(configurationGenerator.getNuxeoConf().getPath()) + ".*" 632 + Pattern.quote(getServerPrint()) + ".*$"; 633 634 // Set OS-specific decorations 635 if (PlatformUtils.isMac()) { 636 System.setProperty("com.apple.mrj.application.apple.menu.about.name", "NuxeoCtl"); 637 } 638 } 639 640 private ProcessManager getOSProcessManager() { 641 if (PlatformUtils.isLinux() || SystemUtils.IS_OS_AIX) { 642 UnixProcessManager unixProcessManager = new UnixProcessManager(); 643 return unixProcessManager; 644 } else if (PlatformUtils.isMac()) { 645 return new MacProcessManager(); 646 } else if (SystemUtils.IS_OS_SUN_OS) { 647 return new SolarisProcessManager(); 648 } else if (PlatformUtils.isWindows()) { 649 WindowsProcessManager windowsProcessManager = new WindowsProcessManager(); 650 return windowsProcessManager.isUsable() ? windowsProcessManager : new PureJavaProcessManager(); 651 } else { 652 return new PureJavaProcessManager(); 653 } 654 } 655 656 public static class SolarisProcessManager extends UnixProcessManager { 657 658 protected static final String SOLARIS_11 = "5.11"; 659 660 protected static final String SOLARIS_10 = "5.10"; 661 662 protected static final String[] SOLARIS_11_PS = { "/usr/bin/ps", "auxww" }; 663 664 protected static final String[] SOLARIS_10_PS = { "/usr/ucb/ps", "auxww" }; 665 666 protected static final Pattern PS_OUTPUT_LINE = Pattern.compile("^" + "[^\\s]+\\s+" // USER 667 + "([0-9]+)\\s+" // PID 668 + "[0-9.\\s]+" // %CPU %MEM SZ RSS (may be collapsed) 669 + "[^\\s]+\\s+" // TT (no starting digit) 670 + "[^\\s]+\\s+" // S 671 + "[^\\s]+\\s+" // START 672 + "[^\\s]+\\s+" // TIME 673 + "(.*)$" // COMMAND 674 ); 675 676 protected String solarisVersion; 677 678 protected String getSolarisVersion() { 679 if (solarisVersion == null) { 680 List<String> lines; 681 try { 682 lines = execute(new String[] { "/usr/bin/uname", "-r" }); 683 } catch (IOException e) { 684 log.debug(e.getMessage(), e); 685 lines = Collections.emptyList(); 686 } 687 if (lines.isEmpty()) { 688 solarisVersion = "?"; 689 } else { 690 solarisVersion = lines.get(0).trim(); 691 } 692 } 693 return solarisVersion; 694 } 695 696 @Override 697 protected String[] psCommand() { 698 if (SOLARIS_11.equals(getSolarisVersion())) { 699 return SOLARIS_11_PS; 700 } 701 return null; 702 } 703 704 protected Matcher getLineMatcher(String line) { 705 return PS_OUTPUT_LINE.matcher(line); 706 } 707 708 @Override 709 public String findPid(String regex) throws IOException { 710 if (SOLARIS_11.equals(getSolarisVersion())) { 711 Pattern commandPattern = Pattern.compile(regex); 712 for (String line : execute(psCommand())) { 713 Matcher lineMatcher = getLineMatcher(line); 714 if (lineMatcher.matches()) { 715 String pid = lineMatcher.group(1); 716 String command = lineMatcher.group(2); 717 Matcher commandMatcher = commandPattern.matcher(command); 718 if (commandMatcher.find()) { 719 return pid; 720 } 721 } 722 } 723 } else { 724 log.debug("Unsupported Solaris version: " + solarisVersion); 725 } 726 return null; 727 } 728 729 protected List<String> execute(String... command) throws IOException { 730 Process process = new ProcessBuilder(command).start(); 731 List<String> lines = IOUtils.readLines(process.getInputStream()); 732 return lines; 733 } 734 } 735 736 /** 737 * Do not directly call this method without a call to {@link #checkNoRunningServer()} 738 * 739 * @see #doStart() 740 * @throws IOException In case of issue with process. 741 * @throws InterruptedException If any thread has interrupted the current thread. 742 */ 743 protected void start(boolean logProcessOutput) throws IOException, InterruptedException { 744 List<String> startCommand = new ArrayList<>(); 745 startCommand.add(getJavaExecutable().getPath()); 746 startCommand.addAll(getJavaOptsPropertyAsList()); 747 startCommand.add("-cp"); 748 startCommand.add(getClassPath()); 749 startCommand.addAll(getNuxeoProperties()); 750 startCommand.addAll(getServerProperties()); 751 setServerStartCommand(startCommand); 752 for (String param : params) { 753 startCommand.add(param); 754 } 755 ProcessBuilder pb = new ProcessBuilder(getOSCommand(startCommand)); 756 pb.directory(configurationGenerator.getNuxeoHome()); 757 log.debug("Server command: " + pb.command()); 758 nuxeoProcess = pb.start(); 759 Thread.sleep(1000); 760 boolean processExited = false; 761 // Check if process exited early 762 if (nuxeoProcess == null) { 763 log.error(String.format("Server start failed with command: %s", pb.command())); 764 if (PlatformUtils.isWindows() && configurationGenerator.getNuxeoHome().getPath().contains(" ")) { 765 // NXP-17679 766 log.error("The server path must not contain spaces under Windows."); 767 } 768 return; 769 } 770 try { 771 int exitValue = nuxeoProcess.exitValue(); 772 if (exitValue != 0) { 773 log.error(String.format("Server start failed (%d).", exitValue)); 774 } 775 processExited = true; 776 } catch (IllegalThreadStateException e) { 777 // Normal case 778 } 779 logProcessStreams(nuxeoProcess, processExited || logProcessOutput); 780 if (!processExited) { 781 if (getPid() != null) { 782 log.warn("Server started with process ID " + pid + "."); 783 } else { 784 log.warn("Sent server start command but could not get process ID."); 785 } 786 } 787 } 788 789 /** 790 * Gets the Java options with 'nuxeo.*' properties substituted. It enables usage of property like ${nuxeo.log.dir} 791 * inside JAVA_OPTS. 792 * 793 * @return the java options string. 794 */ 795 protected String getJavaOptsProperty() { 796 String ret = System.getProperty(JAVA_OPTS_PROPERTY, JAVA_OPTS_DEFAULT); 797 ret = StrSubstitutor.replace(ret, configurationGenerator.getUserConfig()); 798 return ret; 799 } 800 801 /** 802 * @return Java OPTS split on spaces followed by an even number of quotes (or zero) 803 * @since 7.10 804 */ 805 protected List<String> getJavaOptsPropertyAsList() { 806 String javaOptsProperty = getJavaOptsProperty(); 807 log.debug("JAVA OPTS:" + javaOptsProperty); 808 return Arrays.asList(javaOptsProperty.split("[ ]+(?=([^\"]*\"[^\"]*\")*[^\"]*$)")); 809 } 810 811 /** 812 * Check if some server is already running (from another thread) and throw a Runtime exception if it finds one. That 813 * method will work where {@link #isRunning()} won't. 814 * 815 * @throws IllegalThreadStateException Thrown if a server is already running. 816 */ 817 public void checkNoRunningServer() throws IllegalStateException { 818 try { 819 String existingPid = getPid(); 820 if (existingPid != null) { 821 errorValue = EXIT_CODE_OK; 822 throw new IllegalStateException("A server is running with process ID " + existingPid); 823 } 824 } catch (IOException e) { 825 log.warn("Could not check existing process: " + e.getMessage()); 826 } 827 } 828 829 /** 830 * @return (since 5.5) Array list with created stream gobbler threads. 831 */ 832 public ArrayList<ThreadedStreamGobbler> logProcessStreams(Process process, boolean logProcessOutput) { 833 ArrayList<ThreadedStreamGobbler> sgArray = new ArrayList<>(); 834 ThreadedStreamGobbler inputSG, errorSG; 835 if (logProcessOutput) { 836 inputSG = new ThreadedStreamGobbler(process.getInputStream(), System.out); 837 errorSG = new ThreadedStreamGobbler(process.getErrorStream(), System.err); 838 } else { 839 inputSG = new ThreadedStreamGobbler(process.getInputStream(), SimpleLog.LOG_LEVEL_OFF); 840 errorSG = new ThreadedStreamGobbler(process.getErrorStream(), SimpleLog.LOG_LEVEL_OFF); 841 } 842 inputSG.start(); 843 errorSG.start(); 844 sgArray.add(inputSG); 845 sgArray.add(errorSG); 846 return sgArray; 847 } 848 849 protected abstract String getServerPrint(); 850 851 /** 852 * Will wrap, if necessary, the command within a Shell command 853 * 854 * @param roughCommand Java command which will be run 855 * @return wrapped command depending on the OS 856 */ 857 private List<String> getOSCommand(List<String> roughCommand) { 858 ArrayList<String> osCommand = new ArrayList<>(); 859 if (PlatformUtils.isLinux() || PlatformUtils.isMac()) { 860 String linearizedCommand = new String(); 861 for (String commandToken : roughCommand) { 862 if (StringUtils.isBlank(commandToken)) { 863 continue; 864 } 865 if (commandToken.contains(" ")) { 866 commandToken = commandToken.replaceAll(" ", "\\\\ "); 867 } 868 linearizedCommand += " " + commandToken; 869 } 870 osCommand.add("/bin/sh"); 871 osCommand.add("-c"); 872 osCommand.add(linearizedCommand); 873 } else { 874 for (String commandToken : roughCommand) { 875 if (StringUtils.isBlank(commandToken)) { 876 continue; 877 } 878 osCommand.add(commandToken); 879 } 880 } 881 return osCommand; 882 } 883 884 protected abstract Collection<? extends String> getServerProperties(); 885 886 protected abstract void setServerStartCommand(List<String> command); 887 888 private File getJavaExecutable() { 889 File javaExec = new File(System.getProperty("java.home"), "bin" + File.separator + "java"); 890 return javaExec; 891 } 892 893 protected abstract String getClassPath(); 894 895 /** 896 * @since 5.6 897 */ 898 protected abstract String getShutdownClassPath(); 899 900 protected Collection<? extends String> getNuxeoProperties() { 901 ArrayList<String> nuxeoProperties = new ArrayList<>(); 902 nuxeoProperties.add(String.format("-D%s=%s", Environment.NUXEO_HOME, configurationGenerator.getNuxeoHome() 903 .getPath())); 904 nuxeoProperties.add(String.format("-D%s=%s", ConfigurationGenerator.NUXEO_CONF, 905 configurationGenerator.getNuxeoConf().getPath())); 906 nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_LOG_DIR)); 907 nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_DATA_DIR)); 908 nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_TMP_DIR)); 909 if (!DEFAULT_NUXEO_CONTEXT_PATH.equals(configurationGenerator.getUserConfig().getProperty( 910 Environment.NUXEO_CONTEXT_PATH))) { 911 nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_CONTEXT_PATH)); 912 } 913 if (overrideJavaTmpDir) { 914 nuxeoProperties.add("-Djava.io.tmpdir=" 915 + configurationGenerator.getUserConfig().getProperty(Environment.NUXEO_TMP_DIR)); 916 } 917 return nuxeoProperties; 918 } 919 920 private String getNuxeoProperty(String property) { 921 return "-D" + property + "=" + configurationGenerator.getUserConfig().getProperty(property); 922 } 923 924 protected String addToClassPath(String cp, String filename) { 925 File classPathEntry = new File(configurationGenerator.getNuxeoHome(), filename); 926 if (!classPathEntry.exists()) { 927 classPathEntry = new File(filename); 928 } 929 if (!classPathEntry.exists()) { 930 throw new RuntimeException("Tried to add inexistent classpath entry: " + filename); 931 } 932 cp += System.getProperty("path.separator") + classPathEntry.getPath(); 933 return cp; 934 } 935 936 /** 937 * @since 5.6 938 */ 939 protected static void initParserOptions() { 940 if (launcherOptions == null) { 941 launcherOptions = new Options(); 942 // help option 943 launcherOptions.addOption(Option.builder("h").longOpt(OPTION_HELP).desc(OPTION_HELP_DESC).build()); 944 // Quiet option 945 launcherOptions.addOption(Option.builder("q").longOpt(OPTION_QUIET).desc(OPTION_QUIET_DESC).build()); 946 { // Debug options (mutually exclusive) 947 OptionGroup debugOptions = new OptionGroup(); 948 // Debug option 949 debugOptions.addOption(Option.builder("d") 950 .longOpt(OPTION_DEBUG) 951 .desc(OPTION_DEBUG_DESC) 952 .hasArgs() 953 .argName(OPTION_DEBUG_CATEGORY_ARG_NAME) 954 .optionalArg(true) 955 .valueSeparator(',') 956 .build()); 957 // Debug category option 958 debugOptions.addOption(Option.builder(OPTION_DEBUG_CATEGORY) 959 .desc(OPTION_DEBUG_CATEGORY_DESC) 960 .hasArgs() 961 .argName(OPTION_DEBUG_CATEGORY_ARG_NAME) 962 .optionalArg(true) 963 .valueSeparator(',') 964 .build()); 965 launcherOptions.addOptionGroup(debugOptions); 966 } 967 // For help output purpose only: that option is managed and swallowed by the nuxeoctl Shell script 968 launcherOptions.addOption(Option.builder() 969 .longOpt("debug-launcher") 970 .desc("Linux-only. Activate Java debugging mode on the Launcher.") 971 .build()); 972 // Instance CLID option 973 launcherOptions.addOption(Option.builder().longOpt(OPTION_CLID).desc(OPTION_CLID_DESC).hasArg().build()); 974 { // Output options (mutually exclusive) 975 OptionGroup outputOptions = new OptionGroup(); 976 // XML option 977 outputOptions.addOption(Option.builder().longOpt(OPTION_XML).desc(OPTION_XML_DESC).build()); 978 // JSON option 979 outputOptions.addOption(Option.builder().longOpt(OPTION_JSON).desc(OPTION_JSON_DESC).build()); 980 launcherOptions.addOptionGroup(outputOptions); 981 } 982 // GUI option 983 launcherOptions.addOption(Option.builder() 984 .longOpt(OPTION_GUI) 985 .desc(OPTION_GUI_DESC) 986 .hasArg() 987 .argName("true|false|yes|no") 988 .build()); 989 // Package management option 990 launcherOptions.addOption(Option.builder().longOpt(OPTION_NODEPS).desc(OPTION_NODEPS_DESC).build()); 991 // Relax on target platform option 992 launcherOptions.addOption(Option.builder() 993 .longOpt(OPTION_RELAX) 994 .desc(OPTION_RELAX_DESC) 995 .hasArg() 996 .argName("true|false|yes|no|ask") 997 .build()); 998 // Accept option 999 launcherOptions.addOption(Option.builder() 1000 .longOpt(OPTION_ACCEPT) 1001 .desc(OPTION_ACCEPT_DESC) 1002 .hasArg() 1003 .argName("true|false|yes|no|ask") 1004 .build()); 1005 // Allow SNAPSHOT option 1006 launcherOptions.addOption(Option.builder("s").longOpt(OPTION_SNAPSHOT).desc(OPTION_SNAPSHOT_DESC).build()); 1007 // Force option 1008 launcherOptions.addOption(Option.builder("f").longOpt(OPTION_FORCE).desc(OPTION_FORCE_DESC).build()); 1009 // Strict option 1010 launcherOptions.addOption(Option.builder().longOpt(OPTION_STRICT).desc(OPTION_STRICT_DESC).build()); 1011 1012 // Ignore missing option 1013 launcherOptions.addOption(Option.builder("im") 1014 .longOpt(OPTION_IGNORE_MISSING) 1015 .desc(OPTION_IGNORE_MISSING_DESC) 1016 .build()); 1017 // Hide deprecation warnings option 1018 launcherOptions.addOption(Option.builder("hdw") 1019 .longOpt(OPTION_HIDE_DEPRECATION) 1020 .desc(OPTION_HIDE_DEPRECATION_DESC) 1021 .build()); 1022 // Encrypt option 1023 launcherOptions.addOption(Option.builder() 1024 .longOpt(OPTION_ENCRYPT) 1025 .desc(OPTION_ENCRYPT_DESC) 1026 .hasArg() 1027 .argName(OPTION_ENCRYPT_ARG_NAME) 1028 .optionalArg(true) 1029 .build()); 1030 { // Config options (mutually exclusive) 1031 OptionGroup configOptions = new OptionGroup(); 1032 // Set option 1033 configOptions.addOption(Option.builder().longOpt(OPTION_SET).desc(OPTION_SET_DESC).build()); 1034 configOptions.addOption(Option.builder().longOpt(OPTION_GET).desc(OPTION_GET_DESC).build()); 1035 configOptions.addOption(Option.builder() 1036 .longOpt(OPTION_GET_REGEXP) 1037 .desc(OPTION_GET_REGEXP_DESC) 1038 .build()); 1039 launcherOptions.addOptionGroup(configOptions); 1040 } 1041 } 1042 } 1043 1044 /** 1045 * @since 5.6 1046 */ 1047 protected static CommandLine parseOptions(String[] args) throws ParseException { 1048 initParserOptions(); 1049 CommandLineParser parser = new DefaultParser(); 1050 CommandLine cmdLine = null; 1051 cmdLine = parser.parse(launcherOptions, args); 1052 if (cmdLine.hasOption(OPTION_HELP)) { 1053 cmdLine.getArgList().add(OPTION_HELP); 1054 setQuiet(); 1055 } else if (cmdLine.getArgList().isEmpty()) { 1056 throw new ParseException("Missing command."); 1057 } 1058 // Common options to the Launcher and the ConfigurationGenerator 1059 if (cmdLine.hasOption(OPTION_QUIET) || cmdLine.hasOption(OPTION_XML) || cmdLine.hasOption(OPTION_JSON)) { 1060 setQuiet(); 1061 } 1062 if (cmdLine.hasOption(OPTION_DEBUG)) { 1063 setDebug(cmdLine.getOptionValues(OPTION_DEBUG), "org.nuxeo.launcher"); 1064 } 1065 if (cmdLine.hasOption(OPTION_DEBUG_CATEGORY)) { 1066 setDebug(cmdLine.getOptionValues(OPTION_DEBUG_CATEGORY), "org.nuxeo.launcher"); 1067 } 1068 if (cmdLine.hasOption(OPTION_FORCE) || cmdLine.hasOption(OPTION_STRICT)) { 1069 setStrict(true); 1070 } 1071 return cmdLine; 1072 } 1073 1074 public static void main(String[] args) { 1075 NuxeoLauncher launcher = null; 1076 try { 1077 launcher = createLauncher(args); 1078 if (launcher.commandRequiresNoGUI()) { 1079 launcher.useGui = false; 1080 } 1081 if (launcher.useGui && launcher.getGUI() == null) { 1082 launcher.setGUI(new NuxeoLauncherGUI(launcher)); 1083 } 1084 launch(launcher); 1085 } catch (ParseException e) { 1086 log.error("Invalid command line. " + e.getMessage()); 1087 log.debug(e, e); 1088 printShortHelp(); 1089 System.exit(launcher == null || launcher.errorValue == EXIT_CODE_OK ? EXIT_CODE_INVALID 1090 : launcher.errorValue); 1091 } catch (IOException | PackageException | ConfigurationException | GeneralSecurityException e) { 1092 log.error(e.getMessage()); 1093 log.debug(e, e); 1094 System.exit(launcher == null || launcher.errorValue == EXIT_CODE_OK ? EXIT_CODE_INVALID 1095 : launcher.errorValue); 1096 } catch (Exception e) { 1097 log.error("Cannot execute command. " + e.getMessage()); 1098 log.debug(e, e); 1099 System.exit(1); 1100 } 1101 } 1102 1103 /** 1104 * @since 5.5 1105 * @param launcher 1106 * @throws PackageException 1107 * @throws IOException 1108 * @throws ConfigurationException 1109 * @throws ParseException 1110 * @throws GeneralSecurityException 1111 */ 1112 public static void launch(final NuxeoLauncher launcher) throws IOException, PackageException, 1113 ConfigurationException, ParseException, GeneralSecurityException { 1114 boolean commandSucceeded = true; 1115 if (launcher.commandIs(null)) { 1116 return; 1117 } 1118 if (launcher.commandRequiresNoRunningServer()) { 1119 launcher.checkNoRunningServer(); 1120 } 1121 if (launcher.commandIs(OPTION_HELP)) { 1122 printLongHelp(); 1123 } else if (launcher.commandIs("status")) { 1124 String statusMsg = launcher.status(); 1125 launcher.errorValue = launcher.getStatus(); 1126 if (!quiet) { 1127 log.warn(statusMsg); 1128 if (launcher.isStarted()) { 1129 log.info("Go to " + launcher.getURL()); 1130 log.info(launcher.getStartupSummary()); 1131 } 1132 } 1133 } else if (launcher.commandIs("startbg")) { 1134 commandSucceeded = launcher.doStart(); 1135 } else if (launcher.commandIs("start")) { 1136 if (launcher.useGui) { 1137 launcher.getGUI().start(); 1138 } else { 1139 commandSucceeded = launcher.doStartAndWait(); 1140 } 1141 } else if (launcher.commandIs("console")) { 1142 launcher.executor.execute(new Runnable() { 1143 @Override 1144 public void run() { 1145 launcher.addShutdownHook(); 1146 try { 1147 if (!launcher.doStart(true)) { 1148 launcher.removeShutdownHook(); 1149 System.exit(1); 1150 } else if (!quiet) { 1151 log.info("Go to " + launcher.getURL()); 1152 } 1153 } catch (PackageException e) { 1154 log.error("Could not initialize the packaging subsystem", e); 1155 launcher.removeShutdownHook(); 1156 System.exit(EXIT_CODE_ERROR); 1157 } 1158 } 1159 }); 1160 } else if (launcher.commandIs("stop")) { 1161 if (launcher.useGui) { 1162 launcher.getGUI().stop(); 1163 } else { 1164 launcher.stop(); 1165 } 1166 } else if (launcher.commandIs("restartbg")) { 1167 launcher.stop(); 1168 commandSucceeded = launcher.doStart(); 1169 } else if (launcher.commandIs("restart")) { 1170 launcher.stop(); 1171 commandSucceeded = launcher.doStartAndWait(); 1172 } else if (launcher.commandIs("wizard")) { 1173 commandSucceeded = launcher.startWizard(); 1174 } else if (launcher.commandIs("configure")) { 1175 launcher.configure(); 1176 } else if (launcher.commandIs("pack")) { 1177 launcher.pack(); 1178 } else if (launcher.commandIs("mp-list")) { 1179 launcher.pkgList(); 1180 } else if (launcher.commandIs("mp-listall")) { 1181 launcher.pkgListAll(); 1182 } else if (launcher.commandIs("mp-init")) { 1183 commandSucceeded = launcher.pkgInit(); 1184 } else if (launcher.commandIs("mp-purge")) { 1185 commandSucceeded = launcher.pkgPurge(); 1186 } else if (launcher.commandIs("mp-add")) { 1187 if (launcher.cmdLine.hasOption(OPTION_NODEPS)) { 1188 commandSucceeded = launcher.pkgAdd(launcher.params); 1189 } else { 1190 commandSucceeded = launcher.pkgRequest(Arrays.asList(launcher.params), null, null, null); 1191 } 1192 } else if (launcher.commandIs("mp-install")) { 1193 if (launcher.cmdLine.hasOption(OPTION_NODEPS)) { 1194 commandSucceeded = launcher.pkgInstall(launcher.params); 1195 } else { 1196 commandSucceeded = launcher.pkgRequest(null, Arrays.asList(launcher.params), null, null); 1197 } 1198 } else if (launcher.commandIs("mp-uninstall")) { 1199 if (launcher.cmdLine.hasOption(OPTION_NODEPS)) { 1200 commandSucceeded = launcher.pkgUninstall(launcher.params); 1201 } else { 1202 commandSucceeded = launcher.pkgRequest(null, null, Arrays.asList(launcher.params), null); 1203 } 1204 } else if (launcher.commandIs("mp-remove")) { 1205 if (launcher.cmdLine.hasOption(OPTION_NODEPS)) { 1206 commandSucceeded = launcher.pkgRemove(launcher.params); 1207 } else { 1208 commandSucceeded = launcher.pkgRequest(null, null, null, Arrays.asList(launcher.params)); 1209 } 1210 } else if (launcher.commandIs("mp-request")) { 1211 if (launcher.cmdLine.hasOption(OPTION_NODEPS)) { 1212 throw new ParseException("The command mp-request is not available with the --nodeps option"); 1213 } else { 1214 commandSucceeded = launcher.pkgCompoundRequest(Arrays.asList(launcher.params)); 1215 } 1216 } else if (launcher.commandIs("mp-set")) { 1217 commandSucceeded = launcher.pkgSetRequest(Arrays.asList(launcher.params), 1218 launcher.cmdLine.hasOption(OPTION_NODEPS)); 1219 } else if (launcher.commandIs("mp-hotfix")) { 1220 commandSucceeded = launcher.pkgHotfix(); 1221 } else if (launcher.commandIs("mp-upgrade")) { 1222 commandSucceeded = launcher.pkgUpgrade(); 1223 } else if (launcher.commandIs("mp-reset")) { 1224 commandSucceeded = launcher.pkgReset(); 1225 } else if (launcher.commandIs("mp-update")) { 1226 commandSucceeded = launcher.pkgRefreshCache(); 1227 } else if (launcher.commandIs("showconf")) { 1228 launcher.showConfig(); 1229 } else if (launcher.commandIs("mp-show")) { 1230 commandSucceeded = launcher.pkgShow(launcher.params); 1231 } else if (launcher.commandIs("encrypt")) { 1232 launcher.encrypt(); 1233 } else if (launcher.commandIs("decrypt")) { 1234 launcher.decrypt(); 1235 } else if (launcher.commandIs("config")) { 1236 launcher.config(); 1237 } else { 1238 log.error("Unknown command " + launcher.command); 1239 printLongHelp(); 1240 launcher.errorValue = EXIT_CODE_INVALID; 1241 } 1242 if (launcher.xmlOutput && launcher.command.startsWith("mp-")) { 1243 launcher.printXMLOutput(); 1244 } 1245 commandSucceeded = commandSucceeded && launcher.errorValue == EXIT_CODE_OK; 1246 if (!commandSucceeded && !quiet || debug) { 1247 launcher.cset.log(commandSucceeded && debug); 1248 } 1249 if (!commandSucceeded) { 1250 System.exit(launcher.errorValue); 1251 } 1252 } 1253 1254 /** 1255 * @throws ConfigurationException 1256 * @throws GeneralSecurityException 1257 * @since 7.4 1258 */ 1259 protected void encrypt() throws ConfigurationException, GeneralSecurityException { 1260 Crypto crypto = configurationGenerator.getCrypto(); 1261 String algorithm = cmdLine.getOptionValue(OPTION_ENCRYPT, null); 1262 if (params.length == 0) { 1263 Console console = System.console(); 1264 if (console == null) { 1265 errorValue = EXIT_CODE_INVALID; 1266 return; 1267 } 1268 params = new String[] { console.readLine("Please enter the value to encrypt: ") }; 1269 } 1270 for (String strToEncrypt : params) { 1271 String encryptedString = crypto.encrypt(algorithm, strToEncrypt.getBytes()); 1272 System.out.println(encryptedString); 1273 } 1274 } 1275 1276 /** 1277 * @throws ConfigurationException 1278 * @since 7.4 1279 */ 1280 protected void decrypt() throws ConfigurationException { 1281 Crypto crypto = configurationGenerator.getCrypto(); 1282 Console console = System.console(); 1283 if (console == null) { 1284 errorValue = EXIT_CODE_ERROR; 1285 return; 1286 } 1287 if (!crypto.verifyKey(console.readPassword("Please enter the secret key: "))) { 1288 errorValue = EXIT_CODE_INVALID; 1289 return; 1290 } 1291 for (String strToDecrypt : params) { 1292 System.out.println(Crypto.getChars(crypto.decrypt(strToDecrypt))); 1293 } 1294 } 1295 1296 /** 1297 * @throws ConfigurationException 1298 * @throws IOException 1299 * @throws GeneralSecurityException 1300 * @since 7.4 1301 */ 1302 protected void config() throws ConfigurationException, IOException, GeneralSecurityException { 1303 if (cmdLine.hasOption(OPTION_SET) || !cmdLine.hasOption(OPTION_GET) && !cmdLine.hasOption(OPTION_GET_REGEXP) 1304 && params.length == 2) { 1305 setConfigProperties(); 1306 } else { // OPTION_GET || OPTION_GET_REGEXP || !OPTION_SET && params.length != 2 1307 getConfigProperties(); 1308 } 1309 } 1310 1311 /** 1312 * @since 7.4 1313 */ 1314 protected void getConfigProperties() { 1315 boolean isRegexp = cmdLine.hasOption(OPTION_GET_REGEXP); 1316 CryptoProperties userConfig = configurationGenerator.getUserConfig(); 1317 List<String> keys; 1318 if (isRegexp) { 1319 keys = new ArrayList<>(); 1320 for (Object key : userConfig.keySet()) { 1321 for (String param : params) { 1322 Pattern pattern = Pattern.compile(param, Pattern.CASE_INSENSITIVE); 1323 if (pattern.matcher((String) key).find()) { 1324 keys.add((String) key); 1325 } 1326 } 1327 } 1328 if (keys.isEmpty()) { 1329 errorValue = EXIT_CODE_NOT_CONFIGURED; 1330 } 1331 } else { 1332 keys = Arrays.asList(params); 1333 } 1334 1335 Crypto crypto = userConfig.getCrypto(); 1336 boolean keyChecked = false; // Secret key is asked only once 1337 boolean raw = true; 1338 StringBuilder sb = new StringBuilder(); 1339 final String newLine = System.getProperty("line.separator"); 1340 for (String key : keys) { 1341 String value = userConfig.getProperty(key, raw); 1342 if (value == null) { 1343 errorValue = EXIT_CODE_NOT_CONFIGURED; 1344 sb.append(OUTPUT_UNSET_VALUE + newLine); 1345 } else { 1346 if (raw && !keyChecked && Crypto.isEncrypted(value)) { 1347 keyChecked = true; 1348 Console console = System.console(); 1349 if (console != null && crypto.verifyKey(console.readPassword("Please enter the secret key: "))) { 1350 raw = false; 1351 value = new String(crypto.decrypt(value)); 1352 } else { 1353 errorValue = EXIT_CODE_ERROR; 1354 } 1355 } 1356 if (isRegexp) { 1357 sb.append(key + "="); 1358 } 1359 sb.append(value + newLine); 1360 } 1361 } 1362 System.out.println(sb.toString()); 1363 } 1364 1365 /** 1366 * @throws IOException 1367 * @throws GeneralSecurityException 1368 * @since 7.4 1369 */ 1370 protected void setConfigProperties() throws ConfigurationException, IOException, GeneralSecurityException { 1371 Crypto crypto = configurationGenerator.getCrypto(); 1372 boolean doEncrypt = cmdLine.hasOption(OPTION_ENCRYPT); 1373 String algorithm = cmdLine.getOptionValue(OPTION_ENCRYPT, null); 1374 Map<String, String> changedParameters = new HashMap<>(); 1375 for (Iterator<String> iterator = Arrays.asList(params).iterator(); iterator.hasNext();) { 1376 String key = iterator.next(); 1377 String value; 1378 if (iterator.hasNext()) { 1379 value = iterator.next(); 1380 if (doEncrypt) { 1381 value = crypto.encrypt(algorithm, value.getBytes()); 1382 } else if (Environment.CRYPT_KEY.equals(key) || Environment.CRYPT_KEYSTORE_PASS.equals(key)) { 1383 value = Base64.encodeBase64String(value.getBytes()); 1384 } 1385 } else { 1386 Console console = System.console(); 1387 if (console == null) { 1388 errorValue = EXIT_CODE_INVALID; 1389 return; 1390 } 1391 if (doEncrypt) { 1392 value = crypto.encrypt(algorithm, 1393 Crypto.getBytes(console.readPassword("Please enter the value for %s: ", key))); 1394 } else if (Environment.CRYPT_KEY.equals(key) || Environment.CRYPT_KEYSTORE_PASS.equals(key)) { 1395 value = Base64.encodeBase64String(Crypto.getBytes(console.readPassword( 1396 "Please enter the value for %s: ", key))); 1397 } else { 1398 value = console.readLine("Please enter the value for %s: ", key); 1399 } 1400 } 1401 changedParameters.put(key, value); 1402 } 1403 String template = cmdLine.getOptionValue(OPTION_SET); 1404 Map<String, String> oldValues; 1405 if (template == null) { 1406 oldValues = configurationGenerator.setProperties(changedParameters); 1407 } else { 1408 oldValues = configurationGenerator.setProperties(template, changedParameters); 1409 } 1410 log.debug("Old values: " + oldValues); 1411 } 1412 1413 /** 1414 * Since 5.5 1415 */ 1416 protected boolean pack() { 1417 try { 1418 configurationGenerator.setProperty(PARAM_UPDATECENTER_DISABLED, "true"); 1419 List<String> startCommand = new ArrayList<>(); 1420 startCommand.add(getJavaExecutable().getPath()); 1421 startCommand.addAll(Arrays.asList(getJavaOptsProperty().split(" "))); 1422 startCommand.add("-cp"); 1423 String classpath = getClassPath(); 1424 classpath = addToClassPath(classpath, "bin" + File.separator + "nuxeo-launcher.jar"); 1425 classpath = getClassPath(classpath, configurationGenerator.getServerConfigurator().getServerLibDir()); 1426 classpath = getClassPath(classpath, configurationGenerator.getServerConfigurator().getNuxeoLibDir()); 1427 classpath = getClassPath(classpath, new File(configurationGenerator.getRuntimeHome(), "bundles")); 1428 startCommand.add(classpath); 1429 startCommand.addAll(getNuxeoProperties()); 1430 if (configurationGenerator.isTomcat) { 1431 startCommand.add(PACK_TOMCAT_CLASS); 1432 } else { 1433 errorValue = EXIT_CODE_ERROR; 1434 return false; 1435 } 1436 startCommand.add(configurationGenerator.getRuntimeHome().getPath()); 1437 for (String param : params) { 1438 startCommand.add(param); 1439 } 1440 ProcessBuilder pb = new ProcessBuilder(getOSCommand(startCommand)); 1441 pb.directory(configurationGenerator.getNuxeoHome()); 1442 log.debug("Pack command: " + pb.command()); 1443 Process process = pb.start(); 1444 ArrayList<ThreadedStreamGobbler> sgArray = logProcessStreams(process, true); 1445 Thread.sleep(100); 1446 process.waitFor(); 1447 waitForProcessStreams(sgArray); 1448 } catch (IOException | InterruptedException e) { 1449 errorValue = EXIT_CODE_ERROR; 1450 log.error("Could not start process", e); 1451 } catch (ConfigurationException e) { 1452 errorValue = EXIT_CODE_ERROR; 1453 log.error(e); 1454 } 1455 return errorValue == EXIT_CODE_OK; 1456 } 1457 1458 protected boolean startWizard() throws PackageException { 1459 if (!configurationGenerator.getServerConfigurator().isWizardAvailable()) { 1460 log.error("Sorry, the wizard is not available within that server."); 1461 return false; 1462 } 1463 if (isRunning()) { 1464 log.error("Server already running. " + "Please stop it before calling \"wizard\" command " 1465 + "or use the Admin Center instead of the wizard."); 1466 return false; 1467 } 1468 if (reloadConfiguration) { 1469 configurationGenerator = new ConfigurationGenerator(quiet, debug); 1470 configurationGenerator.init(); 1471 reloadConfiguration = false; 1472 } 1473 configurationGenerator.getUserConfig().setProperty(ConfigurationGenerator.PARAM_WIZARD_DONE, "false"); 1474 return doStart(); 1475 } 1476 1477 /** 1478 * @throws PackageException 1479 * @see #doStartAndWait(boolean) 1480 */ 1481 public boolean doStartAndWait() throws PackageException { 1482 boolean started = doStartAndWait(false); 1483 if (started && !quiet) { 1484 log.info("Go to " + getURL()); 1485 } 1486 return started; 1487 } 1488 1489 /** 1490 * @see #stop(boolean) 1491 */ 1492 public void stop() { 1493 stop(false); 1494 } 1495 1496 /** 1497 * Call {@link #doStart(boolean)} with false as parameter. 1498 * 1499 * @see #doStart(boolean) 1500 * @return true if the server started successfully 1501 * @throws PackageException 1502 */ 1503 public boolean doStart() throws PackageException { 1504 boolean started = doStart(false); 1505 if (started && !quiet) { 1506 log.info("Go to " + getURL()); 1507 } 1508 return started; 1509 } 1510 1511 /** 1512 * Whereas {@link #doStart()} considers the server as started when the process is running, {@link #doStartAndWait()} 1513 * waits for effective start by watching the logs 1514 * 1515 * @param logProcessOutput Must process output stream must be logged or not. 1516 * @return true if the server started successfully 1517 * @throws PackageException 1518 */ 1519 public boolean doStartAndWait(boolean logProcessOutput) throws PackageException { 1520 boolean commandSucceeded = false; 1521 if (doStart(logProcessOutput)) { 1522 addShutdownHook(); 1523 try { 1524 if (configurationGenerator.isWizardRequired() || waitForEffectiveStart()) { 1525 commandSucceeded = true; 1526 } 1527 removeShutdownHook(); 1528 } catch (InterruptedException e) { 1529 // do nothing 1530 } 1531 } 1532 return commandSucceeded; 1533 } 1534 1535 protected void removeShutdownHook() { 1536 try { 1537 Runtime.getRuntime().removeShutdownHook(shutdownHook); 1538 log.debug("Removed shutdown hook"); 1539 } catch (IllegalStateException e) { 1540 // the virtual machine is already in the process of shutting down 1541 } 1542 } 1543 1544 /** 1545 * @return true if Nuxeo is ready 1546 * @throws InterruptedException 1547 */ 1548 protected boolean waitForEffectiveStart() throws InterruptedException { 1549 long startTime = new Date().getTime(); 1550 int startMaxWait = Integer.parseInt(configurationGenerator.getUserConfig().getProperty(START_MAX_WAIT_PARAM, 1551 getDefaultMaxWait())); 1552 log.debug("Will wait for effective start during " + startMaxWait + " seconds."); 1553 final StringBuilder startSummary = new StringBuilder(); 1554 final String newLine = System.getProperty("line.separator"); 1555 boolean isReady = false; 1556 long deltaTime = 0; 1557 // Wait for status servlet ready 1558 do { 1559 try { 1560 isReady = statusServletClient.init(); 1561 } catch (SocketTimeoutException e) { 1562 if (!quiet) { 1563 System.out.print("."); 1564 } 1565 } 1566 deltaTime = (new Date().getTime() - startTime) / 1000; 1567 } while (!isReady && deltaTime < startMaxWait && isRunning()); 1568 isReady = false; 1569 // Wait for effective start reported from status servlet 1570 do { 1571 isReady = isStarted(); 1572 if (!isReady) { 1573 if (!quiet) { 1574 System.out.print("."); 1575 } 1576 Thread.sleep(1000); 1577 } 1578 deltaTime = (new Date().getTime() - startTime) / 1000; 1579 } while (!isReady && deltaTime < startMaxWait && isRunning()); 1580 if (isReady) { 1581 startSummary.append(newLine + getStartupSummary()); 1582 long duration = (new Date().getTime() - startTime) / 1000; 1583 startSummary.append("Started in " 1584 + String.format("%dmin%02ds", new Long(duration / 60), new Long(duration % 60))); 1585 if (wasStartupFine()) { 1586 if (!quiet) { 1587 System.out.println(startSummary); 1588 } 1589 } else { 1590 System.err.println(startSummary); 1591 if (strict) { 1592 errorValue = EXIT_CODE_ERROR; 1593 log.error("Shutting down because of unstarted component in strict mode..."); 1594 stop(); 1595 return false; 1596 } 1597 } 1598 return true; 1599 } else if (deltaTime >= startMaxWait) { 1600 if (!quiet) { 1601 System.out.println(); 1602 } 1603 log.error("Starting process is taking too long - giving up."); 1604 } 1605 errorValue = EXIT_CODE_ERROR; 1606 return false; 1607 } 1608 1609 /** 1610 * Must be called after {@link #getStartupSummary()} 1611 * 1612 * @since 5.5 1613 * @return last detected status of running Nuxeo server 1614 */ 1615 public boolean wasStartupFine() { 1616 return statusServletClient.isStartupFine(); 1617 } 1618 1619 /** 1620 * @since 5.5 1621 * @return Nuxeo startup summary 1622 */ 1623 public String getStartupSummary() { 1624 try { 1625 return statusServletClient.getStartupSummary(); 1626 } catch (SocketTimeoutException e) { 1627 log.warn("Failed to contact Nuxeo for getting startup summary", e); 1628 return ""; 1629 } 1630 } 1631 1632 /** 1633 * Starts the server in background. 1634 * 1635 * @return true if server successfully started 1636 * @throws PackageException 1637 */ 1638 public boolean doStart(boolean logProcessOutput) throws PackageException { 1639 errorValue = EXIT_CODE_OK; 1640 boolean serverStarted = false; 1641 try { 1642 if (reloadConfiguration) { 1643 configurationGenerator = new ConfigurationGenerator(quiet, debug); 1644 configurationGenerator.init(); 1645 } else { 1646 // Ensure reload on next start 1647 reloadConfiguration = true; 1648 } 1649 configure(); 1650 configurationGenerator.verifyInstallation(); 1651 1652 if (configurationGenerator.isWizardRequired()) { 1653 if (!configurationGenerator.isForceGeneration()) { 1654 log.error("Cannot start setup wizard with " + ConfigurationGenerator.PARAM_FORCE_GENERATION 1655 + "=false. Either set it to true or once, either set " 1656 + ConfigurationGenerator.PARAM_WIZARD_DONE + "=true to skip the wizard."); 1657 errorValue = EXIT_CODE_NOT_CONFIGURED; 1658 return false; 1659 } 1660 String paramsStr = ""; 1661 for (String param : params) { 1662 paramsStr += " " + param; 1663 } 1664 System.setProperty(ConfigurationGenerator.PARAM_WIZARD_RESTART_PARAMS, paramsStr); 1665 configurationGenerator.prepareWizardStart(); 1666 } else { 1667 configurationGenerator.cleanupPostWizard(); 1668 } 1669 1670 log.debug("Check if install in progress..."); 1671 if (configurationGenerator.isInstallInProgress()) { 1672 if (!getConnectBroker().executePending(configurationGenerator.getInstallFile(), true, true, 1673 ignoreMissing)) { 1674 errorValue = EXIT_CODE_ERROR; 1675 log.error(String.format( 1676 "Start interrupted due to failure on pending actions. You can resume with a new start;" 1677 + " or you can restore the file '%s', optionally using the '--%s' option.", 1678 configurationGenerator.getInstallFile().getName(), OPTION_IGNORE_MISSING)); 1679 return false; 1680 } 1681 1682 // configuration will be reloaded, keep wizard value 1683 System.setProperty( 1684 ConfigurationGenerator.PARAM_WIZARD_DONE, 1685 configurationGenerator.getUserConfig().getProperty(ConfigurationGenerator.PARAM_WIZARD_DONE, 1686 "true")); 1687 return doStart(logProcessOutput); 1688 } 1689 1690 start(logProcessOutput); 1691 serverStarted = isRunning(); 1692 if (pid != null) { 1693 File pidFile = new File(configurationGenerator.getPidDir(), "nuxeo.pid"); 1694 FileWriter writer = new FileWriter(pidFile); 1695 writer.write(pid); 1696 writer.close(); 1697 } 1698 } catch (ConfigurationException e) { 1699 errorValue = EXIT_CODE_NOT_CONFIGURED; 1700 log.error("Could not run configuration: " + e.getMessage()); 1701 log.debug(e, e); 1702 } catch (IOException e) { 1703 errorValue = EXIT_CODE_ERROR; 1704 log.error("Could not start process: " + e.getMessage()); 1705 log.debug(e, e); 1706 } catch (InterruptedException e) { 1707 errorValue = EXIT_CODE_ERROR; 1708 log.error("Could not start process: " + e.getMessage()); 1709 log.debug(e, e); 1710 } catch (IllegalStateException e) { 1711 if (strict) { 1712 // assume program is not configured because of http port binding 1713 // conflict 1714 errorValue = EXIT_CODE_NOT_CONFIGURED; 1715 } 1716 log.error(e.getMessage()); 1717 } 1718 return serverStarted; 1719 } 1720 1721 /** 1722 * @since 5.6 1723 */ 1724 protected void printXMLOutput() { 1725 try { 1726 JAXBContext jaxbContext = JAXBContext.newInstance(CommandSetInfo.class, CommandInfo.class, 1727 PackageInfo.class, MessageInfo.class); 1728 printXMLOutput(jaxbContext, cset); 1729 } catch (JAXBException e) { 1730 log.error("Output serialization failed: " + e.getMessage(), e); 1731 errorValue = EXIT_CODE_NOT_RUNNING; 1732 } 1733 } 1734 1735 /** 1736 * @since 5.6 1737 */ 1738 protected void printXMLOutput(JAXBContext jaxbContext, Object objectToOutput) { 1739 try { 1740 Writer xml = new StringWriter(); 1741 Marshaller marshaller = jaxbContext.createMarshaller(); 1742 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 1743 marshaller.marshal(objectToOutput, xml); 1744 if (!jsonOutput) { 1745 System.out.println(xml.toString()); 1746 } else { 1747 try { 1748 System.out.println(XML.toJSONObject(xml.toString()).toString(2)); 1749 } catch (JSONException e) { 1750 log.error(String.format("XML to JSON conversion failed: %s\nOutput was:\n%s", e.getMessage(), 1751 xml.toString())); 1752 } 1753 } 1754 } catch (JAXBException e) { 1755 log.error("Output serialization failed: " + e.getMessage(), e); 1756 errorValue = EXIT_CODE_NOT_RUNNING; 1757 } 1758 } 1759 1760 /** 1761 * Stop stream gobblers contained in the given ArrayList 1762 * 1763 * @since 5.5 1764 * @see #logProcessStreams(Process, boolean) 1765 */ 1766 public void waitForProcessStreams(ArrayList<ThreadedStreamGobbler> sgArray) { 1767 for (ThreadedStreamGobbler streamGobbler : sgArray) { 1768 try { 1769 streamGobbler.join(STREAM_MAX_WAIT); 1770 } catch (InterruptedException e) { 1771 streamGobbler.interrupt(); 1772 } 1773 } 1774 } 1775 1776 /** 1777 * @since 5.5 1778 * @param classpath 1779 * @param baseDir 1780 * @return classpath with all jar files in baseDir 1781 * @throws IOException 1782 */ 1783 protected String getClassPath(String classpath, File baseDir) throws IOException { 1784 File[] files = getFilename(baseDir, ".*"); 1785 for (File file : files) { 1786 classpath += System.getProperty("path.separator") + file.getPath(); 1787 } 1788 return classpath; 1789 } 1790 1791 /** 1792 * @since 5.5 1793 * @param baseDir 1794 * @param filePattern 1795 * @return filename matching filePattern in baseDir 1796 */ 1797 protected File[] getFilename(File baseDir, final String filePattern) { 1798 File[] files = baseDir.listFiles(new FilenameFilter() { 1799 @Override 1800 public boolean accept(File basedir, String filename) { 1801 return filename.matches(filePattern + "(-[0-9].*)?\\.jar"); 1802 } 1803 }); 1804 return files; 1805 } 1806 1807 protected class ShutdownThread extends Thread { 1808 1809 private NuxeoLauncher launcher; 1810 1811 public ShutdownThread(NuxeoLauncher launcher) { 1812 super(); 1813 this.launcher = launcher; 1814 } 1815 1816 @Override 1817 public void run() { 1818 log.debug("Shutting down..."); 1819 if (launcher.isRunning()) { 1820 launcher.stop(); 1821 } 1822 log.debug("Shutdown complete."); 1823 } 1824 } 1825 1826 protected void addShutdownHook() { 1827 log.debug("Add shutdown hook"); 1828 shutdownHook = new ShutdownThread(this); 1829 Runtime.getRuntime().addShutdownHook(shutdownHook); 1830 } 1831 1832 /** 1833 * Stops the server. Will try to call specific class for a clean stop, retry, waiting between each try, then kill 1834 * the process if still running. 1835 */ 1836 public void stop(boolean logProcessOutput) { 1837 long startTime = new Date().getTime(); 1838 long deltaTime; 1839 try { 1840 if (!isRunning()) { 1841 log.warn("Server is not running."); 1842 return; 1843 } 1844 if (!quiet) { 1845 System.out.print("\nStopping server..."); 1846 } 1847 int nbTry = 0; 1848 boolean retry = false; 1849 int stopMaxWait = Integer.parseInt(configurationGenerator.getUserConfig().getProperty(STOP_MAX_WAIT_PARAM, 1850 STOP_MAX_WAIT_DEFAULT)); 1851 do { 1852 List<String> stopCommand = new ArrayList<>(); 1853 stopCommand.add(getJavaExecutable().getPath()); 1854 stopCommand.add("-cp"); 1855 stopCommand.add(getShutdownClassPath()); 1856 stopCommand.addAll(getNuxeoProperties()); 1857 stopCommand.addAll(getServerProperties()); 1858 setServerStopCommand(stopCommand); 1859 for (String param : params) { 1860 stopCommand.add(param); 1861 } 1862 ProcessBuilder pb = new ProcessBuilder(getOSCommand(stopCommand)); 1863 pb.directory(configurationGenerator.getNuxeoHome()); 1864 // pb = pb.redirectErrorStream(true); 1865 log.debug("Server command: " + pb.command()); 1866 try { 1867 Process stopProcess = pb.start(); 1868 ArrayList<ThreadedStreamGobbler> sgArray = logProcessStreams(stopProcess, logProcessOutput); 1869 stopProcess.waitFor(); 1870 waitForProcessStreams(sgArray); 1871 boolean wait = true; 1872 while (wait) { 1873 try { 1874 if (stopProcess.exitValue() == 0) { 1875 // Successful call for server stop 1876 retry = false; 1877 } else { 1878 // Failed to call for server stop 1879 retry = ++nbTry < STOP_NB_TRY; 1880 if (!quiet) { 1881 System.out.print("."); 1882 } 1883 Thread.sleep(STOP_SECONDS_BEFORE_NEXT_TRY * 1000); 1884 } 1885 wait = false; 1886 } catch (IllegalThreadStateException e) { 1887 // Stop call is still running 1888 wait = true; 1889 if (!quiet) { 1890 System.out.print("."); 1891 } 1892 Thread.sleep(1000); 1893 } 1894 } 1895 // Exit if there's no way to check for server stop 1896 if (!processManager.canFindPid()) { 1897 log.warn("Can't check server status on your OS."); 1898 return; 1899 } 1900 // Wait a few seconds for effective stop 1901 deltaTime = 0; 1902 do { 1903 if (!quiet) { 1904 System.out.print("."); 1905 } 1906 Thread.sleep(1000); 1907 deltaTime = (new Date().getTime() - startTime) / 1000; 1908 } while (!retry && getPid() != null && deltaTime < stopMaxWait); 1909 } catch (InterruptedException e) { 1910 log.error(e); 1911 } 1912 } while (retry); 1913 if (getPid() == null) { 1914 log.warn("Server stopped."); 1915 } else { 1916 log.info("No answer from server, try to kill process " + pid + "..."); 1917 processManager.kill(nuxeoProcess, pid); 1918 if (getPid() == null) { 1919 log.warn("Server forcibly stopped."); 1920 } 1921 } 1922 } catch (IOException e) { 1923 log.error("Could not manage process!", e); 1924 } 1925 } 1926 1927 protected abstract void setServerStopCommand(List<String> command); 1928 1929 private String getPid() throws IOException { 1930 pid = processManager.findPid(processRegex); 1931 log.debug("regexp: " + processRegex + " pid:" + pid); 1932 return pid; 1933 } 1934 1935 /** 1936 * Configure the server after checking installation 1937 * 1938 * @throws ConfigurationException If an installation error is detected or if configuration fails 1939 */ 1940 public void configure() throws ConfigurationException { 1941 try { 1942 checkNoRunningServer(); 1943 configurationGenerator.checkJavaVersion(); 1944 configurationGenerator.run(); 1945 overrideJavaTmpDir = Boolean.parseBoolean(configurationGenerator.getUserConfig().getProperty( 1946 OVERRIDE_JAVA_TMPDIR_PARAM, "true")); 1947 } catch (ConfigurationException e) { 1948 errorValue = EXIT_CODE_NOT_CONFIGURED; 1949 throw e; 1950 } 1951 } 1952 1953 /** 1954 * @return Default max wait depending on server (ie JBoss takes much more time than Tomcat) 1955 */ 1956 private String getDefaultMaxWait() { 1957 return START_MAX_WAIT_DEFAULT; 1958 } 1959 1960 /** 1961 * Return process status (running or not) as String, depending on OS capability to manage processes. Set status 1962 * value following "http://refspecs.freestandards.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html" 1963 * 1964 * @see #getStatus() 1965 */ 1966 public String status() { 1967 try { 1968 if (processManager instanceof PureJavaProcessManager) { 1969 status = STATUS_CODE_UNKNOWN; 1970 return "Can't check server status on your OS."; 1971 } 1972 if (getPid() == null) { 1973 status = STATUS_CODE_OFF; 1974 return "Server is not running."; 1975 } else { 1976 status = STATUS_CODE_ON; 1977 return "Server is running with process ID " + getPid() + "."; 1978 } 1979 } catch (IOException e) { 1980 status = STATUS_CODE_UNKNOWN; 1981 return "Could not check existing process (" + e.getMessage() + ")."; 1982 } 1983 } 1984 1985 /** 1986 * Last status value set by {@link #status()}. 1987 */ 1988 public int getStatus() { 1989 return status; 1990 } 1991 1992 /** 1993 * Last error value set by any method. Exit code values are following the Linux Standard Base Core Specification 1994 * 4.1. 1995 */ 1996 public int getErrorValue() { 1997 return errorValue; 1998 } 1999 2000 /** 2001 * @throws ParseException 2002 * @return a NuxeoLauncher instance specific to current server ( Tomcat or Jetty). 2003 * @throws ConfigurationException If server cannot be identified 2004 * @since 5.5 2005 */ 2006 public static NuxeoLauncher createLauncher(String[] args) throws ConfigurationException, ParseException { 2007 CommandLine cmdLine = parseOptions(args); 2008 ConfigurationGenerator cg = new ConfigurationGenerator(quiet, debug); 2009 if (cmdLine.hasOption(OPTION_HIDE_DEPRECATION)) { 2010 cg.hideDeprecationWarnings(true); 2011 } 2012 NuxeoLauncher launcher; 2013 if (cg.isJetty) { 2014 launcher = new NuxeoJettyLauncher(cg); 2015 } else if (cg.isTomcat) { 2016 launcher = new NuxeoTomcatLauncher(cg); 2017 } else { 2018 throw new ConfigurationException("Unknown server!"); 2019 } 2020 launcher.setArgs(cmdLine); 2021 return launcher; 2022 } 2023 2024 /** 2025 * Sets from program arguments the launcher command and additional parameters. 2026 * 2027 * @param cmdLine Program arguments; may be used by launcher implementation. Must not be null or empty. 2028 * @throws ConfigurationException 2029 */ 2030 private void setArgs(CommandLine cmdLine) throws ConfigurationException { 2031 this.cmdLine = cmdLine; 2032 extractCommandAndParams(cmdLine.getArgs()); 2033 // Use GUI? 2034 if (cmdLine.hasOption(OPTION_GUI)) { 2035 useGui = Boolean.valueOf(ConnectBroker.parseAnswer(cmdLine.getOptionValue(OPTION_GUI))); 2036 log.debug("GUI: " + cmdLine.getOptionValue(OPTION_GUI) + " -> " + new Boolean(useGui).toString()); 2037 } else if (OPTION_GUI.equalsIgnoreCase(command)) { 2038 useGui = true; 2039 // Shift params and extract command if there is one 2040 extractCommandAndParams(params); 2041 } else { 2042 if (PlatformUtils.isWindows()) { 2043 useGui = true; 2044 log.debug("GUI: option not set - platform is Windows -> start GUI"); 2045 } else { 2046 useGui = false; 2047 log.debug("GUI: option not set - platform is not Windows -> do not start GUI"); 2048 } 2049 } 2050 // Output format 2051 if (cmdLine.hasOption(OPTION_XML)) { 2052 setXMLOutput(); 2053 } 2054 if (cmdLine.hasOption(OPTION_JSON)) { 2055 setJSONOutput(); 2056 } 2057 if (cmdLine.hasOption(OPTION_CLID)) { 2058 try { 2059 getConnectBroker().setCLID(cmdLine.getOptionValue(OPTION_CLID)); 2060 } catch (NoCLID | IOException | PackageException e) { 2061 throw new ConfigurationException(e); 2062 } 2063 } 2064 if (cmdLine.hasOption(OPTION_IGNORE_MISSING)) { 2065 ignoreMissing = true; 2066 } 2067 } 2068 2069 private void extractCommandAndParams(String[] args) { 2070 if (args.length > 0) { 2071 command = args[0]; 2072 log.debug("Launcher command: " + command); 2073 // Command parameters 2074 if (args.length > 1) { 2075 params = Arrays.copyOfRange(args, 1, args.length); 2076 if (log.isDebugEnabled()) { 2077 log.debug("Command parameters: " + ArrayUtils.toString(params)); 2078 } 2079 } else { 2080 params = new String[0]; 2081 } 2082 } else { 2083 command = null; 2084 } 2085 } 2086 2087 /** 2088 * Set launcher in quiet mode 2089 * 2090 * @since 5.5 2091 */ 2092 protected static void setQuiet() { 2093 quiet = true; 2094 Log4JHelper.setQuiet(Log4JHelper.CONSOLE_APPENDER_NAME); 2095 } 2096 2097 /** 2098 * @param categories Root categories to switch DEBUG on. 2099 * @since 7.4 2100 */ 2101 protected static void setDebug(String[] categories, String defaultCategory) { 2102 debug = true; 2103 if (categories == null) { 2104 categories = new String[] { defaultCategory }; 2105 } 2106 Log4JHelper.setDebug(categories, true, true, new String[] { Log4JHelper.CONSOLE_APPENDER_NAME, "FILE" }); 2107 } 2108 2109 /** 2110 * @param categories Root categories to switch DEBUG on. 2111 * @since 5.6 2112 */ 2113 protected static void setDebug(String categories) { 2114 setDebug(categories, true); 2115 } 2116 2117 /** 2118 * @param categories Root categories to switch DEBUG on or off 2119 * @param activateDebug Set DEBUG on or off. 2120 * @since 5.6 2121 */ 2122 protected static void setDebug(String categories, boolean activateDebug) { 2123 debug = activateDebug; 2124 Log4JHelper.setDebug(categories, activateDebug, true, 2125 new String[] { Log4JHelper.CONSOLE_APPENDER_NAME, "FILE" }); 2126 } 2127 2128 /** 2129 * @param activateDebug if true, will activate the DEBUG logs 2130 * @since 5.5 2131 */ 2132 protected static void setDebug(boolean activateDebug) { 2133 setDebug("org.nuxeo", activateDebug); 2134 } 2135 2136 /** 2137 * @param isStrict if {@code true}, set the launcher strict option 2138 * @since 7.4 2139 * @see #OPTION_STRICT_DESC 2140 */ 2141 protected static void setStrict(boolean isStrict) { 2142 strict = isStrict; 2143 } 2144 2145 protected void setXMLOutput() { 2146 xmlOutput = true; 2147 } 2148 2149 protected void setJSONOutput() { 2150 jsonOutput = true; 2151 setXMLOutput(); 2152 } 2153 2154 public static void printShortHelp() { 2155 System.out.println(); 2156 HelpFormatter help = new HelpFormatter(); 2157 help.setSyntaxPrefix("USAGE\n"); 2158 help.setOptionComparator(null); 2159 help.setWidth(1000); 2160 help.printHelp(OPTION_HELP_USAGE, "OPTIONS", launcherOptions, null); 2161 System.out.println(OPTION_HELP_DESC_COMMANDS); 2162 } 2163 2164 public static void printLongHelp() { 2165 System.out.println(); 2166 HelpFormatter help = new HelpFormatter(); 2167 help.setSyntaxPrefix("USAGE\n"); 2168 help.setOptionComparator(null); 2169 help.setWidth(1000); 2170 help.printHelp(OPTION_HELP_USAGE, OPTION_HELP_HEADER, launcherOptions, null); 2171 System.out.println(OPTION_HELP_DESC_ENV); 2172 System.out.println(OPTION_HELP_DESC_COMMANDS); 2173 System.out.println(OPTION_HELP_FOOTER); 2174 } 2175 2176 /** 2177 * Work best with current nuxeoProcess. If nuxeoProcess is null or has exited, then will try to get process ID (so, 2178 * result in that case depends on OS capabilities). 2179 * 2180 * @return true if current process is running or if a running PID is found 2181 */ 2182 public boolean isRunning() { 2183 if (nuxeoProcess != null) { 2184 try { 2185 nuxeoProcess.exitValue(); 2186 // Previous process has exited 2187 nuxeoProcess = null; 2188 } catch (IllegalThreadStateException exception) { 2189 return true; 2190 } 2191 } 2192 try { 2193 return (getPid() != null); 2194 } catch (IOException e) { 2195 log.error(e); 2196 return false; 2197 } 2198 } 2199 2200 /** 2201 * @since 5.5 2202 * @return true if Nuxeo finished starting 2203 */ 2204 public boolean isStarted() { 2205 boolean isStarted; 2206 if (configurationGenerator.isWizardRequired()) { 2207 isStarted = isRunning(); 2208 } else { 2209 try { 2210 isStarted = isRunning() && statusServletClient.isStarted(); 2211 } catch (SocketTimeoutException e) { 2212 isStarted = false; 2213 } 2214 } 2215 return isStarted; 2216 } 2217 2218 /** 2219 * @return Server log file 2220 */ 2221 public File getLogFile() { 2222 return new File(configurationGenerator.getLogDir(), "server.log"); 2223 } 2224 2225 /** 2226 * @return Server URL 2227 */ 2228 public String getURL() { 2229 return configurationGenerator.getUserConfig().getProperty(ConfigurationGenerator.PARAM_NUXEO_URL); 2230 } 2231 2232 protected ConnectBroker getConnectBroker() throws IOException, PackageException { 2233 if (connectBroker == null) { 2234 connectBroker = new ConnectBroker(configurationGenerator.getEnv()); 2235 if (cmdLine.hasOption(OPTION_ACCEPT)) { 2236 connectBroker.setAccept(cmdLine.getOptionValue(OPTION_ACCEPT)); 2237 } 2238 if (cmdLine.hasOption(OPTION_RELAX)) { 2239 connectBroker.setRelax(cmdLine.getOptionValue(OPTION_RELAX)); 2240 } 2241 if (cmdLine.hasOption(OPTION_SNAPSHOT) || isSNAPSHOTDistribution()) { 2242 connectBroker.setAllowSNAPSHOT(true); 2243 } 2244 cset = connectBroker.getCommandSet(); 2245 } 2246 return connectBroker; 2247 } 2248 2249 /** 2250 * @since 5.9.1 2251 */ 2252 private boolean isSNAPSHOTDistribution() { 2253 return new Version(getDistributionInfo().version).isSnapshot(); 2254 } 2255 2256 /** 2257 * List all local packages. 2258 * 2259 * @throws IOException 2260 * @throws PackageException 2261 */ 2262 protected void pkgList() throws IOException, PackageException { 2263 getConnectBroker().listPending(configurationGenerator.getInstallFile()); 2264 getConnectBroker().pkgList(); 2265 } 2266 2267 /** 2268 * List all packages including remote ones. 2269 * 2270 * @since 5.6 2271 * @throws IOException 2272 * @throws PackageException 2273 */ 2274 protected void pkgListAll() throws IOException, PackageException { 2275 getConnectBroker().listPending(configurationGenerator.getInstallFile()); 2276 getConnectBroker().pkgListAll(); 2277 } 2278 2279 protected boolean pkgAdd(String[] pkgNames) throws IOException, PackageException { 2280 boolean cmdOK = getConnectBroker().pkgAdd(Arrays.asList(pkgNames), ignoreMissing); 2281 if (!cmdOK) { 2282 errorValue = EXIT_CODE_ERROR; 2283 } 2284 return cmdOK; 2285 } 2286 2287 protected boolean pkgInstall(String[] pkgIDs) throws IOException, PackageException { 2288 boolean cmdOK = true; 2289 if (configurationGenerator.isInstallInProgress()) { 2290 cmdOK = getConnectBroker().executePending(configurationGenerator.getInstallFile(), true, 2291 !cmdLine.hasOption(OPTION_NODEPS), ignoreMissing); 2292 } 2293 cmdOK = cmdOK && getConnectBroker().pkgInstall(Arrays.asList(pkgIDs), ignoreMissing); 2294 if (!cmdOK) { 2295 errorValue = EXIT_CODE_ERROR; 2296 } 2297 return cmdOK; 2298 } 2299 2300 protected boolean pkgUninstall(String[] pkgIDs) throws IOException, PackageException { 2301 boolean cmdOK = getConnectBroker().pkgUninstall(Arrays.asList(pkgIDs)); 2302 if (!cmdOK) { 2303 errorValue = EXIT_CODE_ERROR; 2304 } 2305 return cmdOK; 2306 } 2307 2308 protected boolean pkgRemove(String[] pkgIDs) throws IOException, PackageException { 2309 boolean cmdOK = getConnectBroker().pkgRemove(Arrays.asList(pkgIDs)); 2310 if (!cmdOK) { 2311 errorValue = EXIT_CODE_ERROR; 2312 } 2313 return cmdOK; 2314 } 2315 2316 protected boolean pkgReset() throws IOException, PackageException { 2317 boolean cmdOK = getConnectBroker().pkgReset(); 2318 if (!cmdOK) { 2319 errorValue = EXIT_CODE_ERROR; 2320 } 2321 return cmdOK; 2322 } 2323 2324 /** 2325 * @since 5.6 2326 */ 2327 protected void printInstanceXMLOutput(InstanceInfo instance) { 2328 try { 2329 JAXBContext jaxbContext = JAXBContext.newInstance(InstanceInfo.class, DistributionInfo.class, 2330 PackageInfo.class, ConfigurationInfo.class, KeyValueInfo.class); 2331 printXMLOutput(jaxbContext, instance); 2332 } catch (JAXBException e) { 2333 log.error("Output serialization failed: " + e.getMessage()); 2334 log.debug(e, e); 2335 errorValue = EXIT_CODE_NOT_RUNNING; 2336 } 2337 } 2338 2339 /** 2340 * @throws PackageException 2341 * @throws IOException 2342 * @throws ConfigurationException 2343 * @since 5.6 2344 */ 2345 protected InstanceInfo showConfig() throws IOException, PackageException, ConfigurationException { 2346 InstanceInfo nxInstance = new InstanceInfo(); 2347 log.info("***** Nuxeo instance configuration *****"); 2348 nxInstance.NUXEO_CONF = configurationGenerator.getNuxeoConf().getPath(); 2349 log.info("NUXEO_CONF: " + nxInstance.NUXEO_CONF); 2350 nxInstance.NUXEO_HOME = configurationGenerator.getNuxeoHome().getPath(); 2351 log.info("NUXEO_HOME: " + nxInstance.NUXEO_HOME); 2352 // CLID 2353 try { 2354 nxInstance.clid = getConnectBroker().getCLID(); 2355 log.info("Instance CLID: " + nxInstance.clid); 2356 } catch (NoCLID e) { 2357 // leave nxInstance.clid unset 2358 } catch (IOException | PackageException e) { 2359 // something went wrong in the NuxeoConnectClient initialization 2360 errorValue = EXIT_CODE_UNAUTHORIZED; 2361 throw new ConfigurationException("Could not initialize NuxeoConnectClient", e); 2362 } 2363 // distribution.properties 2364 DistributionInfo nxDistrib = getDistributionInfo(); 2365 nxInstance.distribution = nxDistrib; 2366 log.info("** Distribution"); 2367 log.info("- name: " + nxDistrib.name); 2368 log.info("- server: " + nxDistrib.server); 2369 log.info("- version: " + nxDistrib.version); 2370 log.info("- date: " + nxDistrib.date); 2371 log.info("- packaging: " + nxDistrib.packaging); 2372 // packages 2373 List<LocalPackage> pkgs = getConnectBroker().getPkgList(); 2374 log.info("** Packages:"); 2375 List<String> pkgTemplates = new ArrayList<>(); 2376 for (LocalPackage pkg : pkgs) { 2377 nxInstance.packages.add(new PackageInfo(pkg)); 2378 log.info(String.format("- %s (version: %s - id: %s - state: %s)", pkg.getName(), pkg.getVersion(), 2379 pkg.getId(), pkg.getPackageState().getLabel())); 2380 // store template(s) added by this package 2381 try { 2382 File installFile = pkg.getInstallFile(); 2383 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 2384 DocumentBuilder db = dbf.newDocumentBuilder(); 2385 Document dom = db.parse(installFile); 2386 NodeList nodes = dom.getDocumentElement().getElementsByTagName("config"); 2387 for (int i = 0; i < nodes.getLength(); i++) { 2388 Element node = (Element) nodes.item(i); 2389 if (node.hasAttribute("addtemplate")) { 2390 pkgTemplates.add(node.getAttribute("addtemplate")); 2391 } 2392 } 2393 } catch (Exception e) { 2394 log.warn("Could not parse install file for " + pkg.getName(), e); 2395 } 2396 } 2397 // nuxeo.conf 2398 ConfigurationInfo nxConfig = new ConfigurationInfo(); 2399 nxConfig.dbtemplate = configurationGenerator.extractDatabaseTemplateName(); 2400 log.info("** Templates:"); 2401 log.info("Database template: " + nxConfig.dbtemplate); 2402 String userTemplates = configurationGenerator.getUserTemplates(); 2403 StringTokenizer st = new StringTokenizer(userTemplates, ","); 2404 while (st.hasMoreTokens()) { 2405 String template = st.nextToken(); 2406 if (template.equals(nxConfig.dbtemplate)) { 2407 continue; 2408 } 2409 if (pkgTemplates.contains(template)) { 2410 nxConfig.pkgtemplates.add(template); 2411 log.info("Package template: " + template); 2412 } else { 2413 File testBase = new File(configurationGenerator.getNuxeoHome(), ConfigurationGenerator.TEMPLATES 2414 + File.separator + template); 2415 if (testBase.exists()) { 2416 nxConfig.basetemplates.add(template); 2417 log.info("Base template: " + template); 2418 } else { 2419 nxConfig.usertemplates.add(template); 2420 log.info("User template: " + template); 2421 } 2422 } 2423 } 2424 log.info("** Settings from nuxeo.conf:"); 2425 CryptoProperties userConfig = configurationGenerator.getUserConfig(); 2426 for (Object item : new TreeSet<>(userConfig.keySet())) { 2427 String key = (String) item; 2428 String value = userConfig.getRawProperty(key); 2429 if (key.equals("JAVA_OPTS")) { 2430 value = getJavaOptsProperty(); 2431 } 2432 KeyValueInfo kv = new KeyValueInfo(key, value); 2433 nxConfig.keyvals.add(kv); 2434 if (!key.contains("password") && !key.equals(Environment.SERVER_STATUS_KEY) && !Crypto.isEncrypted(value)) { 2435 log.info(key + "=" + value); 2436 } else { 2437 log.info(key + "=********"); 2438 } 2439 } 2440 nxInstance.config = nxConfig; 2441 log.info("****************************************"); 2442 if (xmlOutput) { 2443 printInstanceXMLOutput(nxInstance); 2444 } 2445 return nxInstance; 2446 } 2447 2448 /** 2449 * @since 5.9.1 2450 */ 2451 protected DistributionInfo getDistributionInfo() { 2452 File distFile = new File(configurationGenerator.getConfigDir(), "distribution.properties"); 2453 if (!distFile.exists()) { 2454 // fallback in the file in templates 2455 distFile = new File(configurationGenerator.getNuxeoHome(), "templates"); 2456 distFile = new File(distFile, "common"); 2457 distFile = new File(distFile, "config"); 2458 distFile = new File(distFile, "distribution.properties"); 2459 } 2460 DistributionInfo nxDistrib; 2461 try { 2462 nxDistrib = new DistributionInfo(distFile); 2463 } catch (IOException e) { 2464 nxDistrib = new DistributionInfo(); 2465 } 2466 return nxDistrib; 2467 } 2468 2469 /** 2470 * @since 5.6 2471 * @param pkgsToAdd 2472 * @param pkgsToInstall 2473 * @param pkgsToUninstall 2474 * @param pkgsToRemove 2475 * @return true if request execution was fine 2476 * @throws IOException 2477 * @throws PackageException 2478 */ 2479 protected boolean pkgRequest(List<String> pkgsToAdd, List<String> pkgsToInstall, List<String> pkgsToUninstall, 2480 List<String> pkgsToRemove) throws IOException, PackageException { 2481 boolean cmdOK = true; 2482 if (configurationGenerator.isInstallInProgress()) { 2483 cmdOK = getConnectBroker().executePending(configurationGenerator.getInstallFile(), true, true, 2484 ignoreMissing); 2485 } 2486 cmdOK = cmdOK 2487 && getConnectBroker().pkgRequest(pkgsToAdd, pkgsToInstall, pkgsToUninstall, pkgsToRemove, true, 2488 ignoreMissing); 2489 if (!cmdOK) { 2490 errorValue = EXIT_CODE_ERROR; 2491 } 2492 return cmdOK; 2493 } 2494 2495 /** 2496 * Update the cached list of remote packages 2497 * 2498 * @since 5.6 2499 * @return true 2500 * @throws IOException 2501 * @throws PackageException 2502 */ 2503 protected boolean pkgRefreshCache() throws IOException, PackageException { 2504 getConnectBroker().refreshCache(); 2505 return true; 2506 } 2507 2508 /** 2509 * Add packages from the distribution to the local cache 2510 * 2511 * @throws PackageException 2512 * @throws IOException 2513 * @since 5.6 2514 */ 2515 protected boolean pkgInit() throws IOException, PackageException { 2516 return getConnectBroker().addDistributionPackages(); 2517 } 2518 2519 /** 2520 * Uninstall and remove all packages from the local cache 2521 * 2522 * @return {@code true} if command succeed 2523 * @throws PackageException 2524 * @throws IOException 2525 * @since 5.6 2526 */ 2527 protected boolean pkgPurge() throws PackageException, IOException { 2528 return getConnectBroker().pkgPurge(); 2529 } 2530 2531 /** 2532 * Install the hotfixes available for the instance 2533 * 2534 * @return {@code true} if command succeed 2535 * @throws PackageException 2536 * @throws IOException 2537 * @since 5.6 2538 */ 2539 protected boolean pkgHotfix() throws IOException, PackageException { 2540 return getConnectBroker().pkgHotfix(); 2541 } 2542 2543 /** 2544 * Upgrade the marketplace packages (addons) available for the instance 2545 * 2546 * @return {@code true} if command succeed 2547 * @throws PackageException 2548 * @throws IOException 2549 * @since 5.6 2550 */ 2551 protected boolean pkgUpgrade() throws IOException, PackageException { 2552 return getConnectBroker().pkgUpgrade(); 2553 } 2554 2555 /** 2556 * Combined install/uninstall request 2557 * 2558 * @param request Space separated list of package names or IDs prefixed with + (install) or - (uninstall) 2559 * @throws IOException 2560 * @throws PackageException 2561 * @since 5.6 2562 */ 2563 protected boolean pkgCompoundRequest(List<String> request) throws IOException, PackageException { 2564 List<String> add = new ArrayList<>(); 2565 List<String> install = new ArrayList<>(); 2566 List<String> uninstall = new ArrayList<>(); 2567 for (String param : request) { 2568 for (String subparam : param.split("[ ,]")) { 2569 if (subparam.charAt(0) == '-') { 2570 uninstall.add(subparam.substring(1)); 2571 } else if (subparam.charAt(0) == '+') { 2572 install.add(subparam.substring(1)); 2573 } else { 2574 add.add(subparam); 2575 } 2576 } 2577 } 2578 return pkgRequest(add, install, uninstall, null); 2579 } 2580 2581 protected boolean pkgSetRequest(List<String> request, boolean nodeps) throws IOException, PackageException { 2582 boolean cmdOK; 2583 if (nodeps) { 2584 cmdOK = getConnectBroker().pkgSet(request, ignoreMissing); 2585 } else { 2586 cmdOK = getConnectBroker().pkgRequest(null, request, null, null, false, ignoreMissing); 2587 } 2588 if (!cmdOK) { 2589 errorValue = EXIT_CODE_ERROR; 2590 } 2591 return cmdOK; 2592 } 2593 2594 /** 2595 * dpkg-like command which returns package location, version, dependencies, conflicts, ... 2596 * 2597 * @param packages List of packages identified by their ID, name or local filename. 2598 * @return false if unable to show package information. 2599 * @throws PackageException 2600 * @throws IOException 2601 * @since 5.7 2602 */ 2603 protected boolean pkgShow(String[] packages) throws IOException, PackageException { 2604 boolean cmdOK = getConnectBroker().pkgShow(Arrays.asList(packages)); 2605 if (!cmdOK) { 2606 errorValue = EXIT_CODE_ERROR; 2607 } 2608 return cmdOK; 2609 } 2610 2611}