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