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