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