001/* 002 * (C) Copyright 2010-2017 Nuxeo (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 * Kevin Leturc <kleturc@nuxeo.com> 019 */ 020package org.nuxeo.launcher.config; 021 022import java.io.BufferedReader; 023import java.io.BufferedWriter; 024import java.io.File; 025import java.io.FileInputStream; 026import java.io.FileNotFoundException; 027import java.io.FileReader; 028import java.io.FileWriter; 029import java.io.IOException; 030import java.io.InputStream; 031import java.io.StringWriter; 032import java.io.Writer; 033import java.net.Inet6Address; 034import java.net.InetAddress; 035import java.net.MalformedURLException; 036import java.net.ServerSocket; 037import java.net.URL; 038import java.net.URLClassLoader; 039import java.net.UnknownHostException; 040import java.security.MessageDigest; 041import java.sql.Connection; 042import java.sql.Driver; 043import java.sql.DriverManager; 044import java.sql.SQLException; 045import java.util.ArrayList; 046import java.util.Arrays; 047import java.util.Enumeration; 048import java.util.HashMap; 049import java.util.HashSet; 050import java.util.Hashtable; 051import java.util.List; 052import java.util.Map; 053import java.util.Map.Entry; 054import java.util.Properties; 055import java.util.Set; 056import java.util.StringTokenizer; 057import java.util.TreeSet; 058import java.util.UUID; 059import java.util.function.Function; 060import java.util.regex.Matcher; 061import java.util.regex.Pattern; 062import java.util.stream.Collectors; 063import java.util.stream.Stream; 064 065import javax.naming.NamingException; 066import javax.naming.directory.DirContext; 067import javax.naming.directory.InitialDirContext; 068 069import org.apache.commons.codec.binary.Hex; 070import org.apache.commons.codec.digest.DigestUtils; 071import org.apache.commons.lang3.ArrayUtils; 072import org.apache.commons.lang3.StringUtils; 073import org.apache.commons.lang3.SystemUtils; 074import org.apache.commons.logging.Log; 075import org.apache.commons.logging.LogFactory; 076import org.apache.commons.text.StrSubstitutor; 077import org.apache.log4j.Logger; 078import org.apache.log4j.helpers.NullEnumeration; 079import org.nuxeo.common.Environment; 080import org.nuxeo.common.codec.Crypto; 081import org.nuxeo.common.codec.CryptoProperties; 082import org.nuxeo.common.utils.TextTemplate; 083import org.nuxeo.launcher.commons.DatabaseDriverException; 084import org.nuxeo.launcher.config.JVMVersion.UpTo; 085import org.nuxeo.log4j.Log4JHelper; 086 087import freemarker.core.ParseException; 088import freemarker.template.TemplateException; 089 090/** 091 * Builder for server configuration and datasource files from templates and properties. 092 * 093 * @author jcarsique 094 */ 095public class ConfigurationGenerator { 096 097 /** 098 * @since 6.0 099 */ 100 public static final String TEMPLATE_SEPARATOR = ","; 101 102 /** 103 * Accurate but not used internally. NXP-18023: Java 8 update 40+ required 104 * 105 * @since 5.7 106 */ 107 public static final String[] COMPLIANT_JAVA_VERSIONS = new String[] { "1.8.0_40", "9" }; 108 109 /** 110 * @since 5.6 111 */ 112 protected static final String CONFIGURATION_PROPERTIES = "configuration.properties"; 113 114 private static final Log log = LogFactory.getLog(ConfigurationGenerator.class); 115 116 public static final String NUXEO_CONF = "nuxeo.conf"; 117 118 public static final String TEMPLATES = "templates"; 119 120 public static final String NUXEO_DEFAULT_CONF = "nuxeo.defaults"; 121 122 /** 123 * Absolute or relative PATH to the user chosen templates (comma separated list) 124 */ 125 public static final String PARAM_TEMPLATES_NAME = "nuxeo.templates"; 126 127 public static final String PARAM_TEMPLATE_DBNAME = "nuxeo.dbtemplate"; 128 129 /** 130 * @since 9.3 131 */ 132 public static final String PARAM_TEMPLATE_DBSECONDARY_NAME = "nuxeo.dbnosqltemplate"; 133 134 public static final String PARAM_TEMPLATE_DBTYPE = "nuxeo.db.type"; 135 136 /** 137 * @since 9.3 138 */ 139 public static final String PARAM_TEMPLATE_DBSECONDARY_TYPE = "nuxeo.dbsecondary.type"; 140 141 public static final String OLD_PARAM_TEMPLATES_PARSING_EXTENSIONS = "nuxeo.templates.parsing.extensions"; 142 143 public static final String PARAM_TEMPLATES_PARSING_EXTENSIONS = "nuxeo.plaintext_parsing_extensions"; 144 145 public static final String PARAM_TEMPLATES_FREEMARKER_EXTENSIONS = "nuxeo.freemarker_parsing_extensions"; 146 147 /** 148 * Absolute or relative PATH to the included templates (comma separated list) 149 */ 150 protected static final String PARAM_INCLUDED_TEMPLATES = "nuxeo.template.includes"; 151 152 public static final String PARAM_FORCE_GENERATION = "nuxeo.force.generation"; 153 154 public static final String BOUNDARY_BEGIN = "### BEGIN - DO NOT EDIT BETWEEN BEGIN AND END ###"; 155 156 public static final String BOUNDARY_END = "### END - DO NOT EDIT BETWEEN BEGIN AND END ###"; 157 158 public static final List<String> DB_LIST = Arrays.asList("default", "mongodb","postgresql", "oracle", "mysql", "mariadb", 159 "mssql", "db2"); 160 161 public static final List<String> DB_SECONDARY_LIST = Arrays.asList("none", "marklogic"); 162 163 public static final List<String> DB_EXCLUDE_CHECK_LIST = Arrays.asList("default", "none", "mongodb"); 164 165 public static final String PARAM_WIZARD_DONE = "nuxeo.wizard.done"; 166 167 public static final String PARAM_WIZARD_RESTART_PARAMS = "wizard.restart.params"; 168 169 public static final String PARAM_FAKE_WINDOWS = "org.nuxeo.fake.vindoz"; 170 171 public static final String PARAM_LOOPBACK_URL = "nuxeo.loopback.url"; 172 173 public static final int MIN_PORT = 1; 174 175 public static final int MAX_PORT = 65535; 176 177 public static final int ADDRESS_PING_TIMEOUT = 1000; 178 179 public static final String PARAM_BIND_ADDRESS = "nuxeo.bind.address"; 180 181 public static final String PARAM_HTTP_PORT = "nuxeo.server.http.port"; 182 183 /** 184 * @deprecated Since 7.4. Use {@link Environment#SERVER_STATUS_KEY} instead 185 */ 186 @Deprecated 187 public static final String PARAM_STATUS_KEY = Environment.SERVER_STATUS_KEY; 188 189 public static final String PARAM_CONTEXT_PATH = "org.nuxeo.ecm.contextPath"; 190 191 public static final String PARAM_MP_DIR = "nuxeo.distribution.marketplace.dir"; 192 193 public static final String DISTRIBUTION_MP_DIR = "setupWizardDownloads"; 194 195 public static final String INSTALL_AFTER_RESTART = "installAfterRestart.log"; 196 197 public static final String PARAM_DB_DRIVER = "nuxeo.db.driver"; 198 199 public static final String PARAM_DB_JDBC_URL = "nuxeo.db.jdbc.url"; 200 201 public static final String PARAM_DB_HOST = "nuxeo.db.host"; 202 203 public static final String PARAM_DB_PORT = "nuxeo.db.port"; 204 205 public static final String PARAM_DB_NAME = "nuxeo.db.name"; 206 207 public static final String PARAM_DB_USER = "nuxeo.db.user"; 208 209 public static final String PARAM_DB_PWD = "nuxeo.db.password"; 210 211 /** 212 * @since 8.1 213 */ 214 public static final String PARAM_MONGODB_NAME = "nuxeo.mongodb.dbname"; 215 216 /** 217 * @since 8.1 218 */ 219 public static final String PARAM_MONGODB_SERVER = "nuxeo.mongodb.server"; 220 221 /** 222 * Catch values like ${env:PARAM_KEY:defaultValue} 223 * 224 * @since 9.1 225 */ 226 private static final Pattern ENV_VALUE_PATTERN = Pattern.compile( 227 "\\$\\{env(?<boolean>\\?\\?)?:(?<envparam>\\w*)(:?(?<defaultvalue>.*?)?)?\\}"); 228 229 /** 230 * Java options split by spaces followed by an even number of quotes (or zero). 231 * 232 * @since 9.3 233 */ 234 protected static final Pattern JAVA_OPTS_PATTERN = Pattern.compile("[ ]+(?=([^\"]*\"[^\"]*\")*[^\"]*$)"); 235 236 /** 237 * Keys which value must be displayed thoughtfully 238 * 239 * @since 8.1 240 */ 241 public static final List<String> SECRET_KEYS = Arrays.asList(PARAM_DB_PWD, "mailservice.password", 242 "mail.transport.password", "nuxeo.http.proxy.password", "nuxeo.ldap.bindpassword", 243 "nuxeo.user.emergency.password"); 244 245 /** 246 * @deprecated Since 7.10. Use {@link Environment#PRODUCT_NAME} 247 */ 248 @Deprecated 249 public static final String PARAM_PRODUCT_NAME = Environment.PRODUCT_NAME; 250 251 /** 252 * @deprecated Since 7.10. Use {@link Environment#PRODUCT_VERSION} 253 */ 254 @Deprecated 255 public static final String PARAM_PRODUCT_VERSION = Environment.PRODUCT_VERSION; 256 257 /** 258 * @since 5.6 259 */ 260 public static final String PARAM_NUXEO_URL = "nuxeo.url"; 261 262 /** 263 * Global dev property, duplicated from runtime framework 264 * 265 * @since 5.6 266 */ 267 public static final String NUXEO_DEV_SYSTEM_PROP = "org.nuxeo.dev"; 268 269 /** 270 * Seam hot reload property, also controlled by {@link #NUXEO_DEV_SYSTEM_PROP} 271 * 272 * @since 5.6 273 */ 274 public static final String SEAM_DEBUG_SYSTEM_PROP = "org.nuxeo.seam.debug"; 275 276 /** @since 8.4 */ 277 public static final String JVMCHECK_PROP = "jvmcheck"; 278 279 /** @since 8.4 */ 280 public static final String JVMCHECK_FAIL = "fail"; 281 282 /** @since 8.4 */ 283 public static final String JVMCHECK_NOFAIL = "nofail"; 284 285 /** 286 * Java options configured in <tt>bin/nuxeo.conf</tt> and <tt>bin/nuxeoctl</tt>. 287 * 288 * @since 9.3 289 */ 290 public static final String JAVA_OPTS_PROP = "launcher.java.opts"; 291 292 private final File nuxeoHome; 293 294 // User configuration file 295 private final File nuxeoConf; 296 297 // Chosen templates 298 private final List<File> includedTemplates = new ArrayList<>(); 299 300 // Common default configuration file 301 private File nuxeoDefaultConf; 302 303 public boolean isJetty; 304 305 public boolean isTomcat; 306 307 private ServerConfigurator serverConfigurator; 308 309 private BackingServiceConfigurator backingServicesConfigurator; 310 311 private boolean forceGeneration; 312 313 private Properties defaultConfig; 314 315 private CryptoProperties userConfig; 316 317 private boolean configurable = false; 318 319 private boolean onceGeneration = false; 320 321 private String templates; 322 323 // if PARAM_FORCE_GENERATION=once, set to false; else keep current value 324 private boolean setOnceToFalse = true; 325 326 // if PARAM_FORCE_GENERATION=false, set to once; else keep the current 327 // value 328 private boolean setFalseToOnce = false; 329 330 public boolean isConfigurable() { 331 return configurable; 332 } 333 334 public ConfigurationGenerator() { 335 this(true, false); 336 } 337 338 private boolean quiet = false; 339 340 private static boolean hideDeprecationWarnings = false; 341 342 private Environment env; 343 344 private Properties storedConfig; 345 346 private String currentConfigurationDigest; 347 348 /** 349 * @since 5.7 350 */ 351 protected Properties getStoredConfig() { 352 if (storedConfig == null) { 353 updateStoredConfig(); 354 } 355 return storedConfig; 356 } 357 358 protected static final Map<String, String> parametersMigration = new HashMap<String, String>() { 359 private static final long serialVersionUID = 1L; 360 361 { 362 put(OLD_PARAM_TEMPLATES_PARSING_EXTENSIONS, PARAM_TEMPLATES_PARSING_EXTENSIONS); 363 put("nuxeo.db.user.separator.key", "nuxeo.db.user_separator_key"); 364 put("mail.pop3.host", "mail.store.host"); 365 put("mail.pop3.port", "mail.store.port"); 366 put("mail.smtp.host", "mail.transport.host"); 367 put("mail.smtp.port", "mail.transport.port"); 368 put("mail.smtp.username", "mail.transport.username"); 369 put("mail.transport.username", "mail.transport.user"); 370 put("mail.smtp.password", "mail.transport.password"); 371 put("mail.smtp.usetls", "mail.transport.usetls"); 372 put("mail.smtp.auth", "mail.transport.auth"); 373 } 374 }; 375 376 /** 377 * @param quiet Suppress info level messages from the console output 378 * @param debug Activate debug level logging 379 * @since 5.6 380 */ 381 public ConfigurationGenerator(boolean quiet, boolean debug) { 382 this.quiet = quiet; 383 File serverHome = Environment.getDefault().getServerHome(); 384 if (serverHome != null) { 385 nuxeoHome = serverHome.getAbsoluteFile(); 386 } else { 387 File userDir = new File(System.getProperty("user.dir")); 388 if ("bin".equalsIgnoreCase(userDir.getName())) { 389 nuxeoHome = userDir.getParentFile().getAbsoluteFile(); 390 } else { 391 nuxeoHome = userDir.getAbsoluteFile(); 392 } 393 } 394 String nuxeoConfPath = System.getProperty(NUXEO_CONF); 395 if (nuxeoConfPath != null) { 396 nuxeoConf = new File(nuxeoConfPath).getAbsoluteFile(); 397 } else { 398 nuxeoConf = new File(nuxeoHome, "bin" + File.separator + "nuxeo.conf").getAbsoluteFile(); 399 } 400 System.setProperty(NUXEO_CONF, nuxeoConf.getPath()); 401 402 nuxeoDefaultConf = new File(nuxeoHome, TEMPLATES + File.separator + NUXEO_DEFAULT_CONF); 403 404 // detect server type based on System properties 405 isJetty = System.getProperty(JettyConfigurator.JETTY_HOME) != null; 406 isTomcat = System.getProperty(TomcatConfigurator.TOMCAT_HOME) != null; 407 if (!isJetty && !isTomcat) { 408 // fallback on jar detection 409 isTomcat = new File(nuxeoHome, "bin/bootstrap.jar").exists(); 410 String[] files = nuxeoHome.list(); 411 for (String file : files) { 412 if (file.startsWith("nuxeo-runtime-launcher")) { 413 isJetty = true; 414 break; 415 } 416 } 417 } 418 if (isTomcat) { 419 serverConfigurator = new TomcatConfigurator(this); 420 } else if (isJetty) { 421 serverConfigurator = new JettyConfigurator(this); 422 } else { 423 serverConfigurator = new UnknownServerConfigurator(this); 424 } 425 if (Logger.getRootLogger().getAllAppenders() instanceof NullEnumeration) { 426 serverConfigurator.initLogs(); 427 } 428 backingServicesConfigurator = new BackingServiceConfigurator(this); 429 String homeInfo = "Nuxeo home: " + nuxeoHome.getPath(); 430 String confInfo = "Nuxeo configuration: " + nuxeoConf.getPath(); 431 if (quiet) { 432 log.debug(homeInfo); 433 log.debug(confInfo); 434 } else { 435 log.info(homeInfo); 436 log.info(confInfo); 437 } 438 } 439 440 public void hideDeprecationWarnings(boolean hide) { 441 hideDeprecationWarnings = hide; 442 } 443 444 /** 445 * @see #PARAM_FORCE_GENERATION 446 */ 447 public void setForceGeneration(boolean forceGeneration) { 448 this.forceGeneration = forceGeneration; 449 } 450 451 /** 452 * @see #PARAM_FORCE_GENERATION 453 * @return true if configuration will be generated from templates 454 * @since 5.4.2 455 */ 456 public boolean isForceGeneration() { 457 return forceGeneration; 458 } 459 460 public CryptoProperties getUserConfig() { 461 return userConfig; 462 } 463 464 /** 465 * @since 5.4.2 466 */ 467 public final ServerConfigurator getServerConfigurator() { 468 return serverConfigurator; 469 } 470 471 /** 472 * Runs the configuration files generation. 473 */ 474 public void run() throws ConfigurationException { 475 if (init()) { 476 if (!serverConfigurator.isConfigured()) { 477 log.info("No current configuration, generating files..."); 478 generateFiles(); 479 } else if (forceGeneration) { 480 log.info("Configuration files generation (nuxeo.force.generation=" 481 + userConfig.getProperty(PARAM_FORCE_GENERATION) + ")..."); 482 generateFiles(); 483 } else { 484 log.info( 485 "Server already configured (set nuxeo.force.generation=true to force configuration files generation)."); 486 } 487 } 488 } 489 490 /** 491 * Initialize configurator, check requirements and load current configuration 492 * 493 * @return returns true if current install is configurable, else returns false 494 */ 495 public boolean init() { 496 return init(false); 497 } 498 499 /** 500 * Initialize configurator, check requirements and load current configuration 501 * 502 * @since 5.6 503 * @param forceReload If true, forces configuration reload. 504 * @return returns true if current install is configurable, else returns false 505 */ 506 public boolean init(boolean forceReload) { 507 if (serverConfigurator instanceof UnknownServerConfigurator) { 508 configurable = false; 509 forceGeneration = false; 510 log.warn("Server will be considered as not configurable."); 511 } 512 if (!nuxeoConf.exists()) { 513 log.info("Missing " + nuxeoConf); 514 configurable = false; 515 userConfig = new CryptoProperties(); 516 defaultConfig = new Properties(); 517 } else if (userConfig == null || userConfig.size() == 0 || forceReload) { 518 try { 519 if (forceReload) { 520 // force 'templates' reload 521 templates = null; 522 } 523 setBasicConfiguration(); 524 configurable = true; 525 } catch (ConfigurationException e) { 526 log.warn("Error reading basic configuration.", e); 527 configurable = false; 528 } 529 } else { 530 configurable = true; 531 } 532 return configurable; 533 } 534 535 /** 536 * @return Old templates 537 */ 538 public String changeTemplates(String newTemplates) { 539 String oldTemplates = templates; 540 templates = newTemplates; 541 try { 542 setBasicConfiguration(false); 543 configurable = true; 544 } catch (ConfigurationException e) { 545 log.warn("Error reading basic configuration.", e); 546 configurable = false; 547 } 548 return oldTemplates; 549 } 550 551 /** 552 * Change templates using given database template 553 * 554 * @param dbTemplate new database template 555 * @since 5.4.2 556 */ 557 public void changeDBTemplate(String dbTemplate) { 558 changeTemplates(rebuildTemplatesStr(dbTemplate)); 559 } 560 561 private void setBasicConfiguration() throws ConfigurationException { 562 setBasicConfiguration(true); 563 } 564 565 private void setBasicConfiguration(boolean save) throws ConfigurationException { 566 try { 567 // Load default configuration 568 defaultConfig = loadTrimmedProperties(nuxeoDefaultConf); 569 // Add System properties 570 defaultConfig.putAll(System.getProperties()); 571 userConfig = new CryptoProperties(defaultConfig); 572 573 // If Windows, replace backslashes in paths in nuxeo.conf 574 if (SystemUtils.IS_OS_WINDOWS) { 575 replaceBackslashes(); 576 } 577 // Load user configuration 578 userConfig.putAll(loadTrimmedProperties(nuxeoConf)); 579 onceGeneration = "once".equals(userConfig.getProperty(PARAM_FORCE_GENERATION)); 580 forceGeneration = onceGeneration 581 || Boolean.parseBoolean(userConfig.getProperty(PARAM_FORCE_GENERATION, "false")); 582 checkForDeprecatedParameters(userConfig); 583 584 // Synchronize directories between serverConfigurator and 585 // userConfig/defaultConfig 586 setDirectoryWithProperty(org.nuxeo.common.Environment.NUXEO_DATA_DIR); 587 setDirectoryWithProperty(org.nuxeo.common.Environment.NUXEO_LOG_DIR); 588 setDirectoryWithProperty(org.nuxeo.common.Environment.NUXEO_PID_DIR); 589 setDirectoryWithProperty(org.nuxeo.common.Environment.NUXEO_TMP_DIR); 590 setDirectoryWithProperty(org.nuxeo.common.Environment.NUXEO_MP_DIR); 591 } catch (NullPointerException e) { 592 throw new ConfigurationException("Missing file", e); 593 } catch (FileNotFoundException e) { 594 throw new ConfigurationException("Missing file: " + nuxeoDefaultConf + " or " + nuxeoConf, e); 595 } catch (IOException e) { 596 throw new ConfigurationException("Error reading " + nuxeoConf, e); 597 } 598 599 // Override default configuration with specific configuration(s) of 600 // the chosen template(s) which can be outside of server filesystem 601 try { 602 includeTemplates(); 603 checkForDeprecatedParameters(defaultConfig); 604 extractDatabaseTemplateName(); 605 extractSecondaryDatabaseTemplateName(); 606 } catch (FileNotFoundException e) { 607 throw new ConfigurationException("Missing file", e); 608 } catch (IOException e) { 609 throw new ConfigurationException("Error reading " + nuxeoConf, e); 610 } 611 612 Map<String, String> newParametersToSave = evalDynamicProperties(); 613 if (save && newParametersToSave != null && !newParametersToSave.isEmpty()) { 614 saveConfiguration(newParametersToSave, false, false); 615 } 616 617 logDebugInformation(); 618 } 619 620 /** 621 * @since 5.7 622 */ 623 protected void includeTemplates() throws IOException { 624 includedTemplates.clear(); 625 List<File> orderedTemplates = includeTemplates(getUserTemplates()); 626 includedTemplates.clear(); 627 includedTemplates.addAll(orderedTemplates); 628 log.debug(includedTemplates); 629 } 630 631 private void logDebugInformation() { 632 String debugPropValue = userConfig.getProperty(NUXEO_DEV_SYSTEM_PROP); 633 if (Boolean.parseBoolean(debugPropValue)) { 634 log.debug("Nuxeo Dev mode enabled"); 635 } else { 636 log.debug("Nuxeo Dev mode is not enabled"); 637 } 638 639 // XXX: cannot init seam debug mode when global debug mode is set, as 640 // it needs to be activated at startup, and requires the seam-debug jar 641 // to be in the classpath anyway 642 String seamDebugPropValue = userConfig.getProperty(SEAM_DEBUG_SYSTEM_PROP); 643 if (Boolean.parseBoolean(seamDebugPropValue)) { 644 log.debug("Nuxeo Seam HotReload is enabled"); 645 } else { 646 log.debug("Nuxeo Seam HotReload is not enabled"); 647 } 648 } 649 650 /** 651 * Generate properties which values are based on others 652 * 653 * @return Map with new parameters to save in {@code nuxeoConf} 654 * @since 5.5 655 */ 656 protected HashMap<String, String> evalDynamicProperties() throws ConfigurationException { 657 HashMap<String, String> newParametersToSave = new HashMap<>(); 658 evalEnvironmentVariables(newParametersToSave); 659 evalLoopbackURL(); 660 evalServerStatusKey(newParametersToSave); 661 return newParametersToSave; 662 } 663 664 /** 665 * Expand environment variable for properties values of the form ${env:MY_VAR}. 666 * 667 * @since 9.1 668 */ 669 protected void evalEnvironmentVariables(Map<String, String> newParametersToSave) { 670 for (Object keyObject : userConfig.keySet()) { 671 String key = (String) keyObject; 672 String value = userConfig.getProperty(key); 673 674 if (StringUtils.isNotBlank(value)) { 675 String newValue = replaceEnvironmentVariables(value); 676 if (!value.equals(newValue)) { 677 newParametersToSave.put(key, newValue); 678 } 679 } 680 } 681 } 682 683 private String replaceEnvironmentVariables(String value) { 684 if (StringUtils.isBlank(value)) { 685 return value; 686 } 687 688 Matcher matcher = ENV_VALUE_PATTERN.matcher(value); 689 StringBuffer sb = new StringBuffer(); 690 while (matcher.find()) { 691 boolean booleanValue = "??".equals(matcher.group("boolean")); 692 String envVarName = matcher.group("envparam"); 693 String defaultValue = matcher.group("defaultvalue"); 694 695 String envValue = getEnvironmentVariableValue(envVarName); 696 697 String result; 698 if (booleanValue) { 699 result = StringUtils.isBlank(envValue) ? "false" : "true"; 700 } else { 701 result = StringUtils.isBlank(envValue) ? defaultValue : envValue; 702 } 703 matcher.appendReplacement(sb, result); 704 } 705 matcher.appendTail(sb); 706 707 return sb.toString(); 708 709 } 710 711 /** 712 * Generate a server status key if not already set 713 * 714 * @see Environment#SERVER_STATUS_KEY 715 * @since 5.5 716 */ 717 private void evalServerStatusKey(Map<String, String> newParametersToSave) throws ConfigurationException { 718 if (userConfig.getProperty(Environment.SERVER_STATUS_KEY) == null) { 719 newParametersToSave.put(Environment.SERVER_STATUS_KEY, UUID.randomUUID().toString().substring(0, 8)); 720 } 721 } 722 723 private void evalLoopbackURL() throws ConfigurationException { 724 String loopbackURL = userConfig.getProperty(PARAM_LOOPBACK_URL); 725 if (loopbackURL != null) { 726 log.debug("Using configured loop back url: " + loopbackURL); 727 return; 728 } 729 InetAddress bindAddress = getBindAddress(); 730 // Address and ports already checked by #checkAddressesAndPorts 731 try { 732 if (bindAddress.isAnyLocalAddress()) { 733 boolean preferIPv6 = "false".equals(System.getProperty("java.net.preferIPv4Stack")) 734 && "true".equals(System.getProperty("java.net.preferIPv6Addresses")); 735 bindAddress = preferIPv6 ? InetAddress.getByName("::1") : InetAddress.getByName("127.0.0.1"); 736 log.debug("Bind address is \"ANY\", using local address instead: " + bindAddress); 737 } 738 } catch (UnknownHostException e) { 739 log.debug(e, e); 740 log.error(e.getMessage()); 741 } 742 743 String httpPort = userConfig.getProperty(PARAM_HTTP_PORT); 744 String contextPath = userConfig.getProperty(PARAM_CONTEXT_PATH); 745 // Is IPv6 or IPv4 ? 746 if (bindAddress instanceof Inet6Address) { 747 loopbackURL = "http://[" + bindAddress.getHostAddress() + "]:" + httpPort + contextPath; 748 } else { 749 loopbackURL = "http://" + bindAddress.getHostAddress() + ":" + httpPort + contextPath; 750 } 751 log.debug("Set as loop back URL: " + loopbackURL); 752 defaultConfig.setProperty(PARAM_LOOPBACK_URL, loopbackURL); 753 } 754 755 /** 756 * Read nuxeo.conf, replace backslashes in paths and write new nuxeo.conf 757 * 758 * @throws ConfigurationException if any error reading or writing nuxeo.conf 759 * @since 5.4.1 760 */ 761 protected void replaceBackslashes() throws ConfigurationException { 762 StringBuilder sb = new StringBuilder(); 763 try (BufferedReader reader = new BufferedReader(new FileReader(nuxeoConf))) { 764 String line; 765 while ((line = reader.readLine()) != null) { 766 if (line.matches(".*:\\\\.*")) { 767 line = line.replaceAll("\\\\", "/"); 768 } 769 sb.append(line).append(System.getProperty("line.separator")); 770 } 771 } catch (IOException e) { 772 throw new ConfigurationException("Error reading " + nuxeoConf, e); 773 } 774 try (FileWriter writer = new FileWriter(nuxeoConf, false)) { 775 // Copy back file content 776 writer.append(sb.toString()); 777 } catch (IOException e) { 778 throw new ConfigurationException("Error writing in " + nuxeoConf, e); 779 } 780 } 781 782 /** 783 * @since 5.4.2 784 * @param key Directory system key 785 * @see Environment 786 */ 787 public void setDirectoryWithProperty(String key) { 788 String directory = userConfig.getProperty(key); 789 if (directory == null) { 790 defaultConfig.setProperty(key, serverConfigurator.getDirectory(key).getPath()); 791 } else { 792 serverConfigurator.setDirectory(key, directory); 793 } 794 } 795 796 public String getUserTemplates() { 797 if (templates == null) { 798 templates = userConfig.getProperty(PARAM_TEMPLATES_NAME); 799 } 800 if (templates == null) { 801 log.warn("No template found in configuration! Fallback on 'default'."); 802 templates = "default"; 803 } 804 templates = replaceEnvironmentVariables(templates); 805 userConfig.setProperty(PARAM_TEMPLATES_NAME, templates); 806 return templates; 807 } 808 809 protected void generateFiles() throws ConfigurationException { 810 try { 811 serverConfigurator.parseAndCopy(userConfig); 812 serverConfigurator.dumpProperties(userConfig); 813 log.info("Configuration files generated."); 814 // keep true or false, switch once to false 815 if (onceGeneration) { 816 setOnceToFalse = true; 817 writeConfiguration(); 818 } 819 } catch (FileNotFoundException e) { 820 throw new ConfigurationException("Missing file: " + e.getMessage(), e); 821 } catch (TemplateException | ParseException e) { 822 throw new ConfigurationException("Could not process FreeMarker template: " + e.getMessage(), e); 823 } catch (IOException e) { 824 throw new ConfigurationException("Configuration failure: " + e.getMessage(), e); 825 } 826 } 827 828 private List<File> includeTemplates(String templatesList) throws IOException { 829 List<File> orderedTemplates = new ArrayList<>(); 830 StringTokenizer st = new StringTokenizer(templatesList, TEMPLATE_SEPARATOR); 831 while (st.hasMoreTokens()) { 832 String nextToken = replaceEnvironmentVariables(st.nextToken()); 833 File chosenTemplate = new File(nextToken); 834 // is it absolute and existing or relative path ? 835 if (!chosenTemplate.exists() || !chosenTemplate.getPath().equals(chosenTemplate.getAbsolutePath())) { 836 chosenTemplate = new File(nuxeoDefaultConf.getParentFile(), nextToken); 837 } 838 if (includedTemplates.contains(chosenTemplate)) { 839 log.debug("Already included " + nextToken); 840 continue; 841 } 842 if (!chosenTemplate.exists()) { 843 log.error(String.format( 844 "Template '%s' not found with relative or absolute path (%s). " 845 + "Check your %s parameter, and %s for included files.", 846 nextToken, chosenTemplate, PARAM_TEMPLATES_NAME, PARAM_INCLUDED_TEMPLATES)); 847 continue; 848 } 849 File chosenTemplateConf = new File(chosenTemplate, NUXEO_DEFAULT_CONF); 850 includedTemplates.add(chosenTemplate); 851 if (!chosenTemplateConf.exists()) { 852 log.warn("Ignore template (no default configuration): " + nextToken); 853 continue; 854 } 855 856 Properties subTemplateConf = loadTrimmedProperties(chosenTemplateConf); 857 String subTemplatesList = replaceEnvironmentVariables( 858 subTemplateConf.getProperty(PARAM_INCLUDED_TEMPLATES)); 859 if (subTemplatesList != null && subTemplatesList.length() > 0) { 860 orderedTemplates.addAll(includeTemplates(subTemplatesList)); 861 } 862 // Load configuration from chosen templates 863 defaultConfig.putAll(subTemplateConf); 864 orderedTemplates.add(chosenTemplate); 865 String templateInfo = "Include template: " + chosenTemplate.getPath(); 866 if (quiet) { 867 log.debug(templateInfo); 868 } else { 869 log.info(templateInfo); 870 } 871 } 872 return orderedTemplates; 873 } 874 875 /** 876 * Check for deprecated parameters 877 * 878 * @since 5.6 879 */ 880 protected void checkForDeprecatedParameters(Properties properties) { 881 serverConfigurator.addServerSpecificParameters(parametersMigration); 882 @SuppressWarnings("rawtypes") 883 Enumeration userEnum = properties.propertyNames(); 884 while (userEnum.hasMoreElements()) { 885 String key = (String) userEnum.nextElement(); 886 if (parametersMigration.containsKey(key)) { 887 String value = properties.getProperty(key); 888 properties.setProperty(parametersMigration.get(key), value); 889 // Don't remove the deprecated key yet - more 890 // warnings but old things should keep working 891 // properties.remove(key); 892 if (!hideDeprecationWarnings) { 893 log.warn("Parameter " + key + " is deprecated - please use " + parametersMigration.get(key) 894 + " instead"); 895 } 896 } 897 } 898 } 899 900 public File getNuxeoHome() { 901 return nuxeoHome; 902 } 903 904 public File getNuxeoDefaultConf() { 905 return nuxeoDefaultConf; 906 } 907 908 public List<File> getIncludedTemplates() { 909 return includedTemplates; 910 } 911 912 /** 913 * Save changed parameters in {@code nuxeo.conf}. This method does not check values in map. Use 914 * {@link #saveFilteredConfiguration(Map)} for parameters filtering. 915 * 916 * @param changedParameters Map of modified parameters 917 * @see #saveFilteredConfiguration(Map) 918 */ 919 public void saveConfiguration(Map<String, String> changedParameters) throws ConfigurationException { 920 // Keep generation true or once; switch false to once 921 saveConfiguration(changedParameters, false, true); 922 } 923 924 /** 925 * Save changed parameters in {@code nuxeo.conf} calculating templates if changedParameters contains a value for 926 * {@link #PARAM_TEMPLATE_DBNAME}. If a parameter value is empty ("" or null), then the property is unset. 927 * {@link #PARAM_WIZARD_DONE}, {@link #PARAM_TEMPLATES_NAME} and {@link #PARAM_FORCE_GENERATION} cannot be unset, 928 * but their value can be changed.<br/> 929 * This method does not check values in map: use {@link #saveFilteredConfiguration(Map)} for parameters filtering. 930 * 931 * @param changedParameters Map of modified parameters 932 * @param setGenerationOnceToFalse If generation was on (true or once), then set it to false or not? 933 * @param setGenerationFalseToOnce If generation was off (false), then set it to once? 934 * @see #saveFilteredConfiguration(Map) 935 * @since 5.5 936 */ 937 public void saveConfiguration(Map<String, String> changedParameters, boolean setGenerationOnceToFalse, 938 boolean setGenerationFalseToOnce) throws ConfigurationException { 939 setOnceToFalse = setGenerationOnceToFalse; 940 setFalseToOnce = setGenerationFalseToOnce; 941 updateStoredConfig(); 942 String newDbTemplate = changedParameters.remove(PARAM_TEMPLATE_DBNAME); 943 if (newDbTemplate != null) { 944 changedParameters.put(PARAM_TEMPLATES_NAME, rebuildTemplatesStr(newDbTemplate)); 945 } 946 newDbTemplate = changedParameters.remove(PARAM_TEMPLATE_DBSECONDARY_NAME); 947 if (newDbTemplate != null) { 948 changedParameters.put(PARAM_TEMPLATES_NAME, rebuildTemplatesStr(newDbTemplate)); 949 } 950 if (changedParameters.containsValue(null) || changedParameters.containsValue("")) { 951 // There are properties to unset 952 Set<String> propertiesToUnset = new HashSet<>(); 953 for (Entry<String, String> entry : changedParameters.entrySet()) { 954 if (StringUtils.isEmpty(entry.getValue())) { 955 propertiesToUnset.add(entry.getKey()); 956 } 957 } 958 for (String key : propertiesToUnset) { 959 changedParameters.remove(key); 960 userConfig.remove(key); 961 } 962 } 963 userConfig.putAll(changedParameters); 964 writeConfiguration(); 965 updateStoredConfig(); 966 } 967 968 private void updateStoredConfig() { 969 if (storedConfig == null) { 970 storedConfig = new Properties(defaultConfig); 971 } else { 972 storedConfig.clear(); 973 } 974 storedConfig.putAll(userConfig); 975 } 976 977 /** 978 * Save changed parameters in {@code nuxeo.conf}, filtering parameters with {@link #getChangedParameters(Map)} 979 * 980 * @param changedParameters Maps of modified parameters 981 * @since 5.4.2 982 * @see #saveConfiguration(Map) 983 * @see #getChangedParameters(Map) 984 */ 985 public void saveFilteredConfiguration(Map<String, String> changedParameters) throws ConfigurationException { 986 Map<String, String> filteredParameters = getChangedParameters(changedParameters); 987 saveConfiguration(filteredParameters); 988 } 989 990 /** 991 * Filters given parameters including them only if (there was no previous value and new value is not empty/null) or 992 * (there was a previous value and it differs from the new value) 993 * 994 * @param changedParameters parameters to be filtered 995 * @return filtered map 996 * @since 5.4.2 997 */ 998 public Map<String, String> getChangedParameters(Map<String, String> changedParameters) { 999 Map<String, String> filteredChangedParameters = new HashMap<>(); 1000 for (String key : changedParameters.keySet()) { 1001 String oldParam = getStoredConfig().getProperty(key); 1002 String newParam = changedParameters.get(key); 1003 if (newParam != null) { 1004 newParam = newParam.trim(); 1005 } 1006 if (oldParam == null && StringUtils.isNotEmpty(newParam) 1007 || oldParam != null && !oldParam.trim().equals(newParam)) { 1008 filteredChangedParameters.put(key, newParam); 1009 } 1010 } 1011 return filteredChangedParameters; 1012 } 1013 1014 private void writeConfiguration() throws ConfigurationException { 1015 final MessageDigest newContentDigest = DigestUtils.getMd5Digest(); 1016 StringWriter newContent = new StringWriter() { 1017 @Override 1018 public void write(String str) { 1019 if (str != null) { 1020 newContentDigest.update(str.getBytes()); 1021 } 1022 super.write(str); 1023 } 1024 }; 1025 // Copy back file content 1026 newContent.append(readConfiguration()); 1027 // Write changed parameters 1028 newContent.write(BOUNDARY_BEGIN + System.getProperty("line.separator")); 1029 for (Object o : new TreeSet<>(userConfig.keySet())) { 1030 String key = (String) o; 1031 // Ignore parameters already stored in newContent 1032 if (PARAM_FORCE_GENERATION.equals(key) || PARAM_WIZARD_DONE.equals(key) 1033 || PARAM_TEMPLATES_NAME.equals(key)) { 1034 continue; 1035 } 1036 String oldValue = storedConfig.getProperty(key, ""); 1037 String newValue = userConfig.getRawProperty(key, ""); 1038 if (!newValue.equals(oldValue)) { 1039 newContent.write("#" + key + "=" + oldValue + System.getProperty("line.separator")); 1040 newContent.write(key + "=" + newValue + System.getProperty("line.separator")); 1041 } 1042 } 1043 newContent.write(BOUNDARY_END + System.getProperty("line.separator")); 1044 1045 // Write file only if content has changed 1046 if (!Hex.encodeHexString(newContentDigest.digest()).equals(currentConfigurationDigest)) { 1047 try (Writer writer = new FileWriter(nuxeoConf, false)) { 1048 writer.append(newContent.getBuffer()); 1049 } catch (IOException e) { 1050 throw new ConfigurationException("Error writing in " + nuxeoConf, e); 1051 } 1052 } 1053 } 1054 1055 private StringBuffer readConfiguration() throws ConfigurationException { 1056 // Will change wizardParam value instead of appending it 1057 String wizardParam = userConfig.getProperty(PARAM_WIZARD_DONE); 1058 1059 // Will change templatesParam value instead of appending it 1060 String templatesParam = userConfig.getProperty(PARAM_TEMPLATES_NAME); 1061 Integer generationIndex = null, wizardIndex = null, templatesIndex = null; 1062 List<String> newLines = new ArrayList<>(); 1063 try (BufferedReader reader = new BufferedReader(new FileReader(nuxeoConf))) { 1064 String line; 1065 MessageDigest digest = DigestUtils.getMd5Digest(); 1066 boolean onConfiguratorContent = false; 1067 while ((line = reader.readLine()) != null) { 1068 digest.update(line.getBytes()); 1069 if (!onConfiguratorContent) { 1070 if (!line.startsWith(BOUNDARY_BEGIN)) { 1071 if (line.startsWith(PARAM_FORCE_GENERATION)) { 1072 if (setOnceToFalse && onceGeneration) { 1073 line = PARAM_FORCE_GENERATION + "=false"; 1074 } 1075 if (setFalseToOnce && !forceGeneration) { 1076 line = PARAM_FORCE_GENERATION + "=once"; 1077 } 1078 if (generationIndex == null) { 1079 newLines.add(line); 1080 generationIndex = newLines.size() - 1; 1081 } else { 1082 newLines.set(generationIndex, line); 1083 } 1084 } else if (line.startsWith(PARAM_WIZARD_DONE)) { 1085 if (wizardParam != null) { 1086 line = PARAM_WIZARD_DONE + "=" + wizardParam; 1087 } 1088 if (wizardIndex == null) { 1089 newLines.add(line); 1090 wizardIndex = newLines.size() - 1; 1091 } else { 1092 newLines.set(wizardIndex, line); 1093 } 1094 } else if (line.startsWith(PARAM_TEMPLATES_NAME)) { 1095 if (templatesParam != null) { 1096 line = PARAM_TEMPLATES_NAME + "=" + templatesParam; 1097 } 1098 if (templatesIndex == null) { 1099 newLines.add(line); 1100 templatesIndex = newLines.size() - 1; 1101 } else { 1102 newLines.set(templatesIndex, line); 1103 } 1104 } else { 1105 int equalIdx = line.indexOf("="); 1106 if (equalIdx < 1 || line.trim().startsWith("#")) { 1107 newLines.add(line); 1108 } else { 1109 String key = line.substring(0, equalIdx).trim(); 1110 if (userConfig.getProperty(key) != null) { 1111 newLines.add(line); 1112 } else { 1113 newLines.add("#" + line); 1114 } 1115 } 1116 } 1117 } else { 1118 // What must be written just before the BOUNDARY_BEGIN 1119 if (templatesIndex == null && templatesParam != null) { 1120 newLines.add(PARAM_TEMPLATES_NAME + "=" + templatesParam); 1121 templatesIndex = newLines.size() - 1; 1122 } 1123 if (wizardIndex == null && wizardParam != null) { 1124 newLines.add(PARAM_WIZARD_DONE + "=" + wizardParam); 1125 wizardIndex = newLines.size() - 1; 1126 } 1127 onConfiguratorContent = true; 1128 } 1129 } else { 1130 if (!line.startsWith(BOUNDARY_END)) { 1131 int equalIdx = line.indexOf("="); 1132 if (line.startsWith("#" + PARAM_TEMPLATES_NAME) || line.startsWith(PARAM_TEMPLATES_NAME)) { 1133 // Backward compliance, it must be ignored 1134 continue; 1135 } 1136 if (equalIdx < 1) { // Ignore non-readable lines 1137 continue; 1138 } 1139 if (line.trim().startsWith("#")) { 1140 String key = line.substring(1, equalIdx).trim(); 1141 String value = line.substring(equalIdx + 1).trim(); 1142 getStoredConfig().setProperty(key, value); 1143 } else { 1144 String key = line.substring(0, equalIdx).trim(); 1145 String value = line.substring(equalIdx + 1).trim(); 1146 if (!value.equals(userConfig.getRawProperty(key))) { 1147 getStoredConfig().setProperty(key, value); 1148 } 1149 } 1150 } else { 1151 onConfiguratorContent = false; 1152 } 1153 } 1154 } 1155 reader.close(); 1156 currentConfigurationDigest = Hex.encodeHexString(digest.digest()); 1157 } catch (IOException e) { 1158 throw new ConfigurationException("Error reading " + nuxeoConf, e); 1159 } 1160 StringBuffer newContent = new StringBuffer(); 1161 for (String newLine : newLines) { 1162 newContent.append(newLine.trim()).append(System.getProperty("line.separator")); 1163 } 1164 return newContent; 1165 } 1166 1167 /** 1168 * Extract a database template from the current list of templates. Return the last one if there are multiples. 1169 * 1170 * @see #rebuildTemplatesStr(String) 1171 */ 1172 public String extractDatabaseTemplateName() { 1173 return extractDbTemplateName(DB_LIST, PARAM_TEMPLATE_DBTYPE, PARAM_TEMPLATE_DBNAME, "unknown"); 1174 } 1175 1176 /** 1177 * Extract a NoSQL database template from the current list of templates. Return the last one if there are multiples. 1178 * 1179 * @see #rebuildTemplatesStr(String) 1180 * @since 8.1 1181 */ 1182 public String extractSecondaryDatabaseTemplateName() { 1183 return extractDbTemplateName(DB_SECONDARY_LIST, PARAM_TEMPLATE_DBSECONDARY_TYPE, PARAM_TEMPLATE_DBSECONDARY_NAME, null); 1184 } 1185 1186 private String extractDbTemplateName(List<String> knownDbList, String paramTemplateDbType, 1187 String paramTemplateDbName, String defaultTemplate) { 1188 String dbTemplate = defaultTemplate; 1189 boolean found = false; 1190 for (File templateFile : includedTemplates) { 1191 String template = templateFile.getName(); 1192 if (knownDbList.contains(template)) { 1193 dbTemplate = template; 1194 found = true; 1195 } 1196 } 1197 String dbType = userConfig.getProperty(paramTemplateDbType); 1198 if (!found && dbType != null) { 1199 log.warn(String.format("Didn't find a known database template in the list but " 1200 + "some template contributed a value for %s.", paramTemplateDbType)); 1201 dbTemplate = dbType; 1202 } 1203 if (dbTemplate != null && !dbTemplate.equals(dbType)) { 1204 if (dbType == null) { 1205 log.warn(String.format("Missing value for %s, using %s", paramTemplateDbType, dbTemplate)); 1206 userConfig.setProperty(paramTemplateDbType, dbTemplate); 1207 } else { 1208 log.debug(String.format("Different values between %s (%s) and %s (%s)", paramTemplateDbName, dbTemplate, 1209 paramTemplateDbType, dbType)); 1210 } 1211 } 1212 if (dbTemplate == null) { 1213 defaultConfig.remove(paramTemplateDbName); 1214 } else { 1215 defaultConfig.setProperty(paramTemplateDbName, dbTemplate); 1216 } 1217 return dbTemplate; 1218 } 1219 1220 /** 1221 * @return nuxeo.conf file used 1222 */ 1223 public File getNuxeoConf() { 1224 return nuxeoConf; 1225 } 1226 1227 /** 1228 * Delegate logs initialization to serverConfigurator instance 1229 * 1230 * @since 5.4.2 1231 */ 1232 public void initLogs() { 1233 serverConfigurator.initLogs(); 1234 } 1235 1236 /** 1237 * @return log directory 1238 * @since 5.4.2 1239 */ 1240 public File getLogDir() { 1241 return serverConfigurator.getLogDir(); 1242 } 1243 1244 /** 1245 * @return pid directory 1246 * @since 5.4.2 1247 */ 1248 public File getPidDir() { 1249 return serverConfigurator.getPidDir(); 1250 } 1251 1252 /** 1253 * @return Data directory 1254 * @since 5.4.2 1255 */ 1256 public File getDataDir() { 1257 return serverConfigurator.getDataDir(); 1258 } 1259 1260 /** 1261 * Create needed directories. Check existence of old paths. If old paths have been found and they cannot be upgraded 1262 * automatically, then upgrading message is logged and error thrown. 1263 * 1264 * @throws ConfigurationException If a deprecated directory has been detected. 1265 * @since 5.4.2 1266 * @see ServerConfigurator#verifyInstallation() 1267 */ 1268 public void verifyInstallation() throws ConfigurationException { 1269 checkJavaVersion(); 1270 ifNotExistsAndIsDirectoryThenCreate(getLogDir()); 1271 ifNotExistsAndIsDirectoryThenCreate(getPidDir()); 1272 ifNotExistsAndIsDirectoryThenCreate(getDataDir()); 1273 ifNotExistsAndIsDirectoryThenCreate(getTmpDir()); 1274 ifNotExistsAndIsDirectoryThenCreate(getPackagesDir()); 1275 checkAddressesAndPorts(); 1276 serverConfigurator.verifyInstallation(); 1277 backingServicesConfigurator.verifyInstallation(); 1278 1279 } 1280 1281 /** 1282 * @return Marketplace packages directory 1283 * @since 5.9.4 1284 */ 1285 private File getPackagesDir() { 1286 return serverConfigurator.getPackagesDir(); 1287 } 1288 1289 /** 1290 * Check that the process is executed with a supported Java version. See 1291 * <a href="http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html">J2SE SDK/JRE Version String 1292 * Naming Convention</a> 1293 * 1294 * @since 5.6 1295 */ 1296 public void checkJavaVersion() throws ConfigurationException { 1297 String version = System.getProperty("java.version"); 1298 checkJavaVersion(version, COMPLIANT_JAVA_VERSIONS); 1299 } 1300 1301 /** 1302 * Check the java version compared to compliant ones. 1303 * 1304 * @param version the java version 1305 * @param compliantVersions the compliant java versions 1306 * @since 9.1 1307 */ 1308 protected static void checkJavaVersion(String version, String[] compliantVersions) throws ConfigurationException { 1309 // compliantVersions represents the java versions on which Nuxeo runs perfectly, so: 1310 // - if we run Nuxeo with a major java version present in compliantVersions and compatible with then this 1311 // method exits without error and without logging a warn message about loose compliance 1312 // - if we run Nuxeo with a major java version not present in compliantVersions but greater than once then 1313 // this method exits without error and logs a warn message about loose compliance 1314 // - if we run Nuxeo with a non valid java version then method exits with error 1315 // - if we run Nuxeo with a non valid java version and with jvmcheck=nofail property then method exits without 1316 // error and logs a warn message about loose compliance 1317 1318 // try to retrieve the closest compliant java version 1319 String lastCompliantVersion = null; 1320 for (String compliantVersion : compliantVersions) { 1321 if (checkJavaVersion(version, compliantVersion, false, false)) { 1322 // current compliant version is valid, go to next one 1323 lastCompliantVersion = compliantVersion; 1324 } else if (lastCompliantVersion != null) { 1325 // current compliant version is not valid, but we found a valid one earlier, 1st case 1326 return; 1327 } else if (checkJavaVersion(version, compliantVersion, true, true)) { 1328 // current compliant version is not valid, try to check java version with jvmcheck=nofail, 4th case 1329 // here we will log about loose compliance for the lower compliant java version 1330 return; 1331 } 1332 } 1333 // we might have lastCompliantVersion, unless nothing is valid against the current java version 1334 if (lastCompliantVersion != null) { 1335 // 2nd case: log about loose compliance if current major java version is greater than the greatest 1336 // compliant java version 1337 checkJavaVersion(version, lastCompliantVersion, false, true); 1338 return; 1339 } 1340 1341 // 3th case 1342 String message = String.format("Nuxeo requires Java %s (detected %s).", ArrayUtils.toString(compliantVersions), 1343 version); 1344 throw new ConfigurationException(message + " See '" + JVMCHECK_PROP + "' option to bypass version check."); 1345 } 1346 1347 /** 1348 * Checks the java version compared to the required one. 1349 * <p> 1350 * Loose compliance is assumed if the major version is greater than the required major version or a jvmcheck=nofail 1351 * flag is set. 1352 * 1353 * @param version the java version 1354 * @param requiredVersion the required java version 1355 * @param allowNoFailFlag if {@code true} then check jvmcheck=nofail flag to always have loose compliance 1356 * @param warnIfLooseCompliance if {@code true} then log a WARN if the is loose compliance 1357 * @return true if the java version is compliant (maybe loosely) with the required version 1358 * @since 8.4 1359 */ 1360 protected static boolean checkJavaVersion(String version, String requiredVersion, boolean allowNoFailFlag, 1361 boolean warnIfLooseCompliance) { 1362 allowNoFailFlag = allowNoFailFlag 1363 && JVMCHECK_NOFAIL.equalsIgnoreCase(System.getProperty(JVMCHECK_PROP, JVMCHECK_FAIL)); 1364 try { 1365 JVMVersion required = JVMVersion.parse(requiredVersion); 1366 JVMVersion actual = JVMVersion.parse(version); 1367 boolean compliant = actual.compareTo(required) >= 0; 1368 if (compliant && actual.compareTo(required, UpTo.MAJOR) == 0) { 1369 return true; 1370 } 1371 if (!compliant && !allowNoFailFlag) { 1372 return false; 1373 } 1374 // greater major version or noFail is present in system property, considered loosely compliant but may warn 1375 if (warnIfLooseCompliance) { 1376 log.warn(String.format("Nuxeo requires Java %s+ (detected %s).", requiredVersion, version)); 1377 } 1378 return true; 1379 } catch (java.text.ParseException cause) { 1380 if (allowNoFailFlag) { 1381 log.warn("Cannot check java version", cause); 1382 return true; 1383 } 1384 throw new IllegalArgumentException("Cannot check java version", cause); 1385 } 1386 } 1387 1388 /** 1389 * Checks the java version compared to the required one. 1390 * <p> 1391 * If major version is same as required major version and minor is greater or equal, it is compliant. 1392 * <p> 1393 * If major version is greater than required major version, it is compliant. 1394 * 1395 * @param version the java version 1396 * @param requiredVersion the required java version 1397 * @return true if the java version is compliant with the required version 1398 * @since 8.4 1399 */ 1400 public static boolean checkJavaVersion(String version, String requiredVersion) { 1401 return checkJavaVersion(version, requiredVersion, false, false); 1402 } 1403 1404 /** 1405 * Will check the configured addresses are reachable and Nuxeo required ports are available on those addresses. 1406 * Server specific implementations should override this method in order to check for server specific ports. 1407 * {@link #PARAM_BIND_ADDRESS} must be set before. 1408 * 1409 * @since 5.5 1410 * @see ServerConfigurator#verifyInstallation() 1411 */ 1412 public void checkAddressesAndPorts() throws ConfigurationException { 1413 InetAddress bindAddress = getBindAddress(); 1414 // Sanity check 1415 if (bindAddress.isMulticastAddress()) { 1416 throw new ConfigurationException("Multicast address won't work: " + bindAddress); 1417 } 1418 checkAddressReachable(bindAddress); 1419 checkPortAvailable(bindAddress, Integer.parseInt(userConfig.getProperty(PARAM_HTTP_PORT))); 1420 } 1421 1422 /** 1423 * Checks the userConfig bind address is not 0.0.0.0 and replaces it with 127.0.0.1 if needed 1424 * 1425 * @return the userConfig bind address if not 0.0.0.0 else 127.0.0.1 1426 * @since 5.7 1427 */ 1428 public InetAddress getBindAddress() throws ConfigurationException { 1429 return getBindAddress(userConfig.getProperty(PARAM_BIND_ADDRESS)); 1430 } 1431 1432 /** 1433 * Checks hostName bind address is not 0.0.0.0 and replaces it with 127.0.0.1 if needed 1434 * 1435 * @param hostName the hostname of Nuxeo server (works also with the IP) 1436 * @return the bind address matching hostName parameter if not 0.0.0.0 else 127.0.0.1 1437 * @since 9.2 1438 */ 1439 public static InetAddress getBindAddress(String hostName) throws ConfigurationException { 1440 InetAddress bindAddress; 1441 try { 1442 bindAddress = InetAddress.getByName(hostName); 1443 if (bindAddress.isAnyLocalAddress()) { 1444 boolean preferIPv6 = "false".equals(System.getProperty("java.net.preferIPv4Stack")) 1445 && "true".equals(System.getProperty("java.net.preferIPv6Addresses")); 1446 bindAddress = preferIPv6 ? InetAddress.getByName("::1") : InetAddress.getByName("127.0.0.1"); 1447 log.debug("Bind address is \"ANY\", using local address instead: " + bindAddress); 1448 } 1449 log.debug("Configured bind address: " + bindAddress); 1450 } catch (UnknownHostException e) { 1451 throw new ConfigurationException(e); 1452 } 1453 return bindAddress; 1454 } 1455 1456 /** 1457 * @param address address to check for availability 1458 * @since 5.5 1459 */ 1460 public static void checkAddressReachable(InetAddress address) throws ConfigurationException { 1461 try { 1462 log.debug("Checking availability of " + address); 1463 address.isReachable(ADDRESS_PING_TIMEOUT); 1464 } catch (IOException e) { 1465 throw new ConfigurationException("Unreachable bind address " + address); 1466 } 1467 } 1468 1469 /** 1470 * Checks if port is available on given address. 1471 * 1472 * @param port port to check for availability 1473 * @throws ConfigurationException Throws an exception if address is unavailable. 1474 * @since 5.5 1475 */ 1476 public static void checkPortAvailable(InetAddress address, int port) throws ConfigurationException { 1477 if ((port == 0) || (port == -1)) { 1478 log.warn("Port is set to " + Integer.toString(port) 1479 + " - assuming it is disabled - skipping availability check"); 1480 return; 1481 } 1482 if (port < MIN_PORT || port > MAX_PORT) { 1483 throw new IllegalArgumentException("Invalid port: " + port); 1484 } 1485 ServerSocket socketTCP = null; 1486 // DatagramSocket socketUDP = null; 1487 try { 1488 log.debug("Checking availability of port " + port + " on address " + address); 1489 socketTCP = new ServerSocket(port, 0, address); 1490 socketTCP.setReuseAddress(true); 1491 // socketUDP = new DatagramSocket(port, address); 1492 // socketUDP.setReuseAddress(true); 1493 // return true; 1494 } catch (IOException e) { 1495 throw new ConfigurationException(e.getMessage() + ": " + address + ":" + port, e); 1496 } finally { 1497 // if (socketUDP != null) { 1498 // socketUDP.close(); 1499 // } 1500 if (socketTCP != null) { 1501 try { 1502 socketTCP.close(); 1503 } catch (IOException e) { 1504 // Do not throw 1505 } 1506 } 1507 } 1508 } 1509 1510 /** 1511 * @return Temporary directory 1512 */ 1513 public File getTmpDir() { 1514 return serverConfigurator.getTmpDir(); 1515 } 1516 1517 private void ifNotExistsAndIsDirectoryThenCreate(File directory) { 1518 if (!directory.isDirectory()) { 1519 directory.mkdirs(); 1520 } 1521 } 1522 1523 /** 1524 * @return Log files produced by Log4J configuration without loading this configuration instead of current active 1525 * one. 1526 * @since 5.4.2 1527 */ 1528 public ArrayList<String> getLogFiles() { 1529 File log4jConfFile = serverConfigurator.getLogConfFile(); 1530 System.setProperty(org.nuxeo.common.Environment.NUXEO_LOG_DIR, getLogDir().getPath()); 1531 return Log4JHelper.getFileAppendersFiles(log4jConfFile); 1532 } 1533 1534 /** 1535 * Check if wizard must and can be ran 1536 * 1537 * @return true if configuration wizard is required before starting Nuxeo 1538 * @since 5.4.2 1539 */ 1540 public boolean isWizardRequired() { 1541 return !"true".equalsIgnoreCase(getUserConfig().getProperty(PARAM_WIZARD_DONE, "true")) 1542 && serverConfigurator.isWizardAvailable(); 1543 } 1544 1545 /** 1546 * Rebuild a templates string for use in nuxeo.conf 1547 * 1548 * @param dbTemplate database template to use instead of current one 1549 * @return new templates string using given dbTemplate 1550 * @since 5.4.2 1551 * @see #extractDatabaseTemplateName() 1552 * @see #changeDBTemplate(String) 1553 * @see #changeTemplates(String) 1554 */ 1555 public String rebuildTemplatesStr(String dbTemplate) { 1556 List<String> templatesList = new ArrayList<>(); 1557 templatesList.addAll(Arrays.asList(templates.split(TEMPLATE_SEPARATOR))); 1558 String currentDBTemplate = null; 1559 if (DB_LIST.contains(dbTemplate)) { 1560 currentDBTemplate = userConfig.getProperty(PARAM_TEMPLATE_DBNAME); 1561 if (currentDBTemplate == null) { 1562 currentDBTemplate = extractDatabaseTemplateName(); 1563 } 1564 } else if (DB_SECONDARY_LIST.contains(dbTemplate)) { 1565 currentDBTemplate = userConfig.getProperty(PARAM_TEMPLATE_DBSECONDARY_NAME); 1566 if (currentDBTemplate == null) { 1567 currentDBTemplate = extractSecondaryDatabaseTemplateName(); 1568 } 1569 if ("none".equals(dbTemplate)) { 1570 dbTemplate = null; 1571 } 1572 } 1573 int dbIdx = templatesList.indexOf(currentDBTemplate); 1574 if (dbIdx < 0) { 1575 if (dbTemplate == null) { 1576 return templates; 1577 } 1578 // current db template is implicit => set the new one 1579 templatesList.add(dbTemplate); 1580 } else if (dbTemplate == null) { 1581 // current db template is explicit => remove it 1582 templatesList.remove(dbIdx); 1583 } else { 1584 // current db template is explicit => replace it 1585 templatesList.set(dbIdx, dbTemplate); 1586 } 1587 return replaceEnvironmentVariables(String.join(TEMPLATE_SEPARATOR, templatesList)); 1588 } 1589 1590 /** 1591 * @return Nuxeo config directory 1592 * @since 5.4.2 1593 */ 1594 public File getConfigDir() { 1595 return serverConfigurator.getConfigDir(); 1596 } 1597 1598 /** 1599 * Ensure the server will start only wizard application, not Nuxeo 1600 * 1601 * @since 5.4.2 1602 */ 1603 public void prepareWizardStart() { 1604 serverConfigurator.prepareWizardStart(); 1605 } 1606 1607 /** 1608 * Ensure the wizard won't be started and nuxeo is ready for use 1609 * 1610 * @since 5.4.2 1611 */ 1612 public void cleanupPostWizard() { 1613 serverConfigurator.cleanupPostWizard(); 1614 } 1615 1616 /** 1617 * @return Nuxeo runtime home 1618 */ 1619 public File getRuntimeHome() { 1620 return serverConfigurator.getRuntimeHome(); 1621 } 1622 1623 /** 1624 * @since 5.4.2 1625 * @return true if there's an install in progress 1626 */ 1627 public boolean isInstallInProgress() { 1628 return getInstallFile().exists(); 1629 } 1630 1631 /** 1632 * @return File pointing to the directory containing the marketplace packages included in the distribution 1633 * @since 5.6 1634 */ 1635 public File getDistributionMPDir() { 1636 String mpDir = userConfig.getProperty(PARAM_MP_DIR, DISTRIBUTION_MP_DIR); 1637 return new File(getNuxeoHome(), mpDir); 1638 } 1639 1640 /** 1641 * @return Install/upgrade file 1642 * @since 5.4.1 1643 */ 1644 public File getInstallFile() { 1645 return new File(serverConfigurator.getDataDir(), INSTALL_AFTER_RESTART); 1646 } 1647 1648 /** 1649 * Add template(s) to the {@link #PARAM_TEMPLATES_NAME} list if not already present 1650 * 1651 * @param templatesToAdd Comma separated templates to add 1652 * @since 5.5 1653 */ 1654 public void addTemplate(String templatesToAdd) throws ConfigurationException { 1655 List<String> templatesList = getTemplateList(); 1656 List<String> templatesToAddList = Arrays.asList(templatesToAdd.split(TEMPLATE_SEPARATOR)); 1657 if (templatesList.addAll(templatesToAddList)) { 1658 String newTemplatesStr = String.join(TEMPLATE_SEPARATOR, templatesList); 1659 HashMap<String, String> parametersToSave = new HashMap<>(); 1660 parametersToSave.put(PARAM_TEMPLATES_NAME, newTemplatesStr); 1661 saveFilteredConfiguration(parametersToSave); 1662 changeTemplates(newTemplatesStr); 1663 } 1664 } 1665 1666 /** 1667 * Return the list of templates. 1668 * 1669 * @since 9.2 1670 */ 1671 public List<String> getTemplateList() { 1672 String currentTemplatesStr = userConfig.getProperty(PARAM_TEMPLATES_NAME); 1673 1674 return Stream.of(replaceEnvironmentVariables(currentTemplatesStr).split(TEMPLATE_SEPARATOR)) 1675 .collect(Collectors.toList()); 1676 1677 } 1678 1679 /** 1680 * Remove template(s) from the {@link #PARAM_TEMPLATES_NAME} list 1681 * 1682 * @param templatesToRm Comma separated templates to remove 1683 * @since 5.5 1684 */ 1685 public void rmTemplate(String templatesToRm) throws ConfigurationException { 1686 List<String> templatesList = getTemplateList(); 1687 List<String> templatesToRmList = Arrays.asList(templatesToRm.split(TEMPLATE_SEPARATOR)); 1688 if (templatesList.removeAll(templatesToRmList)) { 1689 String newTemplatesStr = String.join(TEMPLATE_SEPARATOR, templatesList); 1690 Map<String, String> parametersToSave = new HashMap<>(); 1691 parametersToSave.put(PARAM_TEMPLATES_NAME, newTemplatesStr); 1692 saveFilteredConfiguration(parametersToSave); 1693 changeTemplates(newTemplatesStr); 1694 } 1695 } 1696 1697 /** 1698 * Set a property in nuxeo configuration 1699 * 1700 * @return The old value 1701 * @since 5.5 1702 */ 1703 public String setProperty(String key, String value) throws ConfigurationException { 1704 String oldValue = getStoredConfig().getProperty(key); 1705 if (PARAM_TEMPLATES_NAME.equals(key)) { 1706 templates = StringUtils.isBlank(value) ? null : value; 1707 } 1708 HashMap<String, String> newParametersToSave = new HashMap<>(); 1709 newParametersToSave.put(key, value); 1710 saveFilteredConfiguration(newParametersToSave); 1711 setBasicConfiguration(); 1712 return oldValue; 1713 } 1714 1715 /** 1716 * Set properties in nuxeo configuration 1717 * 1718 * @return The old values 1719 * @since 7.4 1720 */ 1721 public Map<String, String> setProperties(Map<String, String> newParametersToSave) throws ConfigurationException { 1722 Map<String, String> oldValues = new HashMap<>(); 1723 for (String key : newParametersToSave.keySet()) { 1724 oldValues.put(key, getStoredConfig().getProperty(key)); 1725 if (PARAM_TEMPLATES_NAME.equals(key)) { 1726 String value = newParametersToSave.get(key); 1727 templates = StringUtils.isBlank(value) ? null : value; 1728 } 1729 } 1730 saveFilteredConfiguration(newParametersToSave); 1731 setBasicConfiguration(); 1732 return oldValues; 1733 } 1734 1735 /** 1736 * Set properties in the given template, if it exists 1737 * 1738 * @return The old values 1739 * @since 7.4 1740 */ 1741 public Map<String, String> setProperties(String template, Map<String, String> newParametersToSave) 1742 throws ConfigurationException, IOException { 1743 File templateConf = getTemplateConf(template); 1744 Properties templateProperties = loadTrimmedProperties(templateConf); 1745 Map<String, String> oldValues = new HashMap<>(); 1746 StringBuilder newContent = new StringBuilder(); 1747 try (BufferedReader reader = new BufferedReader(new FileReader(templateConf))) { 1748 String line = reader.readLine(); 1749 if (line != null && line.startsWith("## DO NOT EDIT THIS FILE")) { 1750 throw new ConfigurationException("The template states in its header that it must not be modified."); 1751 } 1752 while (line != null) { 1753 int equalIdx = line.indexOf("="); 1754 if (equalIdx < 1 || line.trim().startsWith("#")) { 1755 newContent.append(line).append(System.getProperty("line.separator")); 1756 } else { 1757 String key = line.substring(0, equalIdx).trim(); 1758 if (newParametersToSave.containsKey(key)) { 1759 newContent.append(key).append("=").append(newParametersToSave.get(key)).append( 1760 System.getProperty("line.separator")); 1761 } else { 1762 newContent.append(line).append(System.getProperty("line.separator")); 1763 } 1764 } 1765 line = reader.readLine(); 1766 } 1767 } 1768 for (String key : newParametersToSave.keySet()) { 1769 if (templateProperties.containsKey(key)) { 1770 oldValues.put(key, templateProperties.getProperty(key)); 1771 } else { 1772 newContent.append(key).append("=").append(newParametersToSave.get(key)).append( 1773 System.getProperty("line.separator")); 1774 } 1775 } 1776 try (BufferedWriter writer = new BufferedWriter(new FileWriter(templateConf))) { 1777 writer.append(newContent.toString()); 1778 } 1779 setBasicConfiguration(); 1780 return oldValues; 1781 } 1782 1783 /** 1784 * Check driver availability and database connection 1785 * 1786 * @param databaseTemplate Nuxeo database template 1787 * @param dbName nuxeo.db.name parameter in nuxeo.conf 1788 * @param dbUser nuxeo.db.user parameter in nuxeo.conf 1789 * @param dbPassword nuxeo.db.password parameter in nuxeo.conf 1790 * @param dbHost nuxeo.db.host parameter in nuxeo.conf 1791 * @param dbPort nuxeo.db.port parameter in nuxeo.conf 1792 * @since 5.6 1793 */ 1794 public void checkDatabaseConnection(String databaseTemplate, String dbName, String dbUser, String dbPassword, 1795 String dbHost, String dbPort) 1796 throws FileNotFoundException, IOException, DatabaseDriverException, SQLException { 1797 File databaseTemplateDir = new File(nuxeoHome, TEMPLATES + File.separator + databaseTemplate); 1798 Properties templateProperties = loadTrimmedProperties(new File(databaseTemplateDir, NUXEO_DEFAULT_CONF)); 1799 String classname, connectionUrl; 1800 // check if value is set in nuxeo.conf 1801 if (userConfig.containsKey(PARAM_DB_DRIVER)) { 1802 classname = (String) userConfig.get(PARAM_DB_DRIVER); 1803 } else { 1804 classname = templateProperties.getProperty(PARAM_DB_DRIVER); 1805 } 1806 if (userConfig.containsKey(PARAM_DB_JDBC_URL)) { 1807 connectionUrl = (String) userConfig.get(PARAM_DB_JDBC_URL); 1808 } else { 1809 connectionUrl = templateProperties.getProperty(PARAM_DB_JDBC_URL); 1810 } 1811 // Load driver class from template or default lib directory 1812 Driver driver = lookupDriver(databaseTemplate, databaseTemplateDir, classname); 1813 // Test db connection 1814 DriverManager.registerDriver(driver); 1815 Properties ttProps = new Properties(userConfig); 1816 ttProps.put(PARAM_DB_HOST, dbHost); 1817 ttProps.put(PARAM_DB_PORT, dbPort); 1818 ttProps.put(PARAM_DB_NAME, dbName); 1819 ttProps.put(PARAM_DB_USER, dbUser); 1820 ttProps.put(PARAM_DB_PWD, dbPassword); 1821 TextTemplate tt = new TextTemplate(ttProps); 1822 String url = tt.processText(connectionUrl); 1823 Properties conProps = new Properties(); 1824 conProps.put("user", dbUser); 1825 conProps.put("password", dbPassword); 1826 log.debug("Testing URL " + url + " with " + conProps); 1827 Connection con = driver.connect(url, conProps); 1828 con.close(); 1829 } 1830 1831 /** 1832 * Build an {@link URLClassLoader} for the given databaseTemplate looking in the templates directory and in the 1833 * server lib directory, then looks for a driver 1834 * 1835 * @param classname Driver class name, defined by {@link #PARAM_DB_DRIVER} 1836 * @return Driver driver if found, else an Exception must have been raised. 1837 * @throws DatabaseDriverException If there was an error when trying to instantiate the driver. 1838 * @since 5.6 1839 */ 1840 private Driver lookupDriver(String databaseTemplate, File databaseTemplateDir, String classname) 1841 throws IOException, DatabaseDriverException { 1842 File[] files = (File[]) ArrayUtils.addAll( // 1843 new File(databaseTemplateDir, "lib").listFiles(), // 1844 serverConfigurator.getServerLibDir().listFiles()); 1845 List<URL> urlsList = new ArrayList<>(); 1846 if (files != null) { 1847 for (File file : files) { 1848 if (file.getName().endsWith("jar")) { 1849 try { 1850 urlsList.add(new URL("jar:file:" + file.getPath() + "!/")); 1851 log.debug("Added " + file.getPath()); 1852 } catch (MalformedURLException e) { 1853 log.error(e); 1854 } 1855 } 1856 } 1857 } 1858 URLClassLoader ucl = new URLClassLoader(urlsList.toArray(new URL[0])); 1859 try { 1860 return (Driver) Class.forName(classname, true, ucl).newInstance(); 1861 } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { 1862 throw new DatabaseDriverException(e); 1863 } 1864 } 1865 1866 /** 1867 * @since 5.6 1868 * @return an {@link Environment} initialized with a few basics 1869 */ 1870 public Environment getEnv() { 1871 /* 1872 * It could be useful to initialize DEFAULT env in {@link #setBasicConfiguration()}... For now, the generated 1873 * {@link Environment} is not static. 1874 */ 1875 if (env == null) { 1876 env = new Environment(getRuntimeHome()); 1877 File distribFile = new File(new File(nuxeoHome, TEMPLATES), "common/config/distribution.properties"); 1878 if (distribFile.exists()) { 1879 try { 1880 env.loadProperties(loadTrimmedProperties(distribFile)); 1881 } catch (IOException e) { 1882 log.error(e); 1883 } 1884 } 1885 env.loadProperties(userConfig); 1886 env.setServerHome(getNuxeoHome()); 1887 env.init(); 1888 env.setData(userConfig.getProperty(Environment.NUXEO_DATA_DIR, "data")); 1889 env.setLog(userConfig.getProperty(Environment.NUXEO_LOG_DIR, "logs")); 1890 env.setTemp(userConfig.getProperty(Environment.NUXEO_TMP_DIR, "tmp")); 1891 env.setPath(PARAM_MP_DIR, getDistributionMPDir(), env.getServerHome()); 1892 env.setPath(Environment.NUXEO_MP_DIR, getPackagesDir(), env.getServerHome()); 1893 } 1894 return env; 1895 } 1896 1897 /** 1898 * @since 5.6 1899 * @param propsFile Properties file 1900 * @return new Properties containing trimmed keys and values read in {@code propsFile} 1901 */ 1902 public static Properties loadTrimmedProperties(File propsFile) throws IOException { 1903 Properties props = new Properties(); 1904 try (FileInputStream propsIS = new FileInputStream(propsFile)) { 1905 loadTrimmedProperties(props, propsIS); 1906 } 1907 return props; 1908 } 1909 1910 /** 1911 * @since 5.6 1912 * @param props Properties object to be filled 1913 * @param propsIS Properties InputStream 1914 */ 1915 public static void loadTrimmedProperties(Properties props, InputStream propsIS) throws IOException { 1916 if (props == null) { 1917 return; 1918 } 1919 Properties p = new Properties(); 1920 p.load(propsIS); 1921 @SuppressWarnings("unchecked") 1922 Enumeration<String> pEnum = (Enumeration<String>) p.propertyNames(); 1923 while (pEnum.hasMoreElements()) { 1924 String key = pEnum.nextElement(); 1925 String value = p.getProperty(key); 1926 props.put(key.trim(), value.trim()); 1927 } 1928 } 1929 1930 /** 1931 * @return The generated properties file with dumped configuration. 1932 * @since 5.6 1933 */ 1934 public File getDumpedConfig() { 1935 return new File(getConfigDir(), CONFIGURATION_PROPERTIES); 1936 } 1937 1938 /** 1939 * Build a {@link Hashtable} which contains environment properties to instantiate a {@link InitialDirContext} 1940 * 1941 * @since 6.0 1942 */ 1943 public Hashtable<Object, Object> getContextEnv(String ldapUrl, String bindDn, String bindPassword, 1944 boolean checkAuthentication) { 1945 Hashtable<Object, Object> contextEnv = new Hashtable<>(); 1946 contextEnv.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 1947 contextEnv.put("com.sun.jndi.ldap.connect.timeout", "10000"); 1948 contextEnv.put(javax.naming.Context.PROVIDER_URL, ldapUrl); 1949 if (checkAuthentication) { 1950 contextEnv.put(javax.naming.Context.SECURITY_AUTHENTICATION, "simple"); 1951 contextEnv.put(javax.naming.Context.SECURITY_PRINCIPAL, bindDn); 1952 contextEnv.put(javax.naming.Context.SECURITY_CREDENTIALS, bindPassword); 1953 } 1954 return contextEnv; 1955 } 1956 1957 /** 1958 * Check if the LDAP parameters are correct to bind to a LDAP server. if authenticate argument is true, it will also 1959 * check if the authentication against the LDAP server succeeds 1960 * 1961 * @param authenticate Indicates if authentication against LDAP should be checked. 1962 * @since 6.0 1963 */ 1964 public void checkLdapConnection(String ldapUrl, String ldapBindDn, String ldapBindPwd, boolean authenticate) 1965 throws NamingException { 1966 checkLdapConnection(getContextEnv(ldapUrl, ldapBindDn, ldapBindPwd, authenticate)); 1967 } 1968 1969 /** 1970 * @param contextEnv Environment properties to build a {@link InitialDirContext} 1971 * @since 6.0 1972 */ 1973 public void checkLdapConnection(Hashtable<Object, Object> contextEnv) throws NamingException { 1974 DirContext dirContext = new InitialDirContext(contextEnv); 1975 dirContext.close(); 1976 } 1977 1978 /** 1979 * @return a {@link Crypto} instance initialized with the configuration parameters 1980 * @since 7.4 1981 * @see Crypto 1982 */ 1983 public Crypto getCrypto() { 1984 return userConfig.getCrypto(); 1985 } 1986 1987 /** 1988 * @param template path to configuration template directory 1989 * @return A {@code nuxeo.defaults} file if it exists. 1990 * @throws ConfigurationException if the template file is not found. 1991 * @since 7.4 1992 */ 1993 public File getTemplateConf(String template) throws ConfigurationException { 1994 File templateDir = new File(template); 1995 if (!templateDir.isAbsolute()) { 1996 templateDir = new File(System.getProperty("user.dir"), template); 1997 if (!templateDir.exists() || !new File(templateDir, NUXEO_DEFAULT_CONF).exists()) { 1998 templateDir = new File(nuxeoDefaultConf.getParentFile(), template); 1999 } 2000 } 2001 if (!templateDir.exists() || !new File(templateDir, NUXEO_DEFAULT_CONF).exists()) { 2002 throw new ConfigurationException("Template not found: " + template); 2003 } 2004 return new File(templateDir, NUXEO_DEFAULT_CONF); 2005 } 2006 2007 /** 2008 * Gets the Java options with 'nuxeo.*' properties substituted. It enables usage of property like ${nuxeo.log.dir} 2009 * inside JAVA_OPTS. 2010 * 2011 * @return the Java options string. 2012 * @deprecated Since 9.3. Use {@link #getJavaOptsString()} instead. 2013 */ 2014 @Deprecated 2015 @SuppressWarnings("unused") 2016 protected String getJavaOpts(String key, String value) { 2017 return getJavaOptsString(); 2018 } 2019 2020 /** 2021 * Gets the Java options defined in Nuxeo configuration files, e.g. <tt>bin/nuxeo.conf</tt> and 2022 * <tt>bin/nuxeoctl</tt>. 2023 * 2024 * @return the Java options. 2025 * @since 9.3 2026 */ 2027 public List<String> getJavaOpts(Function<String, String> mapper) { 2028 return Arrays.stream(JAVA_OPTS_PATTERN.split(System.getProperty(JAVA_OPTS_PROP, ""))) 2029 .map(option -> StrSubstitutor.replace(option, getUserConfig())) 2030 .map(mapper) 2031 .collect(Collectors.toList()); 2032 } 2033 2034 /** 2035 * @return the Java options string. 2036 * @since 9.3 2037 * @see #getJavaOpts(Function) 2038 */ 2039 protected String getJavaOptsString() { 2040 return String.join(" ", getJavaOpts(Function.identity())); 2041 } 2042 2043 /** 2044 * @return the value of an environment variable. Overriden for testing. 2045 * @since 9.1 2046 */ 2047 protected String getEnvironmentVariableValue(String key) { 2048 return System.getenv(key); 2049 } 2050}