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 /** 585 * @since 5.5 586 * @return true if quiet mode is active 587 */ 588 public boolean isQuiet() { 589 return quiet; 590 } 591 592 private static Map<String, NuxeoLauncherGUI> guis; 593 594 /** 595 * @since 5.5 596 */ 597 public NuxeoLauncherGUI getGUI() { 598 if (guis == null) { 599 return null; 600 } 601 return guis.get(configurationGenerator.getNuxeoConf().toString()); 602 } 603 604 /** 605 * @since 5.5 606 */ 607 public void setGUI(NuxeoLauncherGUI gui) { 608 if (guis == null) { 609 guis = new HashMap<>(); 610 } 611 guis.put(configurationGenerator.getNuxeoConf().toString(), gui); 612 } 613 614 public NuxeoLauncher(ConfigurationGenerator configurationGenerator) { 615 this.configurationGenerator = configurationGenerator; 616 init(); 617 } 618 619 /** 620 * @since 5.6 621 */ 622 public void init() { 623 if (!configurationGenerator.init(true)) { 624 throw new IllegalStateException("Initialization failed"); 625 } 626 statusServletClient = new StatusServletClient(configurationGenerator); 627 statusServletClient.setKey(configurationGenerator.getUserConfig().getProperty(Environment.SERVER_STATUS_KEY)); 628 processManager = getOSProcessManager(); 629 processRegex = "^(?!/bin/sh).*" + Pattern.quote(configurationGenerator.getNuxeoConf().getPath()) + ".*" 630 + Pattern.quote(getServerPrint()) + ".*$"; 631 632 // Set OS-specific decorations 633 if (PlatformUtils.isMac()) { 634 System.setProperty("com.apple.mrj.application.apple.menu.about.name", "NuxeoCtl"); 635 } 636 } 637 638 private ProcessManager getOSProcessManager() { 639 if (PlatformUtils.isLinux() || SystemUtils.IS_OS_AIX) { 640 UnixProcessManager unixProcessManager = new UnixProcessManager(); 641 return unixProcessManager; 642 } else if (PlatformUtils.isMac()) { 643 return new MacProcessManager(); 644 } else if (SystemUtils.IS_OS_SUN_OS) { 645 return new SolarisProcessManager(); 646 } else if (PlatformUtils.isWindows()) { 647 WindowsProcessManager windowsProcessManager = new WindowsProcessManager(); 648 return windowsProcessManager.isUsable() ? windowsProcessManager : new PureJavaProcessManager(); 649 } else { 650 return new PureJavaProcessManager(); 651 } 652 } 653 654 public static class SolarisProcessManager extends UnixProcessManager { 655 656 protected static final String SOLARIS_11 = "5.11"; 657 658 protected static final String SOLARIS_10 = "5.10"; 659 660 protected static final String[] SOLARIS_11_PS = { "/usr/bin/ps", "auxww" }; 661 662 protected static final String[] SOLARIS_10_PS = { "/usr/ucb/ps", "auxww" }; 663 664 protected static final Pattern PS_OUTPUT_LINE = Pattern.compile("^" + "[^\\s]+\\s+" // USER 665 + "([0-9]+)\\s+" // PID 666 + "[0-9.\\s]+" // %CPU %MEM SZ RSS (may be collapsed) 667 + "[^\\s]+\\s+" // TT (no starting digit) 668 + "[^\\s]+\\s+" // S 669 + "[^\\s]+\\s+" // START 670 + "[^\\s]+\\s+" // TIME 671 + "(.*)$" // COMMAND 672 ); 673 674 protected String solarisVersion; 675 676 protected String getSolarisVersion() { 677 if (solarisVersion == null) { 678 List<String> lines; 679 try { 680 lines = execute(new String[] { "/usr/bin/uname", "-r" }); 681 } catch (IOException e) { 682 log.debug(e.getMessage(), e); 683 lines = Collections.emptyList(); 684 } 685 if (lines.isEmpty()) { 686 solarisVersion = "?"; 687 } else { 688 solarisVersion = lines.get(0).trim(); 689 } 690 } 691 return solarisVersion; 692 } 693 694 @Override 695 protected String[] psCommand() { 696 if (SOLARIS_11.equals(getSolarisVersion())) { 697 return SOLARIS_11_PS; 698 } 699 return null; 700 } 701 702 protected Matcher getLineMatcher(String line) { 703 return PS_OUTPUT_LINE.matcher(line); 704 } 705 706 @Override 707 public String findPid(String regex) throws IOException { 708 if (SOLARIS_11.equals(getSolarisVersion())) { 709 Pattern commandPattern = Pattern.compile(regex); 710 for (String line : execute(psCommand())) { 711 Matcher lineMatcher = getLineMatcher(line); 712 if (lineMatcher.matches()) { 713 String pid = lineMatcher.group(1); 714 String command = lineMatcher.group(2); 715 Matcher commandMatcher = commandPattern.matcher(command); 716 if (commandMatcher.find()) { 717 return pid; 718 } 719 } 720 } 721 } else { 722 log.debug("Unsupported Solaris version: " + solarisVersion); 723 } 724 return null; 725 } 726 727 protected List<String> execute(String... command) throws IOException { 728 Process process = new ProcessBuilder(command).start(); 729 List<String> lines = IOUtils.readLines(process.getInputStream()); 730 return lines; 731 } 732 } 733 734 /** 735 * Do not directly call this method without a call to {@link #checkNoRunningServer()} 736 * 737 * @see #doStart() 738 * @throws IOException In case of issue with process. 739 * @throws InterruptedException If any thread has interrupted the current thread. 740 */ 741 protected void start(boolean logProcessOutput) throws IOException, InterruptedException { 742 List<String> startCommand = new ArrayList<>(); 743 startCommand.add(getJavaExecutable().getPath()); 744 startCommand.addAll(Arrays.asList(getJavaOptsProperty().split(" "))); 745 startCommand.add("-cp"); 746 startCommand.add(getClassPath()); 747 startCommand.addAll(getNuxeoProperties()); 748 startCommand.addAll(getServerProperties()); 749 setServerStartCommand(startCommand); 750 for (String param : params) { 751 startCommand.add(param); 752 } 753 ProcessBuilder pb = new ProcessBuilder(getOSCommand(startCommand)); 754 pb.directory(configurationGenerator.getNuxeoHome()); 755 // pb = pb.redirectErrorStream(true); 756 log.debug("Server command: " + pb.command()); 757 nuxeoProcess = pb.start(); 758 Thread.sleep(1000); 759 boolean processExited = false; 760 // Check if process exited early 761 try { 762 int exitValue = nuxeoProcess.exitValue(); 763 if (exitValue != 0) { 764 log.error(String.format("Server start failed (%d).", exitValue)); 765 } 766 processExited = true; 767 } catch (IllegalThreadStateException e) { 768 // Normal case 769 } 770 logProcessStreams(nuxeoProcess, processExited || logProcessOutput); 771 if (!processExited) { 772 if (getPid() != null) { 773 log.warn("Server started with process ID " + pid + "."); 774 } else { 775 log.warn("Sent server start command but could not get process ID."); 776 } 777 } 778 } 779 780 /** 781 * Gets the Java options with 'nuxeo.*' properties substituted. It enables usage of property like ${nuxeo.log.dir} 782 * inside JAVA_OPTS. 783 * 784 * @return the java options string. 785 */ 786 protected String getJavaOptsProperty() { 787 String ret = System.getProperty(JAVA_OPTS_PROPERTY, JAVA_OPTS_DEFAULT); 788 ret = StrSubstitutor.replace(ret, configurationGenerator.getUserConfig()); 789 return ret; 790 } 791 792 /** 793 * Check if some server is already running (from another thread) and throw a Runtime exception if it finds one. That 794 * method will work where {@link #isRunning()} won't. 795 * 796 * @throws IllegalThreadStateException Thrown if a server is already running. 797 */ 798 public void checkNoRunningServer() throws IllegalStateException { 799 try { 800 String existingPid = getPid(); 801 if (existingPid != null) { 802 errorValue = EXIT_CODE_OK; 803 throw new IllegalStateException("A server is running with process ID " + existingPid); 804 } 805 } catch (IOException e) { 806 log.warn("Could not check existing process: " + e.getMessage()); 807 } 808 } 809 810 /** 811 * @return (since 5.5) Array list with created stream gobbler threads. 812 */ 813 public ArrayList<ThreadedStreamGobbler> logProcessStreams(Process process, boolean logProcessOutput) { 814 ArrayList<ThreadedStreamGobbler> sgArray = new ArrayList<>(); 815 ThreadedStreamGobbler inputSG, errorSG; 816 if (logProcessOutput) { 817 inputSG = new ThreadedStreamGobbler(process.getInputStream(), System.out); 818 errorSG = new ThreadedStreamGobbler(process.getErrorStream(), System.err); 819 } else { 820 inputSG = new ThreadedStreamGobbler(process.getInputStream(), SimpleLog.LOG_LEVEL_OFF); 821 errorSG = new ThreadedStreamGobbler(process.getErrorStream(), SimpleLog.LOG_LEVEL_OFF); 822 } 823 inputSG.start(); 824 errorSG.start(); 825 sgArray.add(inputSG); 826 sgArray.add(errorSG); 827 return sgArray; 828 } 829 830 protected abstract String getServerPrint(); 831 832 /** 833 * Will wrap, if necessary, the command within a Shell command 834 * 835 * @param roughCommand Java command which will be run 836 * @return wrapped command depending on the OS 837 */ 838 private List<String> getOSCommand(List<String> roughCommand) { 839 String linearizedCommand = new String(); 840 ArrayList<String> osCommand = new ArrayList<>(); 841 if (PlatformUtils.isLinux() || PlatformUtils.isMac()) { 842 for (Iterator<String> iterator = roughCommand.iterator(); iterator.hasNext();) { 843 String commandToken = iterator.next(); 844 if (commandToken.contains(" ")) { 845 commandToken = commandToken.replaceAll(" ", "\\\\ "); 846 } 847 linearizedCommand += " " + commandToken; 848 } 849 osCommand.add("/bin/sh"); 850 osCommand.add("-c"); 851 osCommand.add(linearizedCommand); 852 // osCommand.add("&"); 853 return osCommand; 854 // return roughCommand; 855 } else if (PlatformUtils.isWindows()) { 856 // for (Iterator<String> iterator = roughCommand.iterator(); 857 // iterator.hasNext();) { 858 // String commandToken = iterator.next(); 859 // if (commandToken.endsWith("java")) { 860 // commandToken = "^\"" + commandToken + "^\""; 861 // } else if (commandToken.contains(" ")) { 862 // commandToken = commandToken.replaceAll(" ", "^ "); 863 // } 864 // linearizedCommand += " " + commandToken; 865 // } 866 // osCommand.add("cmd"); 867 // osCommand.add("/C"); 868 // osCommand.add(linearizedCommand); 869 // return osCommand; 870 return roughCommand; 871 } else { 872 return roughCommand; 873 } 874 } 875 876 protected abstract Collection<? extends String> getServerProperties(); 877 878 protected abstract void setServerStartCommand(List<String> command); 879 880 private File getJavaExecutable() { 881 File javaExec = new File(System.getProperty("java.home"), "bin" + File.separator + "java"); 882 return javaExec; 883 } 884 885 protected abstract String getClassPath(); 886 887 /** 888 * @since 5.6 889 */ 890 protected abstract String getShutdownClassPath(); 891 892 protected Collection<? extends String> getNuxeoProperties() { 893 ArrayList<String> nuxeoProperties = new ArrayList<>(); 894 nuxeoProperties.add(String.format("-D%s=%s", Environment.NUXEO_HOME, configurationGenerator.getNuxeoHome() 895 .getPath())); 896 nuxeoProperties.add(String.format("-D%s=%s", ConfigurationGenerator.NUXEO_CONF, 897 configurationGenerator.getNuxeoConf().getPath())); 898 nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_LOG_DIR)); 899 nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_DATA_DIR)); 900 nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_TMP_DIR)); 901 if (!DEFAULT_NUXEO_CONTEXT_PATH.equals(configurationGenerator.getUserConfig().getProperty( 902 Environment.NUXEO_CONTEXT_PATH))) { 903 nuxeoProperties.add(getNuxeoProperty(Environment.NUXEO_CONTEXT_PATH)); 904 } 905 if (overrideJavaTmpDir) { 906 nuxeoProperties.add("-Djava.io.tmpdir=" 907 + configurationGenerator.getUserConfig().getProperty(Environment.NUXEO_TMP_DIR)); 908 } 909 return nuxeoProperties; 910 } 911 912 private String getNuxeoProperty(String property) { 913 return "-D" + property + "=" + configurationGenerator.getUserConfig().getProperty(property); 914 } 915 916 protected String addToClassPath(String cp, String filename) { 917 File classPathEntry = new File(configurationGenerator.getNuxeoHome(), filename); 918 if (!classPathEntry.exists()) { 919 classPathEntry = new File(filename); 920 } 921 if (!classPathEntry.exists()) { 922 throw new RuntimeException("Tried to add inexistent classpath entry: " + filename); 923 } 924 cp += System.getProperty("path.separator") + classPathEntry.getPath(); 925 return cp; 926 } 927 928 /** 929 * @since 5.6 930 */ 931 protected static void initParserOptions() { 932 if (launcherOptions == null) { 933 launcherOptions = new Options(); 934 // help option 935 launcherOptions.addOption(Option.builder("h").longOpt(OPTION_HELP).desc(OPTION_HELP_DESC).build()); 936 // Quiet option 937 launcherOptions.addOption(Option.builder("q").longOpt(OPTION_QUIET).desc(OPTION_QUIET_DESC).build()); 938 { // Debug options (mutually exclusive) 939 OptionGroup debugOptions = new OptionGroup(); 940 // Debug option 941 debugOptions.addOption(Option.builder("d") 942 .longOpt(OPTION_DEBUG) 943 .desc(OPTION_DEBUG_DESC) 944 .hasArgs() 945 .argName(OPTION_DEBUG_CATEGORY_ARG_NAME) 946 .optionalArg(true) 947 .valueSeparator(',') 948 .build()); 949 // Debug category option 950 debugOptions.addOption(Option.builder(OPTION_DEBUG_CATEGORY) 951 .desc(OPTION_DEBUG_CATEGORY_DESC) 952 .hasArgs() 953 .argName(OPTION_DEBUG_CATEGORY_ARG_NAME) 954 .optionalArg(true) 955 .valueSeparator(',') 956 .build()); 957 launcherOptions.addOptionGroup(debugOptions); 958 } 959 // For help output purpose only: that option is managed and swallowed by the nuxeoctl Shell script 960 launcherOptions.addOption(Option.builder() 961 .longOpt("--debug-launcher") 962 .desc("Linux-only. Activate Java debugging mode on the Launcher.") 963 .build()); 964 // Instance CLID option 965 launcherOptions.addOption(Option.builder().longOpt(OPTION_CLID).desc(OPTION_CLID_DESC).hasArg().build()); 966 { // Output options (mutually exclusive) 967 OptionGroup outputOptions = new OptionGroup(); 968 // XML option 969 outputOptions.addOption(Option.builder().longOpt(OPTION_XML).desc(OPTION_XML_DESC).build()); 970 // JSON option 971 outputOptions.addOption(Option.builder().longOpt(OPTION_JSON).desc(OPTION_JSON_DESC).build()); 972 launcherOptions.addOptionGroup(outputOptions); 973 } 974 // GUI option 975 launcherOptions.addOption(Option.builder() 976 .longOpt(OPTION_GUI) 977 .desc(OPTION_GUI_DESC) 978 .hasArg() 979 .argName("true|false|yes|no") 980 .build()); 981 // Package management option 982 launcherOptions.addOption(Option.builder().longOpt(OPTION_NODEPS).desc(OPTION_NODEPS_DESC).build()); 983 // Relax on target platform option 984 launcherOptions.addOption(Option.builder() 985 .longOpt(OPTION_RELAX) 986 .desc(OPTION_RELAX_DESC) 987 .hasArg() 988 .argName("true|false|yes|no|ask") 989 .build()); 990 // Accept option 991 launcherOptions.addOption(Option.builder() 992 .longOpt(OPTION_ACCEPT) 993 .desc(OPTION_ACCEPT_DESC) 994 .hasArg() 995 .argName("true|false|yes|no|ask") 996 .build()); 997 // Allow SNAPSHOT option 998 launcherOptions.addOption(Option.builder("s").longOpt(OPTION_SNAPSHOT).desc(OPTION_SNAPSHOT_DESC).build()); 999 // Force option 1000 launcherOptions.addOption(Option.builder("f").longOpt(OPTION_FORCE).desc(OPTION_FORCE_DESC).build()); 1001 // Strict option 1002 launcherOptions.addOption(Option.builder().longOpt(OPTION_STRICT).desc(OPTION_STRICT_DESC).build()); 1003 1004 // Ignore missing option 1005 launcherOptions.addOption(Option.builder("im") 1006 .longOpt(OPTION_IGNORE_MISSING) 1007 .desc(OPTION_IGNORE_MISSING_DESC) 1008 .build()); 1009 // Hide deprecation warnings option 1010 launcherOptions.addOption(Option.builder("hdw") 1011 .longOpt(OPTION_HIDE_DEPRECATION) 1012 .desc(OPTION_HIDE_DEPRECATION_DESC) 1013 .build()); 1014 // Encrypt option 1015 launcherOptions.addOption(Option.builder() 1016 .longOpt(OPTION_ENCRYPT) 1017 .desc(OPTION_ENCRYPT_DESC) 1018 .hasArg() 1019 .argName(OPTION_ENCRYPT_ARG_NAME) 1020 .optionalArg(true) 1021 .build()); 1022 { // Config options (mutually exclusive) 1023 OptionGroup configOptions = new OptionGroup(); 1024 // Set option 1025 configOptions.addOption(Option.builder().longOpt(OPTION_SET).desc(OPTION_SET_DESC).build()); 1026 configOptions.addOption(Option.builder().longOpt(OPTION_GET).desc(OPTION_GET_DESC).build()); 1027 configOptions.addOption(Option.builder() 1028 .longOpt(OPTION_GET_REGEXP) 1029 .desc(OPTION_GET_REGEXP_DESC) 1030 .build()); 1031 launcherOptions.addOptionGroup(configOptions); 1032 } 1033 } 1034 } 1035 1036 /** 1037 * @since 5.6 1038 */ 1039 protected static CommandLine parseOptions(String[] args) throws ParseException { 1040 initParserOptions(); 1041 CommandLineParser parser = new DefaultParser(); 1042 CommandLine cmdLine = null; 1043 cmdLine = parser.parse(launcherOptions, args); 1044 if (cmdLine.hasOption(OPTION_HELP)) { 1045 cmdLine.getArgList().add(OPTION_HELP); 1046 setQuiet(); 1047 } else if (cmdLine.getArgList().isEmpty()) { 1048 throw new ParseException("Missing command."); 1049 } 1050 // Common options to the Launcher and the ConfigurationGenerator 1051 if (cmdLine.hasOption(OPTION_QUIET) || cmdLine.hasOption(OPTION_XML) || cmdLine.hasOption(OPTION_JSON)) { 1052 setQuiet(); 1053 } 1054 if (cmdLine.hasOption(OPTION_DEBUG)) { 1055 setDebug(cmdLine.getOptionValues(OPTION_DEBUG), "org.nuxeo.launcher"); 1056 } 1057 if (cmdLine.hasOption(OPTION_DEBUG_CATEGORY)) { 1058 setDebug(cmdLine.getOptionValues(OPTION_DEBUG_CATEGORY), "org.nuxeo.launcher"); 1059 } 1060 if (cmdLine.hasOption(OPTION_FORCE) || cmdLine.hasOption(OPTION_STRICT)) { 1061 setStrict(true); 1062 } 1063 return cmdLine; 1064 } 1065 1066 public static void main(String[] args) { 1067 NuxeoLauncher launcher = null; 1068 try { 1069 launcher = createLauncher(args); 1070 if (launcher.commandRequiresNoGUI()) { 1071 launcher.useGui = false; 1072 } 1073 if (launcher.useGui && launcher.getGUI() == null) { 1074 launcher.setGUI(new NuxeoLauncherGUI(launcher)); 1075 } 1076 launch(launcher); 1077 } catch (ParseException e) { 1078 log.error("Invalid command line. " + e.getMessage()); 1079 log.debug(e, e); 1080 printShortHelp(); 1081 System.exit(launcher == null || launcher.errorValue == EXIT_CODE_OK ? EXIT_CODE_INVALID 1082 : launcher.errorValue); 1083 } catch (IOException | PackageException | ConfigurationException | GeneralSecurityException e) { 1084 log.error(e.getMessage()); 1085 log.debug(e, e); 1086 System.exit(launcher == null || launcher.errorValue == EXIT_CODE_OK ? EXIT_CODE_INVALID 1087 : launcher.errorValue); 1088 } catch (Exception e) { 1089 log.error("Cannot execute command. " + e.getMessage()); 1090 log.debug(e, e); 1091 System.exit(1); 1092 } 1093 } 1094 1095 /** 1096 * @since 5.5 1097 * @param launcher 1098 * @throws PackageException 1099 * @throws IOException 1100 * @throws ConfigurationException 1101 * @throws ParseException 1102 * @throws GeneralSecurityException 1103 */ 1104 public static void launch(final NuxeoLauncher launcher) throws IOException, PackageException, 1105 ConfigurationException, ParseException, GeneralSecurityException { 1106 boolean commandSucceeded = true; 1107 if (launcher.commandIs(null)) { 1108 return; 1109 } 1110 if (launcher.commandRequiresNoRunningServer()) { 1111 launcher.checkNoRunningServer(); 1112 } 1113 if (launcher.commandIs(OPTION_HELP)) { 1114 printLongHelp(); 1115 } else if (launcher.commandIs("status")) { 1116 String statusMsg = launcher.status(); 1117 launcher.errorValue = launcher.getStatus(); 1118 if (!quiet) { 1119 log.warn(statusMsg); 1120 if (launcher.isStarted()) { 1121 log.info("Go to " + launcher.getURL()); 1122 log.info(launcher.getStartupSummary()); 1123 } 1124 } 1125 } else if (launcher.commandIs("startbg")) { 1126 commandSucceeded = launcher.doStart(); 1127 } else if (launcher.commandIs("start")) { 1128 if (launcher.useGui) { 1129 launcher.getGUI().start(); 1130 } else { 1131 commandSucceeded = launcher.doStartAndWait(); 1132 } 1133 } else if (launcher.commandIs("console")) { 1134 launcher.executor.execute(new Runnable() { 1135 @Override 1136 public void run() { 1137 launcher.addShutdownHook(); 1138 try { 1139 if (!launcher.doStart(true)) { 1140 launcher.removeShutdownHook(); 1141 System.exit(1); 1142 } else if (!quiet) { 1143 log.info("Go to " + launcher.getURL()); 1144 } 1145 } catch (PackageException e) { 1146 log.error("Could not initialize the packaging subsystem", e); 1147 launcher.removeShutdownHook(); 1148 System.exit(EXIT_CODE_ERROR); 1149 } 1150 } 1151 }); 1152 } else if (launcher.commandIs("stop")) { 1153 if (launcher.useGui) { 1154 launcher.getGUI().stop(); 1155 } else { 1156 launcher.stop(); 1157 } 1158 } else if (launcher.commandIs("restartbg")) { 1159 launcher.stop(); 1160 commandSucceeded = launcher.doStart(); 1161 } else if (launcher.commandIs("restart")) { 1162 launcher.stop(); 1163 commandSucceeded = launcher.doStartAndWait(); 1164 } else if (launcher.commandIs("wizard")) { 1165 commandSucceeded = launcher.startWizard(); 1166 } else if (launcher.commandIs("configure")) { 1167 launcher.configure(); 1168 } else if (launcher.commandIs("pack")) { 1169 launcher.pack(); 1170 } else if (launcher.commandIs("mp-list")) { 1171 launcher.pkgList(); 1172 } else if (launcher.commandIs("mp-listall")) { 1173 launcher.pkgListAll(); 1174 } else if (launcher.commandIs("mp-init")) { 1175 commandSucceeded = launcher.pkgInit(); 1176 } else if (launcher.commandIs("mp-purge")) { 1177 commandSucceeded = launcher.pkgPurge(); 1178 } else if (launcher.commandIs("mp-add")) { 1179 if (launcher.cmdLine.hasOption(OPTION_NODEPS)) { 1180 commandSucceeded = launcher.pkgAdd(launcher.params); 1181 } else { 1182 commandSucceeded = launcher.pkgRequest(Arrays.asList(launcher.params), null, null, null); 1183 } 1184 } else if (launcher.commandIs("mp-install")) { 1185 if (launcher.cmdLine.hasOption(OPTION_NODEPS)) { 1186 commandSucceeded = launcher.pkgInstall(launcher.params); 1187 } else { 1188 commandSucceeded = launcher.pkgRequest(null, Arrays.asList(launcher.params), null, null); 1189 } 1190 } else if (launcher.commandIs("mp-uninstall")) { 1191 if (launcher.cmdLine.hasOption(OPTION_NODEPS)) { 1192 commandSucceeded = launcher.pkgUninstall(launcher.params); 1193 } else { 1194 commandSucceeded = launcher.pkgRequest(null, null, Arrays.asList(launcher.params), null); 1195 } 1196 } else if (launcher.commandIs("mp-remove")) { 1197 if (launcher.cmdLine.hasOption(OPTION_NODEPS)) { 1198 commandSucceeded = launcher.pkgRemove(launcher.params); 1199 } else { 1200 commandSucceeded = launcher.pkgRequest(null, null, null, Arrays.asList(launcher.params)); 1201 } 1202 } else if (launcher.commandIs("mp-request")) { 1203 if (launcher.cmdLine.hasOption(OPTION_NODEPS)) { 1204 throw new ParseException("The command mp-request is not available with the --nodeps option"); 1205 } else { 1206 commandSucceeded = launcher.pkgCompoundRequest(Arrays.asList(launcher.params)); 1207 } 1208 } else if (launcher.commandIs("mp-set")) { 1209 commandSucceeded = launcher.pkgSetRequest(Arrays.asList(launcher.params), 1210 launcher.cmdLine.hasOption(OPTION_NODEPS)); 1211 } else if (launcher.commandIs("mp-hotfix")) { 1212 commandSucceeded = launcher.pkgHotfix(); 1213 } else if (launcher.commandIs("mp-upgrade")) { 1214 commandSucceeded = launcher.pkgUpgrade(); 1215 } else if (launcher.commandIs("mp-reset")) { 1216 commandSucceeded = launcher.pkgReset(); 1217 } else if (launcher.commandIs("mp-update")) { 1218 commandSucceeded = launcher.pkgRefreshCache(); 1219 } else if (launcher.commandIs("showconf")) { 1220 launcher.showConfig(); 1221 } else if (launcher.commandIs("mp-show")) { 1222 commandSucceeded = launcher.pkgShow(launcher.params); 1223 } else if (launcher.commandIs("encrypt")) { 1224 launcher.encrypt(); 1225 } else if (launcher.commandIs("decrypt")) { 1226 launcher.decrypt(); 1227 } else if (launcher.commandIs("config")) { 1228 launcher.config(); 1229 } else { 1230 log.error("Unknown command " + launcher.command); 1231 printLongHelp(); 1232 launcher.errorValue = EXIT_CODE_INVALID; 1233 } 1234 if (launcher.xmlOutput && launcher.command.startsWith("mp-")) { 1235 launcher.printXMLOutput(); 1236 } 1237 commandSucceeded = commandSucceeded && launcher.errorValue == EXIT_CODE_OK; 1238 if (!commandSucceeded && !quiet || debug) { 1239 launcher.cset.log(commandSucceeded && debug); 1240 } 1241 if (!commandSucceeded) { 1242 System.exit(launcher.errorValue); 1243 } 1244 } 1245 1246 /** 1247 * @throws ConfigurationException 1248 * @throws GeneralSecurityException 1249 * @since 7.4 1250 */ 1251 protected void encrypt() throws ConfigurationException, GeneralSecurityException { 1252 Crypto crypto = configurationGenerator.getCrypto(); 1253 String algorithm = cmdLine.getOptionValue(OPTION_ENCRYPT, null); 1254 if (params.length == 0) { 1255 Console console = System.console(); 1256 if (console == null) { 1257 errorValue = EXIT_CODE_INVALID; 1258 return; 1259 } 1260 params = new String[] { console.readLine("Please enter the value to encrypt: ") }; 1261 } 1262 for (String strToEncrypt : params) { 1263 String encryptedString = crypto.encrypt(algorithm, strToEncrypt.getBytes()); 1264 System.out.println(encryptedString); 1265 } 1266 } 1267 1268 /** 1269 * @throws ConfigurationException 1270 * @since 7.4 1271 */ 1272 protected void decrypt() throws ConfigurationException { 1273 Crypto crypto = configurationGenerator.getCrypto(); 1274 Console console = System.console(); 1275 if (console == null) { 1276 errorValue = EXIT_CODE_ERROR; 1277 return; 1278 } 1279 if (!crypto.verifyKey(console.readPassword("Please enter the secret key: "))) { 1280 errorValue = EXIT_CODE_INVALID; 1281 return; 1282 } 1283 for (String strToDecrypt : params) { 1284 System.out.println(Crypto.getChars(crypto.decrypt(strToDecrypt))); 1285 } 1286 } 1287 1288 /** 1289 * @throws ConfigurationException 1290 * @throws IOException 1291 * @throws GeneralSecurityException 1292 * @since 7.4 1293 */ 1294 protected void config() throws ConfigurationException, IOException, GeneralSecurityException { 1295 if (cmdLine.hasOption(OPTION_SET) || !cmdLine.hasOption(OPTION_GET) && !cmdLine.hasOption(OPTION_GET_REGEXP) 1296 && params.length == 2) { 1297 setConfigProperties(); 1298 } else { // OPTION_GET || OPTION_GET_REGEXP || !OPTION_SET && params.length != 2 1299 getConfigProperties(); 1300 } 1301 } 1302 1303 /** 1304 * @since 7.4 1305 */ 1306 protected void getConfigProperties() { 1307 boolean isRegexp = cmdLine.hasOption(OPTION_GET_REGEXP); 1308 CryptoProperties userConfig = configurationGenerator.getUserConfig(); 1309 List<String> keys; 1310 if (isRegexp) { 1311 keys = new ArrayList<>(); 1312 for (Object key : userConfig.keySet()) { 1313 for (String param : params) { 1314 Pattern pattern = Pattern.compile(param, Pattern.CASE_INSENSITIVE); 1315 if (pattern.matcher((String) key).find()) { 1316 keys.add((String) key); 1317 } 1318 } 1319 } 1320 if (keys.isEmpty()) { 1321 errorValue = EXIT_CODE_NOT_CONFIGURED; 1322 } 1323 } else { 1324 keys = Arrays.asList(params); 1325 } 1326 1327 Crypto crypto = userConfig.getCrypto(); 1328 boolean keyChecked = false; // Secret key is asked only once 1329 boolean raw = true; 1330 StringBuilder sb = new StringBuilder(); 1331 final String newLine = System.getProperty("line.separator"); 1332 for (String key : keys) { 1333 String value = userConfig.getProperty(key, raw); 1334 if (value == null) { 1335 errorValue = EXIT_CODE_NOT_CONFIGURED; 1336 sb.append(OUTPUT_UNSET_VALUE + newLine); 1337 } else { 1338 if (raw && !keyChecked && Crypto.isEncrypted(value)) { 1339 keyChecked = true; 1340 Console console = System.console(); 1341 if (console != null && crypto.verifyKey(console.readPassword("Please enter the secret key: "))) { 1342 raw = false; 1343 value = new String(crypto.decrypt(value)); 1344 } else { 1345 errorValue = EXIT_CODE_ERROR; 1346 } 1347 } 1348 if (isRegexp) { 1349 sb.append(key + "="); 1350 } 1351 sb.append(value + newLine); 1352 } 1353 } 1354 System.out.println(sb.toString()); 1355 } 1356 1357 /** 1358 * @throws IOException 1359 * @throws GeneralSecurityException 1360 * @since 7.4 1361 */ 1362 protected void setConfigProperties() throws ConfigurationException, IOException, GeneralSecurityException { 1363 Crypto crypto = configurationGenerator.getCrypto(); 1364 boolean doEncrypt = cmdLine.hasOption(OPTION_ENCRYPT); 1365 String algorithm = cmdLine.getOptionValue(OPTION_ENCRYPT, null); 1366 Map<String, String> changedParameters = new HashMap<>(); 1367 for (Iterator<String> iterator = Arrays.asList(params).iterator(); iterator.hasNext();) { 1368 String key = iterator.next(); 1369 String value; 1370 if (iterator.hasNext()) { 1371 value = iterator.next(); 1372 if (doEncrypt) { 1373 value = crypto.encrypt(algorithm, value.getBytes()); 1374 } else if (Environment.CRYPT_KEY.equals(key) || Environment.CRYPT_KEYSTORE_PASS.equals(key)) { 1375 value = Base64.encodeBase64String(value.getBytes()); 1376 } 1377 } else { 1378 Console console = System.console(); 1379 if (console == null) { 1380 errorValue = EXIT_CODE_INVALID; 1381 return; 1382 } 1383 if (doEncrypt) { 1384 value = crypto.encrypt(algorithm, 1385 Crypto.getBytes(console.readPassword("Please enter the value for %s: ", key))); 1386 } else if (Environment.CRYPT_KEY.equals(key) || Environment.CRYPT_KEYSTORE_PASS.equals(key)) { 1387 value = Base64.encodeBase64String(Crypto.getBytes(console.readPassword( 1388 "Please enter the value for %s: ", key))); 1389 } else { 1390 value = console.readLine("Please enter the value for %s: ", key); 1391 } 1392 } 1393 changedParameters.put(key, value); 1394 } 1395 String template = cmdLine.getOptionValue(OPTION_SET); 1396 Map<String, String> oldValues; 1397 if (template == null) { 1398 oldValues = configurationGenerator.setProperties(changedParameters); 1399 } else { 1400 oldValues = configurationGenerator.setProperties(template, changedParameters); 1401 } 1402 log.debug("Old values: " + oldValues); 1403 } 1404 1405 /** 1406 * Since 5.5 1407 */ 1408 protected boolean pack() { 1409 try { 1410 configurationGenerator.setProperty(PARAM_UPDATECENTER_DISABLED, "true"); 1411 List<String> startCommand = new ArrayList<>(); 1412 startCommand.add(getJavaExecutable().getPath()); 1413 startCommand.addAll(Arrays.asList(getJavaOptsProperty().split(" "))); 1414 startCommand.add("-cp"); 1415 String classpath = getClassPath(); 1416 classpath = addToClassPath(classpath, "bin" + File.separator + "nuxeo-launcher.jar"); 1417 classpath = getClassPath(classpath, configurationGenerator.getServerConfigurator().getServerLibDir()); 1418 classpath = getClassPath(classpath, configurationGenerator.getServerConfigurator().getNuxeoLibDir()); 1419 classpath = getClassPath(classpath, new File(configurationGenerator.getRuntimeHome(), "bundles")); 1420 startCommand.add(classpath); 1421 startCommand.addAll(getNuxeoProperties()); 1422 if (configurationGenerator.isTomcat) { 1423 startCommand.add(PACK_TOMCAT_CLASS); 1424 } else { 1425 errorValue = EXIT_CODE_ERROR; 1426 return false; 1427 } 1428 startCommand.add(configurationGenerator.getRuntimeHome().getPath()); 1429 for (String param : params) { 1430 startCommand.add(param); 1431 } 1432 ProcessBuilder pb = new ProcessBuilder(getOSCommand(startCommand)); 1433 pb.directory(configurationGenerator.getNuxeoHome()); 1434 log.debug("Pack command: " + pb.command()); 1435 Process process = pb.start(); 1436 ArrayList<ThreadedStreamGobbler> sgArray = logProcessStreams(process, true); 1437 Thread.sleep(100); 1438 process.waitFor(); 1439 waitForProcessStreams(sgArray); 1440 } catch (IOException | InterruptedException e) { 1441 errorValue = EXIT_CODE_ERROR; 1442 log.error("Could not start process", e); 1443 } catch (ConfigurationException e) { 1444 errorValue = EXIT_CODE_ERROR; 1445 log.error(e); 1446 } 1447 return errorValue == EXIT_CODE_OK; 1448 } 1449 1450 protected boolean startWizard() throws PackageException { 1451 if (!configurationGenerator.getServerConfigurator().isWizardAvailable()) { 1452 log.error("Sorry, the wizard is not available within that server."); 1453 return false; 1454 } 1455 if (isRunning()) { 1456 log.error("Server already running. " + "Please stop it before calling \"wizard\" command " 1457 + "or use the Admin Center instead of the wizard."); 1458 return false; 1459 } 1460 if (reloadConfiguration) { 1461 configurationGenerator = new ConfigurationGenerator(quiet, debug); 1462 configurationGenerator.init(); 1463 reloadConfiguration = false; 1464 } 1465 configurationGenerator.getUserConfig().setProperty(ConfigurationGenerator.PARAM_WIZARD_DONE, "false"); 1466 return doStart(); 1467 } 1468 1469 /** 1470 * @throws PackageException 1471 * @see #doStartAndWait(boolean) 1472 */ 1473 public boolean doStartAndWait() throws PackageException { 1474 boolean started = doStartAndWait(false); 1475 if (started && !quiet) { 1476 log.info("Go to " + getURL()); 1477 } 1478 return started; 1479 } 1480 1481 /** 1482 * @see #stop(boolean) 1483 */ 1484 public void stop() { 1485 stop(false); 1486 } 1487 1488 /** 1489 * Call {@link #doStart(boolean)} with false as parameter. 1490 * 1491 * @see #doStart(boolean) 1492 * @return true if the server started successfully 1493 * @throws PackageException 1494 */ 1495 public boolean doStart() throws PackageException { 1496 boolean started = doStart(false); 1497 if (started && !quiet) { 1498 log.info("Go to " + getURL()); 1499 } 1500 return started; 1501 } 1502 1503 /** 1504 * Whereas {@link #doStart()} considers the server as started when the process is running, {@link #doStartAndWait()} 1505 * waits for effective start by watching the logs 1506 * 1507 * @param logProcessOutput Must process output stream must be logged or not. 1508 * @return true if the server started successfully 1509 * @throws PackageException 1510 */ 1511 public boolean doStartAndWait(boolean logProcessOutput) throws PackageException { 1512 boolean commandSucceeded = false; 1513 if (doStart(logProcessOutput)) { 1514 addShutdownHook(); 1515 try { 1516 if (configurationGenerator.isWizardRequired() || waitForEffectiveStart()) { 1517 commandSucceeded = true; 1518 } 1519 removeShutdownHook(); 1520 } catch (InterruptedException e) { 1521 // do nothing 1522 } 1523 } 1524 return commandSucceeded; 1525 } 1526 1527 protected void removeShutdownHook() { 1528 try { 1529 Runtime.getRuntime().removeShutdownHook(shutdownHook); 1530 log.debug("Removed shutdown hook"); 1531 } catch (IllegalStateException e) { 1532 // the virtual machine is already in the process of shutting down 1533 } 1534 } 1535 1536 /** 1537 * @return true if Nuxeo is ready 1538 * @throws InterruptedException 1539 */ 1540 protected boolean waitForEffectiveStart() throws InterruptedException { 1541 long startTime = new Date().getTime(); 1542 int startMaxWait = Integer.parseInt(configurationGenerator.getUserConfig().getProperty(START_MAX_WAIT_PARAM, 1543 getDefaultMaxWait())); 1544 log.debug("Will wait for effective start during " + startMaxWait + " seconds."); 1545 final StringBuilder startSummary = new StringBuilder(); 1546 final String newLine = System.getProperty("line.separator"); 1547 boolean isReady = false; 1548 long deltaTime = 0; 1549 // Wait for status servlet ready 1550 do { 1551 try { 1552 isReady = statusServletClient.init(); 1553 } catch (SocketTimeoutException e) { 1554 if (!quiet) { 1555 System.out.print("."); 1556 } 1557 } 1558 deltaTime = (new Date().getTime() - startTime) / 1000; 1559 } while (!isReady && deltaTime < startMaxWait && isRunning()); 1560 isReady = false; 1561 // Wait for effective start reported from status servlet 1562 do { 1563 isReady = isStarted(); 1564 if (!isReady) { 1565 if (!quiet) { 1566 System.out.print("."); 1567 } 1568 Thread.sleep(1000); 1569 } 1570 deltaTime = (new Date().getTime() - startTime) / 1000; 1571 } while (!isReady && deltaTime < startMaxWait && isRunning()); 1572 if (isReady) { 1573 startSummary.append(newLine + getStartupSummary()); 1574 long duration = (new Date().getTime() - startTime) / 1000; 1575 startSummary.append("Started in " 1576 + String.format("%dmin%02ds", new Long(duration / 60), new Long(duration % 60))); 1577 if (wasStartupFine()) { 1578 if (!quiet) { 1579 System.out.println(startSummary); 1580 } 1581 } else { 1582 System.err.println(startSummary); 1583 if (strict) { 1584 errorValue = EXIT_CODE_ERROR; 1585 log.error("Shutting down because of unstarted component in strict mode..."); 1586 stop(); 1587 return false; 1588 } 1589 } 1590 return true; 1591 } else if (deltaTime >= startMaxWait) { 1592 if (!quiet) { 1593 System.out.println(); 1594 } 1595 log.error("Starting process is taking too long - giving up."); 1596 } 1597 errorValue = EXIT_CODE_ERROR; 1598 return false; 1599 } 1600 1601 /** 1602 * Must be called after {@link #getStartupSummary()} 1603 * 1604 * @since 5.5 1605 * @return last detected status of running Nuxeo server 1606 */ 1607 public boolean wasStartupFine() { 1608 return statusServletClient.isStartupFine(); 1609 } 1610 1611 /** 1612 * @since 5.5 1613 * @return Nuxeo startup summary 1614 */ 1615 public String getStartupSummary() { 1616 try { 1617 return statusServletClient.getStartupSummary(); 1618 } catch (SocketTimeoutException e) { 1619 log.warn("Failed to contact Nuxeo for getting startup summary", e); 1620 return ""; 1621 } 1622 } 1623 1624 /** 1625 * Starts the server in background. 1626 * 1627 * @return true if server successfully started 1628 * @throws PackageException 1629 */ 1630 public boolean doStart(boolean logProcessOutput) throws PackageException { 1631 errorValue = EXIT_CODE_OK; 1632 boolean serverStarted = false; 1633 try { 1634 if (reloadConfiguration) { 1635 configurationGenerator = new ConfigurationGenerator(quiet, debug); 1636 configurationGenerator.init(); 1637 } else { 1638 // Ensure reload on next start 1639 reloadConfiguration = true; 1640 } 1641 configure(); 1642 configurationGenerator.verifyInstallation(); 1643 1644 if (configurationGenerator.isWizardRequired()) { 1645 if (!configurationGenerator.isForceGeneration()) { 1646 log.error("Cannot start setup wizard with " + ConfigurationGenerator.PARAM_FORCE_GENERATION 1647 + "=false. Either set it to true or once, either set " 1648 + ConfigurationGenerator.PARAM_WIZARD_DONE + "=true to skip the wizard."); 1649 errorValue = EXIT_CODE_NOT_CONFIGURED; 1650 return false; 1651 } 1652 String paramsStr = ""; 1653 for (String param : params) { 1654 paramsStr += " " + param; 1655 } 1656 System.setProperty(ConfigurationGenerator.PARAM_WIZARD_RESTART_PARAMS, paramsStr); 1657 configurationGenerator.prepareWizardStart(); 1658 } else { 1659 configurationGenerator.cleanupPostWizard(); 1660 } 1661 1662 log.debug("Check if install in progress..."); 1663 if (configurationGenerator.isInstallInProgress()) { 1664 getConnectBroker().executePending(configurationGenerator.getInstallFile(), true, true); 1665 // configuration will be reloaded, keep wizard value 1666 System.setProperty( 1667 ConfigurationGenerator.PARAM_WIZARD_DONE, 1668 configurationGenerator.getUserConfig().getProperty(ConfigurationGenerator.PARAM_WIZARD_DONE, 1669 "true")); 1670 return doStart(logProcessOutput); 1671 } 1672 1673 start(logProcessOutput); 1674 serverStarted = isRunning(); 1675 if (pid != null) { 1676 File pidFile = new File(configurationGenerator.getPidDir(), "nuxeo.pid"); 1677 FileWriter writer = new FileWriter(pidFile); 1678 writer.write(pid); 1679 writer.close(); 1680 } 1681 } catch (ConfigurationException e) { 1682 errorValue = EXIT_CODE_NOT_CONFIGURED; 1683 log.error("Could not run configuration: " + e.getMessage()); 1684 log.debug(e, e); 1685 } catch (IOException e) { 1686 errorValue = EXIT_CODE_ERROR; 1687 log.error("Could not start process: " + e.getMessage()); 1688 log.debug(e, e); 1689 } catch (InterruptedException e) { 1690 errorValue = EXIT_CODE_ERROR; 1691 log.error("Could not start process: " + e.getMessage()); 1692 log.debug(e, e); 1693 } catch (IllegalStateException e) { 1694 if (strict) { 1695 // assume program is not configured because of http port binding 1696 // conflict 1697 errorValue = EXIT_CODE_NOT_CONFIGURED; 1698 } 1699 log.error(e.getMessage()); 1700 } 1701 return serverStarted; 1702 } 1703 1704 /** 1705 * @since 5.6 1706 */ 1707 protected void printXMLOutput() { 1708 try { 1709 JAXBContext jaxbContext = JAXBContext.newInstance(CommandSetInfo.class, CommandInfo.class, 1710 PackageInfo.class, MessageInfo.class); 1711 printXMLOutput(jaxbContext, cset); 1712 } catch (JAXBException e) { 1713 log.error("Output serialization failed: " + e.getMessage(), e); 1714 errorValue = EXIT_CODE_NOT_RUNNING; 1715 } 1716 } 1717 1718 /** 1719 * @since 5.6 1720 */ 1721 protected void printXMLOutput(JAXBContext jaxbContext, Object objectToOutput) { 1722 try { 1723 Writer xml = new StringWriter(); 1724 Marshaller marshaller = jaxbContext.createMarshaller(); 1725 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 1726 marshaller.marshal(objectToOutput, xml); 1727 if (!jsonOutput) { 1728 System.out.println(xml.toString()); 1729 } else { 1730 try { 1731 System.out.println(XML.toJSONObject(xml.toString()).toString(2)); 1732 } catch (JSONException e) { 1733 log.error(String.format("XML to JSON conversion failed: %s\nOutput was:\n%s", e.getMessage(), 1734 xml.toString())); 1735 } 1736 } 1737 } catch (JAXBException e) { 1738 log.error("Output serialization failed: " + e.getMessage(), e); 1739 errorValue = EXIT_CODE_NOT_RUNNING; 1740 } 1741 } 1742 1743 /** 1744 * Stop stream gobblers contained in the given ArrayList 1745 * 1746 * @since 5.5 1747 * @see #logProcessStreams(Process, boolean) 1748 */ 1749 public void waitForProcessStreams(ArrayList<ThreadedStreamGobbler> sgArray) { 1750 for (ThreadedStreamGobbler streamGobbler : sgArray) { 1751 try { 1752 streamGobbler.join(STREAM_MAX_WAIT); 1753 } catch (InterruptedException e) { 1754 streamGobbler.interrupt(); 1755 } 1756 } 1757 } 1758 1759 /** 1760 * @since 5.5 1761 * @param classpath 1762 * @param baseDir 1763 * @return classpath with all jar files in baseDir 1764 * @throws IOException 1765 */ 1766 protected String getClassPath(String classpath, File baseDir) throws IOException { 1767 File[] files = getFilename(baseDir, ".*"); 1768 for (File file : files) { 1769 classpath += System.getProperty("path.separator") + file.getPath(); 1770 } 1771 return classpath; 1772 } 1773 1774 /** 1775 * @since 5.5 1776 * @param baseDir 1777 * @param filePattern 1778 * @return filename matching filePattern in baseDir 1779 */ 1780 protected File[] getFilename(File baseDir, final String filePattern) { 1781 File[] files = baseDir.listFiles(new FilenameFilter() { 1782 @Override 1783 public boolean accept(File basedir, String filename) { 1784 return filename.matches(filePattern + "(-[0-9].*)?\\.jar"); 1785 } 1786 }); 1787 return files; 1788 } 1789 1790 protected class ShutdownThread extends Thread { 1791 1792 private NuxeoLauncher launcher; 1793 1794 public ShutdownThread(NuxeoLauncher launcher) { 1795 super(); 1796 this.launcher = launcher; 1797 } 1798 1799 @Override 1800 public void run() { 1801 log.debug("Shutting down..."); 1802 if (launcher.isRunning()) { 1803 launcher.stop(); 1804 } 1805 log.debug("Shutdown complete."); 1806 } 1807 } 1808 1809 protected void addShutdownHook() { 1810 log.debug("Add shutdown hook"); 1811 shutdownHook = new ShutdownThread(this); 1812 Runtime.getRuntime().addShutdownHook(shutdownHook); 1813 } 1814 1815 /** 1816 * Stops the server. Will try to call specific class for a clean stop, retry, waiting between each try, then kill 1817 * the process if still running. 1818 */ 1819 public void stop(boolean logProcessOutput) { 1820 long startTime = new Date().getTime(); 1821 long deltaTime; 1822 try { 1823 if (!isRunning()) { 1824 log.warn("Server is not running."); 1825 return; 1826 } 1827 if (!quiet) { 1828 System.out.print("\nStopping server..."); 1829 } 1830 int nbTry = 0; 1831 boolean retry = false; 1832 int stopMaxWait = Integer.parseInt(configurationGenerator.getUserConfig().getProperty(STOP_MAX_WAIT_PARAM, 1833 STOP_MAX_WAIT_DEFAULT)); 1834 do { 1835 List<String> stopCommand = new ArrayList<>(); 1836 stopCommand.add(getJavaExecutable().getPath()); 1837 stopCommand.add("-cp"); 1838 stopCommand.add(getShutdownClassPath()); 1839 stopCommand.addAll(getNuxeoProperties()); 1840 stopCommand.addAll(getServerProperties()); 1841 setServerStopCommand(stopCommand); 1842 for (String param : params) { 1843 stopCommand.add(param); 1844 } 1845 ProcessBuilder pb = new ProcessBuilder(getOSCommand(stopCommand)); 1846 pb.directory(configurationGenerator.getNuxeoHome()); 1847 // pb = pb.redirectErrorStream(true); 1848 log.debug("Server command: " + pb.command()); 1849 try { 1850 Process stopProcess = pb.start(); 1851 ArrayList<ThreadedStreamGobbler> sgArray = logProcessStreams(stopProcess, logProcessOutput); 1852 stopProcess.waitFor(); 1853 waitForProcessStreams(sgArray); 1854 boolean wait = true; 1855 while (wait) { 1856 try { 1857 if (stopProcess.exitValue() == 0) { 1858 // Successful call for server stop 1859 retry = false; 1860 } else { 1861 // Failed to call for server stop 1862 retry = ++nbTry < STOP_NB_TRY; 1863 if (!quiet) { 1864 System.out.print("."); 1865 } 1866 Thread.sleep(STOP_SECONDS_BEFORE_NEXT_TRY * 1000); 1867 } 1868 wait = false; 1869 } catch (IllegalThreadStateException e) { 1870 // Stop call is still running 1871 wait = true; 1872 if (!quiet) { 1873 System.out.print("."); 1874 } 1875 Thread.sleep(1000); 1876 } 1877 } 1878 // Exit if there's no way to check for server stop 1879 if (!processManager.canFindPid()) { 1880 log.warn("Can't check server status on your OS."); 1881 return; 1882 } 1883 // Wait a few seconds for effective stop 1884 deltaTime = 0; 1885 do { 1886 if (!quiet) { 1887 System.out.print("."); 1888 } 1889 Thread.sleep(1000); 1890 deltaTime = (new Date().getTime() - startTime) / 1000; 1891 } while (!retry && getPid() != null && deltaTime < stopMaxWait); 1892 } catch (InterruptedException e) { 1893 log.error(e); 1894 } 1895 } while (retry); 1896 if (getPid() == null) { 1897 log.warn("Server stopped."); 1898 } else { 1899 log.info("No answer from server, try to kill process " + pid + "..."); 1900 processManager.kill(nuxeoProcess, pid); 1901 if (getPid() == null) { 1902 log.warn("Server forcibly stopped."); 1903 } 1904 } 1905 } catch (IOException e) { 1906 log.error("Could not manage process!", e); 1907 } 1908 } 1909 1910 protected abstract void setServerStopCommand(List<String> command); 1911 1912 private String getPid() throws IOException { 1913 pid = processManager.findPid(processRegex); 1914 log.debug("regexp: " + processRegex + " pid:" + pid); 1915 return pid; 1916 } 1917 1918 /** 1919 * Configure the server after checking installation 1920 * 1921 * @throws ConfigurationException If an installation error is detected or if configuration fails 1922 */ 1923 public void configure() throws ConfigurationException { 1924 try { 1925 checkNoRunningServer(); 1926 configurationGenerator.checkJavaVersion(); 1927 configurationGenerator.run(); 1928 overrideJavaTmpDir = Boolean.parseBoolean(configurationGenerator.getUserConfig().getProperty( 1929 OVERRIDE_JAVA_TMPDIR_PARAM, "true")); 1930 } catch (ConfigurationException e) { 1931 errorValue = EXIT_CODE_NOT_CONFIGURED; 1932 throw e; 1933 } 1934 } 1935 1936 /** 1937 * @return Default max wait depending on server (ie JBoss takes much more time than Tomcat) 1938 */ 1939 private String getDefaultMaxWait() { 1940 return START_MAX_WAIT_DEFAULT; 1941 } 1942 1943 /** 1944 * Return process status (running or not) as String, depending on OS capability to manage processes. Set status 1945 * value following "http://refspecs.freestandards.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html" 1946 * 1947 * @see #getStatus() 1948 */ 1949 public String status() { 1950 try { 1951 if (processManager instanceof PureJavaProcessManager) { 1952 status = STATUS_CODE_UNKNOWN; 1953 return "Can't check server status on your OS."; 1954 } 1955 if (getPid() == null) { 1956 status = STATUS_CODE_OFF; 1957 return "Server is not running."; 1958 } else { 1959 status = STATUS_CODE_ON; 1960 return "Server is running with process ID " + getPid() + "."; 1961 } 1962 } catch (IOException e) { 1963 status = STATUS_CODE_UNKNOWN; 1964 return "Could not check existing process (" + e.getMessage() + ")."; 1965 } 1966 } 1967 1968 /** 1969 * Last status value set by {@link #status()}. 1970 */ 1971 public int getStatus() { 1972 return status; 1973 } 1974 1975 /** 1976 * Last error value set by any method. Exit code values are following the Linux Standard Base Core Specification 1977 * 4.1. 1978 */ 1979 public int getErrorValue() { 1980 return errorValue; 1981 } 1982 1983 /** 1984 * @throws ParseException 1985 * @return a NuxeoLauncher instance specific to current server ( Tomcat or Jetty). 1986 * @throws ConfigurationException If server cannot be identified 1987 * @since 5.5 1988 */ 1989 public static NuxeoLauncher createLauncher(String[] args) throws ConfigurationException, ParseException { 1990 CommandLine cmdLine = parseOptions(args); 1991 ConfigurationGenerator cg = new ConfigurationGenerator(quiet, debug); 1992 if (cmdLine.hasOption(OPTION_HIDE_DEPRECATION)) { 1993 cg.hideDeprecationWarnings(true); 1994 } 1995 NuxeoLauncher launcher; 1996 if (cg.isJetty) { 1997 launcher = new NuxeoJettyLauncher(cg); 1998 } else if (cg.isTomcat) { 1999 launcher = new NuxeoTomcatLauncher(cg); 2000 } else { 2001 throw new ConfigurationException("Unknown server!"); 2002 } 2003 launcher.setArgs(cmdLine); 2004 return launcher; 2005 } 2006 2007 /** 2008 * Sets from program arguments the launcher command and additional parameters. 2009 * 2010 * @param cmdLine Program arguments; may be used by launcher implementation. Must not be null or empty. 2011 * @throws ConfigurationException 2012 */ 2013 private void setArgs(CommandLine cmdLine) throws ConfigurationException { 2014 this.cmdLine = cmdLine; 2015 extractCommandAndParams(cmdLine.getArgs()); 2016 // Use GUI? 2017 if (cmdLine.hasOption(OPTION_GUI)) { 2018 useGui = Boolean.valueOf(ConnectBroker.parseAnswer(cmdLine.getOptionValue(OPTION_GUI))); 2019 log.debug("GUI: " + cmdLine.getOptionValue(OPTION_GUI) + " -> " + new Boolean(useGui).toString()); 2020 } else if (OPTION_GUI.equalsIgnoreCase(command)) { 2021 useGui = true; 2022 // Shift params and extract command if there is one 2023 extractCommandAndParams(params); 2024 } else { 2025 if (PlatformUtils.isWindows()) { 2026 useGui = true; 2027 log.debug("GUI: option not set - platform is Windows -> start GUI"); 2028 } else { 2029 useGui = false; 2030 log.debug("GUI: option not set - platform is not Windows -> do not start GUI"); 2031 } 2032 } 2033 // Output format 2034 if (cmdLine.hasOption(OPTION_XML)) { 2035 setXMLOutput(); 2036 } 2037 if (cmdLine.hasOption(OPTION_JSON)) { 2038 setJSONOutput(); 2039 } 2040 if (cmdLine.hasOption(OPTION_CLID)) { 2041 try { 2042 getConnectBroker().setCLID(cmdLine.getOptionValue(OPTION_CLID)); 2043 } catch (NoCLID | IOException | PackageException e) { 2044 throw new ConfigurationException(e); 2045 } 2046 } 2047 } 2048 2049 private void extractCommandAndParams(String[] args) { 2050 if (args.length > 0) { 2051 command = args[0]; 2052 log.debug("Launcher command: " + command); 2053 // Command parameters 2054 if (args.length > 1) { 2055 params = Arrays.copyOfRange(args, 1, args.length); 2056 if (log.isDebugEnabled()) { 2057 log.debug("Command parameters: " + ArrayUtils.toString(params)); 2058 } 2059 } else { 2060 params = new String[0]; 2061 } 2062 } else { 2063 command = null; 2064 } 2065 } 2066 2067 /** 2068 * Set launcher in quiet mode 2069 * 2070 * @since 5.5 2071 */ 2072 protected static void setQuiet() { 2073 quiet = true; 2074 Log4JHelper.setQuiet(Log4JHelper.CONSOLE_APPENDER_NAME); 2075 } 2076 2077 /** 2078 * @param categories Root categories to switch DEBUG on. 2079 * @since 7.4 2080 */ 2081 protected static void setDebug(String[] categories, String defaultCategory) { 2082 debug = true; 2083 if (categories == null) { 2084 categories = new String[] { defaultCategory }; 2085 } 2086 Log4JHelper.setDebug(categories, true, true, new String[] { Log4JHelper.CONSOLE_APPENDER_NAME, "FILE" }); 2087 } 2088 2089 /** 2090 * @param categories Root categories to switch DEBUG on. 2091 * @since 5.6 2092 */ 2093 protected static void setDebug(String categories) { 2094 setDebug(categories, true); 2095 } 2096 2097 /** 2098 * @param categories Root categories to switch DEBUG on or off 2099 * @param activateDebug Set DEBUG on or off. 2100 * @since 5.6 2101 */ 2102 protected static void setDebug(String categories, boolean activateDebug) { 2103 debug = activateDebug; 2104 Log4JHelper.setDebug(categories, activateDebug, true, 2105 new String[] { Log4JHelper.CONSOLE_APPENDER_NAME, "FILE" }); 2106 } 2107 2108 /** 2109 * @param activateDebug if true, will activate the DEBUG logs 2110 * @since 5.5 2111 */ 2112 protected static void setDebug(boolean activateDebug) { 2113 setDebug("org.nuxeo", activateDebug); 2114 } 2115 2116 /** 2117 * @param isStrict if {@code true}, set the launcher strict option 2118 * @since 7.4 2119 * @see #OPTION_STRICT_DESC 2120 */ 2121 protected static void setStrict(boolean isStrict) { 2122 strict = isStrict; 2123 } 2124 2125 protected void setXMLOutput() { 2126 xmlOutput = true; 2127 } 2128 2129 protected void setJSONOutput() { 2130 jsonOutput = true; 2131 setXMLOutput(); 2132 } 2133 2134 public static void printShortHelp() { 2135 System.out.println(); 2136 HelpFormatter help = new HelpFormatter(); 2137 help.setSyntaxPrefix("USAGE\n"); 2138 help.setOptionComparator(null); 2139 help.setWidth(1000); 2140 help.printHelp(OPTION_HELP_USAGE, "OPTIONS", launcherOptions, null); 2141 System.out.println(OPTION_HELP_DESC_COMMANDS); 2142 } 2143 2144 public static void printLongHelp() { 2145 System.out.println(); 2146 HelpFormatter help = new HelpFormatter(); 2147 help.setSyntaxPrefix("USAGE\n"); 2148 help.setOptionComparator(null); 2149 help.setWidth(1000); 2150 help.printHelp(OPTION_HELP_USAGE, OPTION_HELP_HEADER, launcherOptions, null); 2151 System.out.println(OPTION_HELP_DESC_ENV); 2152 System.out.println(OPTION_HELP_DESC_COMMANDS); 2153 System.out.println(OPTION_HELP_FOOTER); 2154 } 2155 2156 /** 2157 * Work best with current nuxeoProcess. If nuxeoProcess is null or has exited, then will try to get process ID (so, 2158 * result in that case depends on OS capabilities). 2159 * 2160 * @return true if current process is running or if a running PID is found 2161 */ 2162 public boolean isRunning() { 2163 if (nuxeoProcess != null) { 2164 try { 2165 nuxeoProcess.exitValue(); 2166 // Previous process has exited 2167 nuxeoProcess = null; 2168 } catch (IllegalThreadStateException exception) { 2169 return true; 2170 } 2171 } 2172 try { 2173 return (getPid() != null); 2174 } catch (IOException e) { 2175 log.error(e); 2176 return false; 2177 } 2178 } 2179 2180 /** 2181 * @since 5.5 2182 * @return true if Nuxeo finished starting 2183 */ 2184 public boolean isStarted() { 2185 boolean isStarted; 2186 if (configurationGenerator.isWizardRequired()) { 2187 isStarted = isRunning(); 2188 } else { 2189 try { 2190 isStarted = isRunning() && statusServletClient.isStarted(); 2191 } catch (SocketTimeoutException e) { 2192 isStarted = false; 2193 } 2194 } 2195 return isStarted; 2196 } 2197 2198 /** 2199 * @return Server log file 2200 */ 2201 public File getLogFile() { 2202 return new File(configurationGenerator.getLogDir(), "server.log"); 2203 } 2204 2205 /** 2206 * @return Server URL 2207 */ 2208 public String getURL() { 2209 return configurationGenerator.getUserConfig().getProperty(ConfigurationGenerator.PARAM_NUXEO_URL); 2210 } 2211 2212 protected ConnectBroker getConnectBroker() throws IOException, PackageException { 2213 if (connectBroker == null) { 2214 connectBroker = new ConnectBroker(configurationGenerator.getEnv()); 2215 if (cmdLine.hasOption(OPTION_ACCEPT)) { 2216 connectBroker.setAccept(cmdLine.getOptionValue(OPTION_ACCEPT)); 2217 } 2218 if (cmdLine.hasOption(OPTION_RELAX)) { 2219 connectBroker.setRelax(cmdLine.getOptionValue(OPTION_RELAX)); 2220 } 2221 if (cmdLine.hasOption(OPTION_SNAPSHOT) || isSNAPSHOTDistribution()) { 2222 connectBroker.setAllowSNAPSHOT(true); 2223 } 2224 cset = connectBroker.getCommandSet(); 2225 } 2226 return connectBroker; 2227 } 2228 2229 /** 2230 * @since 5.9.1 2231 */ 2232 private boolean isSNAPSHOTDistribution() { 2233 return new Version(getDistributionInfo().version).isSnapshot(); 2234 } 2235 2236 /** 2237 * List all local packages. 2238 * 2239 * @throws IOException 2240 * @throws PackageException 2241 */ 2242 protected void pkgList() throws IOException, PackageException { 2243 getConnectBroker().listPending(configurationGenerator.getInstallFile()); 2244 getConnectBroker().pkgList(); 2245 } 2246 2247 /** 2248 * List all packages including remote ones. 2249 * 2250 * @since 5.6 2251 * @throws IOException 2252 * @throws PackageException 2253 */ 2254 protected void pkgListAll() throws IOException, PackageException { 2255 getConnectBroker().listPending(configurationGenerator.getInstallFile()); 2256 getConnectBroker().pkgListAll(); 2257 } 2258 2259 protected boolean pkgAdd(String[] pkgNames) throws IOException, PackageException { 2260 boolean cmdOK = getConnectBroker().pkgAdd(Arrays.asList(pkgNames), cmdLine.hasOption(OPTION_IGNORE_MISSING)); 2261 if (!cmdOK) { 2262 errorValue = EXIT_CODE_ERROR; 2263 } 2264 return cmdOK; 2265 } 2266 2267 protected boolean pkgInstall(String[] pkgIDs) throws IOException, PackageException { 2268 boolean cmdOK = true; 2269 if (configurationGenerator.isInstallInProgress()) { 2270 cmdOK = getConnectBroker().executePending(configurationGenerator.getInstallFile(), true, 2271 !cmdLine.hasOption(OPTION_NODEPS)); 2272 } 2273 cmdOK = cmdOK && getConnectBroker().pkgInstall(Arrays.asList(pkgIDs), cmdLine.hasOption(OPTION_IGNORE_MISSING)); 2274 if (!cmdOK) { 2275 errorValue = EXIT_CODE_ERROR; 2276 } 2277 return cmdOK; 2278 } 2279 2280 protected boolean pkgUninstall(String[] pkgIDs) throws IOException, PackageException { 2281 boolean cmdOK = getConnectBroker().pkgUninstall(Arrays.asList(pkgIDs)); 2282 if (!cmdOK) { 2283 errorValue = EXIT_CODE_ERROR; 2284 } 2285 return cmdOK; 2286 } 2287 2288 protected boolean pkgRemove(String[] pkgIDs) throws IOException, PackageException { 2289 boolean cmdOK = getConnectBroker().pkgRemove(Arrays.asList(pkgIDs)); 2290 if (!cmdOK) { 2291 errorValue = EXIT_CODE_ERROR; 2292 } 2293 return cmdOK; 2294 } 2295 2296 protected boolean pkgReset() throws IOException, PackageException { 2297 boolean cmdOK = getConnectBroker().pkgReset(); 2298 if (!cmdOK) { 2299 errorValue = EXIT_CODE_ERROR; 2300 } 2301 return cmdOK; 2302 } 2303 2304 /** 2305 * @since 5.6 2306 */ 2307 protected void printInstanceXMLOutput(InstanceInfo instance) { 2308 try { 2309 JAXBContext jaxbContext = JAXBContext.newInstance(InstanceInfo.class, DistributionInfo.class, 2310 PackageInfo.class, ConfigurationInfo.class, KeyValueInfo.class); 2311 printXMLOutput(jaxbContext, instance); 2312 } catch (JAXBException e) { 2313 log.error("Output serialization failed: " + e.getMessage()); 2314 log.debug(e, e); 2315 errorValue = EXIT_CODE_NOT_RUNNING; 2316 } 2317 } 2318 2319 /** 2320 * @throws PackageException 2321 * @throws IOException 2322 * @throws ConfigurationException 2323 * @since 5.6 2324 */ 2325 protected InstanceInfo showConfig() throws IOException, PackageException, ConfigurationException { 2326 InstanceInfo nxInstance = new InstanceInfo(); 2327 log.info("***** Nuxeo instance configuration *****"); 2328 nxInstance.NUXEO_CONF = configurationGenerator.getNuxeoConf().getPath(); 2329 log.info("NUXEO_CONF: " + nxInstance.NUXEO_CONF); 2330 nxInstance.NUXEO_HOME = configurationGenerator.getNuxeoHome().getPath(); 2331 log.info("NUXEO_HOME: " + nxInstance.NUXEO_HOME); 2332 // CLID 2333 try { 2334 nxInstance.clid = getConnectBroker().getCLID(); 2335 log.info("Instance CLID: " + nxInstance.clid); 2336 } catch (NoCLID e) { 2337 // leave nxInstance.clid unset 2338 } catch (IOException | PackageException e) { 2339 // something went wrong in the NuxeoConnectClient initialization 2340 errorValue = EXIT_CODE_UNAUTHORIZED; 2341 throw new ConfigurationException("Could not initialize NuxeoConnectClient", e); 2342 } 2343 // distribution.properties 2344 DistributionInfo nxDistrib = getDistributionInfo(); 2345 nxInstance.distribution = nxDistrib; 2346 log.info("** Distribution"); 2347 log.info("- name: " + nxDistrib.name); 2348 log.info("- server: " + nxDistrib.server); 2349 log.info("- version: " + nxDistrib.version); 2350 log.info("- date: " + nxDistrib.date); 2351 log.info("- packaging: " + nxDistrib.packaging); 2352 // packages 2353 List<LocalPackage> pkgs = getConnectBroker().getPkgList(); 2354 log.info("** Packages:"); 2355 List<String> pkgTemplates = new ArrayList<>(); 2356 for (LocalPackage pkg : pkgs) { 2357 nxInstance.packages.add(new PackageInfo(pkg)); 2358 log.info(String.format("- %s (version: %s - id: %s - state: %s)", pkg.getName(), pkg.getVersion(), 2359 pkg.getId(), pkg.getPackageState().getLabel())); 2360 // store template(s) added by this package 2361 try { 2362 File installFile = pkg.getInstallFile(); 2363 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 2364 DocumentBuilder db = dbf.newDocumentBuilder(); 2365 Document dom = db.parse(installFile); 2366 NodeList nodes = dom.getDocumentElement().getElementsByTagName("config"); 2367 for (int i = 0; i < nodes.getLength(); i++) { 2368 Element node = (Element) nodes.item(i); 2369 if (node.hasAttribute("addtemplate")) { 2370 pkgTemplates.add(node.getAttribute("addtemplate")); 2371 } 2372 } 2373 } catch (Exception e) { 2374 log.warn("Could not parse install file for " + pkg.getName(), e); 2375 } 2376 } 2377 // nuxeo.conf 2378 ConfigurationInfo nxConfig = new ConfigurationInfo(); 2379 nxConfig.dbtemplate = configurationGenerator.extractDatabaseTemplateName(); 2380 log.info("** Templates:"); 2381 log.info("Database template: " + nxConfig.dbtemplate); 2382 String userTemplates = configurationGenerator.getUserTemplates(); 2383 StringTokenizer st = new StringTokenizer(userTemplates, ","); 2384 while (st.hasMoreTokens()) { 2385 String template = st.nextToken(); 2386 if (template.equals(nxConfig.dbtemplate)) { 2387 continue; 2388 } 2389 if (pkgTemplates.contains(template)) { 2390 nxConfig.pkgtemplates.add(template); 2391 log.info("Package template: " + template); 2392 } else { 2393 File testBase = new File(configurationGenerator.getNuxeoHome(), ConfigurationGenerator.TEMPLATES 2394 + File.separator + template); 2395 if (testBase.exists()) { 2396 nxConfig.basetemplates.add(template); 2397 log.info("Base template: " + template); 2398 } else { 2399 nxConfig.usertemplates.add(template); 2400 log.info("User template: " + template); 2401 } 2402 } 2403 } 2404 log.info("** Settings from nuxeo.conf:"); 2405 CryptoProperties userConfig = configurationGenerator.getUserConfig(); 2406 for (Object item : new TreeSet<>(userConfig.keySet())) { 2407 String key = (String) item; 2408 String value = userConfig.getRawProperty(key); 2409 if (key.equals("JAVA_OPTS")) { 2410 value = getJavaOptsProperty(); 2411 } 2412 KeyValueInfo kv = new KeyValueInfo(key, value); 2413 nxConfig.keyvals.add(kv); 2414 if (!key.contains("password") && !key.equals(Environment.SERVER_STATUS_KEY) && !Crypto.isEncrypted(value)) { 2415 log.info(key + "=" + value); 2416 } else { 2417 log.info(key + "=********"); 2418 } 2419 } 2420 nxInstance.config = nxConfig; 2421 log.info("****************************************"); 2422 if (xmlOutput) { 2423 printInstanceXMLOutput(nxInstance); 2424 } 2425 return nxInstance; 2426 } 2427 2428 /** 2429 * @since 5.9.1 2430 */ 2431 protected DistributionInfo getDistributionInfo() { 2432 File distFile = new File(configurationGenerator.getConfigDir(), "distribution.properties"); 2433 if (!distFile.exists()) { 2434 // fallback in the file in templates 2435 distFile = new File(configurationGenerator.getNuxeoHome(), "templates"); 2436 distFile = new File(distFile, "common"); 2437 distFile = new File(distFile, "config"); 2438 distFile = new File(distFile, "distribution.properties"); 2439 } 2440 DistributionInfo nxDistrib; 2441 try { 2442 nxDistrib = new DistributionInfo(distFile); 2443 } catch (IOException e) { 2444 nxDistrib = new DistributionInfo(); 2445 } 2446 return nxDistrib; 2447 } 2448 2449 /** 2450 * @since 5.6 2451 * @param pkgsToAdd 2452 * @param pkgsToInstall 2453 * @param pkgsToUninstall 2454 * @param pkgsToRemove 2455 * @return true if request execution was fine 2456 * @throws IOException 2457 * @throws PackageException 2458 */ 2459 protected boolean pkgRequest(List<String> pkgsToAdd, List<String> pkgsToInstall, List<String> pkgsToUninstall, 2460 List<String> pkgsToRemove) throws IOException, PackageException { 2461 boolean cmdOK = true; 2462 if (configurationGenerator.isInstallInProgress()) { 2463 cmdOK = getConnectBroker().executePending(configurationGenerator.getInstallFile(), true, true); 2464 } 2465 cmdOK = cmdOK 2466 && getConnectBroker().pkgRequest(pkgsToAdd, pkgsToInstall, pkgsToUninstall, pkgsToRemove, true, 2467 cmdLine.hasOption(OPTION_IGNORE_MISSING)); 2468 if (!cmdOK) { 2469 errorValue = EXIT_CODE_ERROR; 2470 } 2471 return cmdOK; 2472 } 2473 2474 /** 2475 * Update the cached list of remote packages 2476 * 2477 * @since 5.6 2478 * @return true 2479 * @throws IOException 2480 * @throws PackageException 2481 */ 2482 protected boolean pkgRefreshCache() throws IOException, PackageException { 2483 getConnectBroker().refreshCache(); 2484 return true; 2485 } 2486 2487 /** 2488 * Add packages from the distribution to the local cache 2489 * 2490 * @throws PackageException 2491 * @throws IOException 2492 * @since 5.6 2493 */ 2494 protected boolean pkgInit() throws IOException, PackageException { 2495 return getConnectBroker().addDistributionPackages(); 2496 } 2497 2498 /** 2499 * Uninstall and remove all packages from the local cache 2500 * 2501 * @return {@code true} if command succeed 2502 * @throws PackageException 2503 * @throws IOException 2504 * @since 5.6 2505 */ 2506 protected boolean pkgPurge() throws PackageException, IOException { 2507 return getConnectBroker().pkgPurge(); 2508 } 2509 2510 /** 2511 * Install the hotfixes available for the instance 2512 * 2513 * @return {@code true} if command succeed 2514 * @throws PackageException 2515 * @throws IOException 2516 * @since 5.6 2517 */ 2518 protected boolean pkgHotfix() throws IOException, PackageException { 2519 return getConnectBroker().pkgHotfix(); 2520 } 2521 2522 /** 2523 * Upgrade the marketplace packages (addons) available for the instance 2524 * 2525 * @return {@code true} if command succeed 2526 * @throws PackageException 2527 * @throws IOException 2528 * @since 5.6 2529 */ 2530 protected boolean pkgUpgrade() throws IOException, PackageException { 2531 return getConnectBroker().pkgUpgrade(); 2532 } 2533 2534 /** 2535 * Combined install/uninstall request 2536 * 2537 * @param request Space separated list of package names or IDs prefixed with + (install) or - (uninstall) 2538 * @throws IOException 2539 * @throws PackageException 2540 * @since 5.6 2541 */ 2542 protected boolean pkgCompoundRequest(List<String> request) throws IOException, PackageException { 2543 List<String> add = new ArrayList<>(); 2544 List<String> install = new ArrayList<>(); 2545 List<String> uninstall = new ArrayList<>(); 2546 for (String param : request) { 2547 for (String subparam : param.split("[ ,]")) { 2548 if (subparam.charAt(0) == '-') { 2549 uninstall.add(subparam.substring(1)); 2550 } else if (subparam.charAt(0) == '+') { 2551 install.add(subparam.substring(1)); 2552 } else { 2553 add.add(subparam); 2554 } 2555 } 2556 } 2557 return pkgRequest(add, install, uninstall, null); 2558 } 2559 2560 protected boolean pkgSetRequest(List<String> request, boolean nodeps) throws IOException, PackageException { 2561 boolean cmdOK; 2562 if (nodeps) { 2563 cmdOK = getConnectBroker().pkgSet(request, cmdLine.hasOption(OPTION_IGNORE_MISSING)); 2564 } else { 2565 cmdOK = getConnectBroker().pkgRequest(null, request, null, null, false, 2566 cmdLine.hasOption(OPTION_IGNORE_MISSING)); 2567 } 2568 if (!cmdOK) { 2569 errorValue = EXIT_CODE_ERROR; 2570 } 2571 return cmdOK; 2572 } 2573 2574 /** 2575 * dpkg-like command which returns package location, version, dependencies, conflicts, ... 2576 * 2577 * @param packages List of packages identified by their ID, name or local filename. 2578 * @return false if unable to show package information. 2579 * @throws PackageException 2580 * @throws IOException 2581 * @since 5.7 2582 */ 2583 protected boolean pkgShow(String[] packages) throws IOException, PackageException { 2584 boolean cmdOK = getConnectBroker().pkgShow(Arrays.asList(packages)); 2585 if (!cmdOK) { 2586 errorValue = EXIT_CODE_ERROR; 2587 } 2588 return cmdOK; 2589 } 2590 2591}