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