001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2016 ForgeRock AS. 016 */ 017package org.opends.server.types; 018 019import static org.opends.messages.ConfigMessages.*; 020import static org.opends.messages.CoreMessages.*; 021import static org.opends.server.config.ConfigConstants.*; 022import static org.opends.server.util.ServerConstants.*; 023 024import java.io.File; 025import java.io.IOException; 026import java.util.Enumeration; 027import java.util.HashMap; 028import java.util.Map; 029import java.util.Properties; 030 031import org.forgerock.i18n.slf4j.LocalizedLogger; 032import org.opends.quicksetup.util.Utils; 033import org.opends.server.core.DirectoryServer; 034 035/** 036 * This class provides a set of properties that may control various 037 * aspects of the server environment. Note that these properties may 038 * only be altered before the Directory Server is started. Any 039 * attempt to change an environment configuration property while the 040 * server is running will be rejected. 041 */ 042@org.opends.server.types.PublicAPI( 043 stability=org.opends.server.types.StabilityLevel.VOLATILE, 044 mayInstantiate=true, 045 mayExtend=false, 046 mayInvoke=true) 047public final class DirectoryEnvironmentConfig 048{ 049 /** The set of properties for the environment config. */ 050 private final Map<String, String> configProperties; 051 052 private final boolean checkIfServerIsRunning; 053 054 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 055 056 /** 057 * Creates a new directory environment configuration initialized 058 * from the system properties defined in the JVM. 059 */ 060 public DirectoryEnvironmentConfig() 061 { 062 this(true); 063 } 064 065 /** 066 * Creates a new directory environment configuration initialized from the 067 * system properties defined in the JVM. 068 * 069 * @param checkIfServerIsRunning 070 * If {@code true}, prevent any change when server is running. 071 */ 072 public DirectoryEnvironmentConfig(boolean checkIfServerIsRunning) 073 { 074 this(System.getProperties(), checkIfServerIsRunning); 075 } 076 077 078 079 /** 080 * Creates a new directory environment configuration initialized 081 * with a copy of the provided set of properties. 082 * 083 * @param properties The properties to use when initializing this 084 * environment configuration, or {@code null} 085 * to use an empty set of properties. 086 * @param checkIfServerIsRunning 087 * If {@code true}, prevent any change when server is running. 088 */ 089 private DirectoryEnvironmentConfig(Properties properties, boolean checkIfServerIsRunning) 090 { 091 this.checkIfServerIsRunning = checkIfServerIsRunning; 092 configProperties = new HashMap<>(); 093 if (properties != null) 094 { 095 Enumeration<?> propertyNames = properties.propertyNames(); 096 while (propertyNames.hasMoreElements()) 097 { 098 Object o = propertyNames.nextElement(); 099 configProperties.put(String.valueOf(o), 100 String.valueOf(properties.get(o))); 101 } 102 } 103 } 104 105 /** 106 * Retrieves the property with the specified name. The check will 107 * first be made in the local config properties, but if no value is 108 * found then the JVM system properties will be checked. 109 * 110 * @param name The name of the property to retrieve. 111 * 112 * @return The property with the specified name, or {@code null} if 113 * no such property is defined. 114 */ 115 private String getProperty(String name) 116 { 117 String value = configProperties.get(name); 118 if (value == null) 119 { 120 value = System.getProperty(name); 121 } 122 return value; 123 } 124 125 126 127 /** 128 * Specifies a property with the given name and value. If a 129 * property is already defined with the given name, then its value 130 * will be replaced with the provided value, or the property will be 131 * removed if the given value is {@code null}. 132 * 133 * @param name The name of the property to set. 134 * @param value The value of the property to set, or {@code null} 135 * if the property is to be removed. 136 * 137 * @return The previous value held for the property, or 138 * {@code null} if it was not previously set. 139 * 140 * @throws InitializationException If the Directory Server is 141 * already running. 142 */ 143 public String setProperty(String name, String value) 144 throws InitializationException 145 { 146 checkServerIsRunning(); 147 148 if (value == null) 149 { 150 return configProperties.remove(name); 151 } 152 else 153 { 154 return configProperties.put(name, value); 155 } 156 } 157 158 /** 159 * Retrieves the directory that should be considered the server root. 160 * <p> 161 * The determination will first be based on the properties defined in this 162 * object. If no value is found there, then the JVM system properties will be 163 * checked, followed by an environment variable. If there is still no value, 164 * then the location of the config file, if available, is used to determine 165 * the root. 166 * 167 * @return The directory that should be considered the server root, or 168 * {@code null} if it can't be determined. 169 */ 170 public File getServerRoot() 171 { 172 File rootFile = null; 173 try 174 { 175 String serverRootPath = getProperty(PROPERTY_SERVER_ROOT); 176 if (serverRootPath == null) 177 { 178 serverRootPath = System.getenv(ENV_VAR_INSTALL_ROOT); 179 } 180 if (serverRootPath != null) 181 { 182 rootFile = new File(serverRootPath); 183 rootFile = forceNonRelativeFile(rootFile); 184 } 185 else 186 { 187 // Try to figure out root from the location of the configuration file 188 // Check for property first to avoid infinite loop with getConfigFile() 189 final String configFilePath = getProperty(PROPERTY_CONFIG_FILE); 190 if (configFilePath != null) 191 { 192 final File configDirFile = getConfigFile().getParentFile(); 193 if (configDirFile != null 194 && CONFIG_DIR_NAME.equals(configDirFile.getName())) 195 { 196 File parent = configDirFile.getParentFile(); 197 rootFile = forceNonRelativeFile(parent); 198 } 199 } 200 } 201 } 202 catch (Exception e) 203 { 204 logger.error(ERR_CONFIG_CANNOT_DETERMINE_SERVER_ROOT, 205 ENV_VAR_INSTALL_ROOT, e); 206 } 207 if (rootFile == null) 208 { 209 logger.error(ERR_CONFIG_CANNOT_DETERMINE_SERVER_ROOT, 210 ENV_VAR_INSTALL_ROOT); 211 } 212 return rootFile; 213 } 214 215 /** 216 * Retrieves the path of the directory that should be considered the server 217 * root. 218 * <p> 219 * This method uses the same rules than {@code getServerRoot} method, but 220 * never returns {@code null}. If no directory can be found it returns as a 221 * last resort the value of "user.dir" system property. 222 * 223 * @return the path of the directory that should be considered the server 224 * root. 225 */ 226 public String getServerRootAsString() { 227 File serverRoot = getServerRoot(); 228 if (serverRoot != null) 229 { 230 return serverRoot.getAbsolutePath(); 231 } 232 // We don't know where the server root is, so we'll have to assume it's 233 // the current working directory. 234 return System.getProperty("user.dir"); 235 } 236 237 /** 238 * Retrieves the directory that should be considered the instance 239 * root. 240 * 241 * @return The directory that should be considered the instance 242 * root or {@code null} if it can't be determined. 243 */ 244 public File getInstanceRoot() { 245 File serverRoot = getServerRoot(); 246 if (serverRoot != null) 247 { 248 File instanceRoot = new File(Utils.getInstancePathFromInstallPath(getServerRoot().getAbsolutePath())); 249 return forceNonRelativeFile(instanceRoot); 250 } 251 return null; 252 } 253 254 /** 255 * Retrieves the path of the directory that should be considered the instance 256 * root. 257 * <p> 258 * This method uses the same rules than {@code getInstanceRoot} method, but 259 * never returns {@code null}. If no directory can be found it returns as a 260 * last resort the value of "user.dir" system property. 261 * 262 * @return the path of the directory that should be considered the instance 263 * root. 264 */ 265 public String getInstanceRootAsString() 266 { 267 File instanceRoot = getInstanceRoot(); 268 if (instanceRoot != null) 269 { 270 return instanceRoot.getAbsolutePath(); 271 } 272 273 // We don't know where the instance root is, so we'll have to assume it's 274 // the current working directory. 275 return System.getProperty("user.dir"); 276 } 277 278 private File forceNonRelativeFile(File file) { 279 // Do a best effort to avoid having a relative representation 280 // (for instance to avoid having ../../../). 281 try 282 { 283 return file.getCanonicalFile(); 284 } 285 catch (IOException ioe) 286 { 287 return file.getAbsoluteFile(); 288 } 289 } 290 291 /** 292 * Retrieves the directory that should be considered the instance 293 * root. The determination will first be based on the properties 294 * defined in this config object. If no value is found there, then 295 * the JVM system properties will be checked, followed by an 296 * environment variable. 297 * 298 * @param serverRoot the server Root 299 * 300 * @return The directory that should be considered the instance 301 * root, or {@code null} if it is not defined. 302 */ 303 private static File getInstanceRootFromServerRoot(File serverRoot) 304 { 305 return new File(Utils.getInstancePathFromInstallPath(serverRoot.getAbsolutePath())); 306 } 307 308 309 310 /** 311 * Specifies the directory that should be considered the server 312 * root. Any relative path used in the server should be considered 313 * relative to the server root. 314 * 315 * @param serverRoot The directory that should be considered the 316 * server root. 317 * 318 * @return The previous server root, or {@code null} if there was 319 * none. 320 * 321 * @throws InitializationException If the Directory Server is 322 * already running or there is a 323 * problem with the provided 324 * server root. 325 */ 326 public File setServerRoot(File serverRoot) 327 throws InitializationException 328 { 329 checkServerIsRunning(); 330 331 if (!serverRoot.exists() || !serverRoot.isDirectory()) 332 { 333 throw new InitializationException( 334 ERR_DIRCFG_INVALID_SERVER_ROOT.get( 335 serverRoot.getAbsolutePath())); 336 } 337 338 return setPathProperty(PROPERTY_SERVER_ROOT, serverRoot); 339 } 340 341 /** 342 * Sets a path property. 343 * 344 * @param propertyName 345 * The property name to set. 346 * @param newPath 347 * The path to set on the property. 348 * @return The previous property value, or {@code null} if there was none. 349 * @throws InitializationException 350 * If the Directory Server is already running or there is a problem 351 * with the provided server root. 352 */ 353 private File setPathProperty(String propertyName, File newPath) 354 throws InitializationException 355 { 356 String normalizedNewPath; 357 try 358 { 359 normalizedNewPath = newPath.getCanonicalPath(); 360 } 361 catch (Exception e) 362 { 363 normalizedNewPath = newPath.getAbsolutePath(); 364 } 365 366 String oldPath = setProperty(propertyName, normalizedNewPath); 367 if (oldPath != null) 368 { 369 return new File(oldPath); 370 } 371 return null; 372 } 373 374 /** 375 * Specifies the directory that should be considered the instance 376 * root. Any relative path used in the server should be considered 377 * relative to the instance root. 378 * 379 * @param instanceRoot The directory that should be considered the 380 * instanceRoot root. 381 * 382 * @return The previous server root, or {@code null} if there was 383 * none. 384 * 385 * @throws InitializationException If the Directory Server is 386 * already running or there is a 387 * problem with the provided 388 * server root. 389 */ 390 public File setInstanceRoot(File instanceRoot) 391 throws InitializationException 392 { 393 checkServerIsRunning(); 394 395 if (!instanceRoot.exists() || !instanceRoot.isDirectory()) 396 { 397 throw new InitializationException( 398 ERR_DIRCFG_INVALID_SERVER_ROOT.get( 399 instanceRoot.getAbsolutePath())); 400 } 401 402 return setPathProperty(PROPERTY_INSTANCE_ROOT, instanceRoot); 403 } 404 405 406 /** 407 * Retrieves the configuration file that should be used to 408 * initialize the Directory Server config handler. If no default 409 * configuration file is specified, then the server will attempt to 410 * use "config/config.ldif" below the server root if it exists. 411 * 412 * @return The configuration file that should be used to initialize 413 * the Directory Server config handler, or {@code null} if 414 * no configuration file is defined. 415 */ 416 public File getConfigFile() 417 { 418 String configFilePath = getProperty(PROPERTY_CONFIG_FILE); 419 if (configFilePath != null) 420 { 421 return new File(configFilePath); 422 } 423 424 File serverRoot = getServerRoot(); 425 if (serverRoot != null) 426 { 427 File instanceRoot = getInstanceRootFromServerRoot(serverRoot); 428 File configDir = new File(instanceRoot, CONFIG_DIR_NAME); 429 File configFile = new File(configDir, CONFIG_FILE_NAME); 430 if (configFile.exists()) 431 { 432 return configFile; 433 } 434 } 435 return null; 436 } 437 438 439 440 /** 441 * Specifies the configuration file that should be used to 442 * initialize the Directory Server config handler. 443 * 444 * @param configFile The configuration file that should be used to 445 * initialize the Directory Server config 446 * handler. 447 * 448 * @return The previously-defined configuration file, or 449 * {@code null} if none was defined. 450 * 451 * @throws InitializationException If the Directory Server is 452 * already running or there is a 453 * problem with the provided 454 * configuration file. 455 */ 456 public File setConfigFile(File configFile) 457 throws InitializationException 458 { 459 checkServerIsRunning(); 460 461 if (!configFile.exists() || !configFile.isFile()) 462 { 463 throw new InitializationException( 464 ERR_DIRCFG_INVALID_CONFIG_FILE.get( 465 configFile.getAbsolutePath())); 466 } 467 468 return setPathProperty(PROPERTY_CONFIG_FILE, configFile); 469 } 470 471 /** 472 * Indicates whether the Directory Server should attempt to start 473 * with the "last known good" configuration rather than the current 474 * active configuration file. Note that if there is no "last known 475 * good" configuration file available, then the server should try to 476 * start using the current, active configuration file. If no 477 * explicit value is defined, then a default result of {@code false} 478 * will be returned. 479 * 480 * @return {@code true} if the Directory Server should attempt to 481 * start using the "last known good" configuration, or 482 * {@code false} if it should try to start using the 483 * active configuration. 484 */ 485 public boolean useLastKnownGoodConfiguration() 486 { 487 return isPropertyTrue(PROPERTY_USE_LAST_KNOWN_GOOD_CONFIG); 488 } 489 490 /** 491 * Indicates whether the property value is set and equal to "true" for the 492 * supplied property name. 493 * 494 * @param propertyName 495 * the name of the property to be checked 496 * @return {@code true} if the property is set and the property value is 497 * <code>"true"</code>, {@code false} otherwise . 498 */ 499 private boolean isPropertyTrue(String propertyName) 500 { 501 return "true".equalsIgnoreCase(getProperty(propertyName)); 502 } 503 504 /** 505 * Indicates whether the Directory Server should maintain an archive 506 * of previous configurations. If no explicit value is defined, 507 * then a default result of {@code true} will be returned. 508 * 509 * @return {@code true} if the Directory Server should maintain an 510 * archive of previous configurations, or {@code false} if 511 * not. 512 */ 513 public boolean maintainConfigArchive() 514 { 515 String maintainArchiveStr = 516 getProperty(PROPERTY_MAINTAIN_CONFIG_ARCHIVE); 517 return maintainArchiveStr == null 518 || !"false".equalsIgnoreCase(maintainArchiveStr); 519 } 520 521 522 523 /** 524 * Specifies whether the Directory Server should maintain an archive 525 * of previous configurations. 526 * 527 * @param maintainConfigArchive Indicates whether the Directory 528 * Server should maintain an archive 529 * of previous configurations. 530 * 531 * @return The previous setting for this configuration option. If 532 * no previous value was specified, then {@code true} will 533 * be returned. 534 * 535 * @throws InitializationException If the Directory Server is 536 * already running. 537 */ 538 public boolean setMaintainConfigArchive( 539 boolean maintainConfigArchive) 540 throws InitializationException 541 { 542 checkServerIsRunning(); 543 544 String oldMaintainStr = 545 setProperty(PROPERTY_MAINTAIN_CONFIG_ARCHIVE, 546 String.valueOf(maintainConfigArchive)); 547 return oldMaintainStr == null || !"false".equalsIgnoreCase(oldMaintainStr); 548 } 549 550 551 552 /** 553 * Retrieves the maximum number of archived configurations that the 554 * Directory Server should maintain. If no value is defined, then a 555 * value of zero will be returned. 556 * 557 * @return The maximum number of archived configurations that the 558 * Directory Server should maintain, or zero if there 559 * should not be any limit. 560 */ 561 public int getMaxConfigArchiveSize() 562 { 563 String maxSizeStr = 564 getProperty(PROPERTY_MAX_CONFIG_ARCHIVE_SIZE); 565 if (maxSizeStr == null) 566 { 567 return 0; 568 } 569 570 try 571 { 572 int maxSize = Integer.parseInt(maxSizeStr); 573 return maxSize > 0 ? maxSize : 0; 574 } 575 catch (Exception e) 576 { 577 return 0; 578 } 579 } 580 581 /** 582 * Retrieves the directory that contains the server schema 583 * configuration files. If no value is defined, but a default 584 * directory of "config/schema" exists below the server root, then 585 * that will be returned. 586 * 587 * @return The directory that contains the server schema 588 * configuration files, or {@code null} if none is defined. 589 */ 590 public File getSchemaDirectory() 591 { 592 String schemaDirectoryPath = getProperty(PROPERTY_SCHEMA_DIRECTORY); 593 if (schemaDirectoryPath != null) 594 { 595 return new File(schemaDirectoryPath); 596 } 597 598 File serverRoot = getServerRoot(); 599 if (serverRoot != null) 600 { 601 File instanceRoot = getInstanceRootFromServerRoot(serverRoot); 602 File schemaDir = new File(instanceRoot.getAbsolutePath() + File.separator + PATH_SCHEMA_DIR); 603 if (schemaDir.exists() && schemaDir.isDirectory()) 604 { 605 return schemaDir; 606 } 607 } 608 return null; 609 } 610 611 612 613 /** 614 * Specifies the directory that should contain the server schema 615 * configuration files. It must exist and must be a directory. 616 * 617 * @param schemaDirectory The directory that should contain the 618 * server schema configuration files. 619 * 620 * @return The previously-defined schema configuration directory, 621 * or {@code null} if none was defined. 622 * 623 * @throws InitializationException If the Directory Server is 624 * already running or there is a 625 * problem with the provided 626 * schema directory. 627 */ 628 public File setSchemaDirectory(File schemaDirectory) 629 throws InitializationException 630 { 631 checkServerIsRunning(); 632 633 if (!schemaDirectory.exists() || !schemaDirectory.isDirectory()) 634 { 635 throw new InitializationException( 636 ERR_DIRCFG_INVALID_SCHEMA_DIRECTORY.get( 637 schemaDirectory.getAbsolutePath())); 638 } 639 640 return setPathProperty(PROPERTY_SCHEMA_DIRECTORY, schemaDirectory); 641 } 642 643 644 645 /** 646 * Retrieves the directory that should be used to hold the server 647 * lock files. If no value is defined, then the server will attempt 648 * to use a default directory of "locks" below the server root. 649 * 650 * @return The directory that should be used to hold the server 651 * lock files, or {@code null} if it cannot be determined. 652 */ 653 public File getLockDirectory() 654 { 655 String lockFilePath = getProperty(PROPERTY_LOCK_DIRECTORY); 656 if (lockFilePath != null) 657 { 658 return new File(lockFilePath); 659 } 660 661 File serverRoot = getServerRoot(); 662 if (serverRoot != null) 663 { 664 File instanceRoot = getInstanceRootFromServerRoot(serverRoot); 665 return new File(instanceRoot, LOCKS_DIRECTORY); 666 } 667 return null; 668 } 669 670 /** 671 * Indicates whether the Directory Server startup process should 672 * skip the connection handler creation and initialization phases. 673 * 674 * @return {@code true} if the Directory Server should not start 675 * its connection handlers, or {@code false} if the 676 * connection handlers should be enabled. 677 */ 678 public boolean disableConnectionHandlers() 679 { 680 return isPropertyTrue(PROPERTY_DISABLE_CONNECTION_HANDLERS); 681 } 682 683 /** 684 * Indicates whether the Directory Server startup process should 685 * skip the synchronization provider creation and initialization 686 * phases. 687 * 688 * @return {@code true} if the Directory Server should not start 689 * its synchronization provider, or {@code false} if the 690 * synchronization provider should be enabled. 691 */ 692 public boolean disableSynchronization() 693 { 694 return isPropertyTrue(PROPERTY_DISABLE_SYNCHRONIZATION); 695 } 696 697 /** 698 * Indicates whether the Directory Server startup process should 699 * skip the synchronization between admin data and the 700 * configuration. 701 * 702 * @return {@code true} if the Directory Server should start 703 * synchronization between admin data and the 704 * configuration. 705 */ 706 public boolean disableAdminDataSynchronization() 707 { 708 return isPropertyTrue(PROPERTY_DISABLE_ADMIN_DATA_SYNCHRONIZATION); 709 } 710 711 /** 712 * Specifies whether the Directory Server startup process should 713 * skip the connection handler creation and initialization phases. 714 * 715 * @param disableConnectionHandlers Indicates whether the 716 * Directory Server should skip 717 * the connection handler 718 * creation and initialization 719 * phases. 720 * 721 * @return The previous setting for this configuration option. If 722 * no previous value was specified, then {@code false} will 723 * be returned. 724 * 725 * @throws InitializationException If the Directory Server is 726 * already running. 727 */ 728 public boolean setDisableConnectionHandlers( 729 boolean disableConnectionHandlers) 730 throws InitializationException 731 { 732 return setBooleanProperty(PROPERTY_DISABLE_CONNECTION_HANDLERS, 733 disableConnectionHandlers); 734 } 735 736 /** 737 * Sets a boolean property. 738 * 739 * @param propertyName 740 * the property name to set 741 * @param newValue 742 * the new value to set for the property 743 * @return The previous setting for this configuration option. If no previous 744 * value was specified, then {@code false} will be returned. 745 * @throws InitializationException 746 * If the Directory Server is already running or there is a problem 747 * with the provided server root. 748 */ 749 private boolean setBooleanProperty(String propertyName, boolean newValue) 750 throws InitializationException 751 { 752 checkServerIsRunning(); 753 754 final String oldValue = setProperty(propertyName, String.valueOf(newValue)); 755 return "true".equalsIgnoreCase(oldValue); 756 } 757 758 /** 759 * Indicates whether all threads created by the Directory Server 760 * should be created as daemon threads. 761 * 762 * @return {@code true} if all threads created by the Directory 763 * Server should be created as daemon threads, or 764 * {@code false} if not. 765 */ 766 public boolean forceDaemonThreads() 767 { 768 return isPropertyTrue(PROPERTY_FORCE_DAEMON_THREADS); 769 } 770 771 772 773 /** 774 * Specifies whether all threads created by the Directory Server 775 * should be created as daemon threads. 776 * 777 * @param forceDaemonThreads Indicates whether all threads created 778 * by the Directory Server should be 779 * created as daemon threads. 780 * 781 * @return The previous setting for this configuration option. If 782 * no previous value was specified, then {@code false} will 783 * be returned. 784 * 785 * @throws InitializationException If the Directory Server is 786 * already running. 787 */ 788 public boolean setForceDaemonThreads(boolean forceDaemonThreads) 789 throws InitializationException 790 { 791 return setBooleanProperty(PROPERTY_FORCE_DAEMON_THREADS, 792 forceDaemonThreads); 793 } 794 795 796 797 /** 798 * Indicates whether the Directory Server should be allowed to use 799 * the {@code Runtime.exec()} method to be able to launch external 800 * commands on the underlying system. 801 * 802 * @return {@code true} if the Directory Server should be allowed 803 * to use {@code Runtime.exec()}, or {@code false} if not. 804 */ 805 public boolean disableExec() 806 { 807 return isPropertyTrue(PROPERTY_DISABLE_EXEC); 808 } 809 810 /** Throws an exception if server is running and it is not allowed. */ 811 private void checkServerIsRunning() throws InitializationException 812 { 813 if (checkIfServerIsRunning && DirectoryServer.isRunning()) 814 { 815 throw new InitializationException( 816 ERR_DIRCFG_SERVER_ALREADY_RUNNING.get()); 817 } 818 } 819}