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