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