001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.backends.jeb; 028 029import java.io.File; 030import java.util.*; 031import java.util.concurrent.ConcurrentHashMap; 032import java.util.concurrent.atomic.AtomicLong; 033 034import org.forgerock.i18n.LocalizableMessage; 035import org.forgerock.i18n.slf4j.LocalizedLogger; 036import org.forgerock.opendj.config.server.ConfigException; 037import org.opends.server.admin.server.ConfigurationChangeListener; 038import org.opends.server.admin.std.server.LocalDBBackendCfg; 039import org.opends.server.api.Backend; 040import org.opends.server.core.DirectoryServer; 041import org.forgerock.opendj.config.server.ConfigChangeResult; 042import org.opends.server.types.DN; 043import org.opends.server.types.FilePermission; 044import org.opends.server.types.InitializationException; 045 046import com.sleepycat.je.*; 047import com.sleepycat.je.config.ConfigParam; 048import com.sleepycat.je.config.EnvironmentParams; 049 050import static org.opends.messages.ConfigMessages.*; 051import static org.opends.messages.BackendMessages.*; 052import static org.opends.server.util.StaticUtils.*; 053 054/** 055 * Wrapper class for the JE environment. Root container holds all the entry 056 * containers for each base DN. It also maintains all the openings and closings 057 * of the entry containers. 058 */ 059public class RootContainer 060 implements ConfigurationChangeListener<LocalDBBackendCfg> 061{ 062 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 063 064 /** The JE database environment. */ 065 private Environment env; 066 067 /** Used to force a checkpoint during import. */ 068 private final CheckpointConfig importForceCheckPoint = new CheckpointConfig(); 069 070 /** The backend configuration. */ 071 private LocalDBBackendCfg config; 072 073 /** The backend to which this entry root container belongs. */ 074 private final Backend<?> backend; 075 076 /** The database environment monitor for this JE environment. */ 077 private DatabaseEnvironmentMonitor monitor; 078 079 /** The base DNs contained in this root container. */ 080 private final ConcurrentHashMap<DN, EntryContainer> entryContainers = new ConcurrentHashMap<>(); 081 082 /** The cached value of the next entry identifier to be assigned. */ 083 private AtomicLong nextid = new AtomicLong(1); 084 085 /** The compressed schema manager for this backend. */ 086 private JECompressedSchema compressedSchema; 087 088 089 090 /** 091 * Creates a new RootContainer object. Each root container represents a JE 092 * environment. 093 * 094 * @param config The configuration of the JE backend. 095 * @param backend A reference to the JE back end that is creating this 096 * root container. 097 */ 098 RootContainer(Backend<?> backend, LocalDBBackendCfg config) 099 { 100 this.backend = backend; 101 this.config = config; 102 103 getMonitorProvider().enableFilterUseStats(config.isIndexFilterAnalyzerEnabled()); 104 getMonitorProvider().setMaxEntries(config.getIndexFilterAnalyzerMaxFilters()); 105 106 config.addLocalDBChangeListener(this); 107 importForceCheckPoint.setForce(true); 108 } 109 110 /** 111 * Opens the root container using the JE configuration object provided. 112 * 113 * @param envConfig The JE environment configuration. 114 * @throws DatabaseException If a database error occurs when creating 115 * the environment. 116 * @throws InitializationException If an initialization error occurs while 117 * creating the environment. 118 * @throws ConfigException If an configuration error occurs while 119 * creating the environment. 120 */ 121 public void open(EnvironmentConfig envConfig) 122 throws DatabaseException, InitializationException, ConfigException 123 { 124 // Determine the backend database directory. 125 File parentDirectory = getFileForPath(config.getDBDirectory()); 126 File backendDirectory = new File(parentDirectory, config.getBackendId()); 127 128 // Create the directory if it doesn't exist. 129 if (!backendDirectory.exists()) 130 { 131 if(!backendDirectory.mkdirs()) 132 { 133 throw new ConfigException(ERR_CREATE_FAIL.get(backendDirectory.getPath())); 134 } 135 } 136 //Make sure the directory is valid. 137 else if (!backendDirectory.isDirectory()) 138 { 139 throw new ConfigException(ERR_DIRECTORY_INVALID.get(backendDirectory.getPath())); 140 } 141 142 FilePermission backendPermission; 143 try 144 { 145 backendPermission = 146 FilePermission.decodeUNIXMode(config.getDBDirectoryPermissions()); 147 } 148 catch(Exception e) 149 { 150 throw new ConfigException(ERR_CONFIG_BACKEND_MODE_INVALID.get(config.dn())); 151 } 152 153 //Make sure the mode will allow the server itself access to 154 //the database 155 if(!backendPermission.isOwnerWritable() || 156 !backendPermission.isOwnerReadable() || 157 !backendPermission.isOwnerExecutable()) 158 { 159 LocalizableMessage message = ERR_CONFIG_BACKEND_INSANE_MODE.get( 160 config.getDBDirectoryPermissions()); 161 throw new ConfigException(message); 162 } 163 164 // Get the backend database backendDirectory permissions and apply 165 try 166 { 167 if(!FilePermission.setPermissions(backendDirectory, backendPermission)) 168 { 169 logger.warn(WARN_UNABLE_SET_PERMISSIONS, backendPermission, backendDirectory); 170 } 171 } 172 catch(Exception e) 173 { 174 // Log an warning that the permissions were not set. 175 logger.warn(WARN_SET_PERMISSIONS_FAILED, backendDirectory, e); 176 } 177 178 // Open the database environment 179 env = new Environment(backendDirectory, 180 envConfig); 181 182 if (logger.isTraceEnabled()) 183 { 184 logger.trace("JE (%s) environment opened with the following config: %n%s", 185 JEVersion.CURRENT_VERSION, env.getConfig()); 186 187 // Get current size of heap in bytes 188 long heapSize = Runtime.getRuntime().totalMemory(); 189 190 // Get maximum size of heap in bytes. The heap cannot grow beyond this size. 191 // Any attempt will result in an OutOfMemoryException. 192 long heapMaxSize = Runtime.getRuntime().maxMemory(); 193 194 // Get amount of free memory within the heap in bytes. This size will increase 195 // after garbage collection and decrease as new objects are created. 196 long heapFreeSize = Runtime.getRuntime().freeMemory(); 197 198 logger.trace("Current size of heap: %d bytes", heapSize); 199 logger.trace("Max size of heap: %d bytes", heapMaxSize); 200 logger.trace("Free memory in heap: %d bytes", heapFreeSize); 201 } 202 203 compressedSchema = new JECompressedSchema(env); 204 openAndRegisterEntryContainers(config.getBaseDN()); 205 } 206 207 /** 208 * Opens the entry container for a base DN. If the entry container does not 209 * exist for the base DN, it will be created. The entry container will be 210 * opened with the same mode as the root container. Any entry containers 211 * opened in a read only root container will also be read only. Any entry 212 * containers opened in a non transactional root container will also be non 213 * transactional. 214 * 215 * @param baseDN The base DN of the entry container to open. 216 * @param name The name of the entry container or <CODE>NULL</CODE> to open 217 * the default entry container for the given base DN. 218 * @return The opened entry container. 219 * @throws DatabaseException If an error occurs while opening the entry 220 * container. 221 * @throws ConfigException If an configuration error occurs while opening 222 * the entry container. 223 */ 224 EntryContainer openEntryContainer(DN baseDN, String name) 225 throws DatabaseException, ConfigException 226 { 227 String databasePrefix; 228 if(name == null || name.equals("")) 229 { 230 databasePrefix = baseDN.toNormalizedUrlSafeString(); 231 } 232 else 233 { 234 databasePrefix = name; 235 } 236 237 EntryContainer ec = new EntryContainer(baseDN, databasePrefix, backend.getBackendID(), config, env, this); 238 ec.open(); 239 return ec; 240 } 241 242 /** 243 * Registers the entry container for a base DN. 244 * 245 * @param baseDN The base DN of the entry container to close. 246 * @param entryContainer The entry container to register for the baseDN. 247 * @throws InitializationException If an error occurs while opening the 248 * entry container. 249 */ 250 void registerEntryContainer(DN baseDN, EntryContainer entryContainer) 251 throws InitializationException 252 { 253 EntryContainer ec1 = this.entryContainers.get(baseDN); 254 255 // If an entry container for this baseDN is already open we don't allow 256 // another to be opened. 257 if (ec1 != null) 258 { 259 throw new InitializationException(ERR_ENTRY_CONTAINER_ALREADY_REGISTERED.get(ec1.getDatabasePrefix(), baseDN)); 260 } 261 262 this.entryContainers.put(baseDN, entryContainer); 263 } 264 265 /** 266 * Opens the entry containers for multiple base DNs. 267 * 268 * @param baseDNs The base DNs of the entry containers to open. 269 * @throws DatabaseException If a database error occurs while opening 270 * the entry container. 271 * @throws InitializationException If an initialization error occurs while 272 * opening the entry container. 273 * @throws ConfigException If a configuration error occurs while 274 * opening the entry container. 275 */ 276 private void openAndRegisterEntryContainers(Set<DN> baseDNs) 277 throws DatabaseException, InitializationException, ConfigException 278 { 279 EntryID id; 280 EntryID highestID = null; 281 for(DN baseDN : baseDNs) 282 { 283 EntryContainer ec = openEntryContainer(baseDN, null); 284 id = ec.getHighestEntryID(); 285 registerEntryContainer(baseDN, ec); 286 if(highestID == null || id.compareTo(highestID) > 0) 287 { 288 highestID = id; 289 } 290 } 291 292 nextid = new AtomicLong(highestID.longValue() + 1); 293 } 294 295 /** 296 * Unregisters the entry container for a base DN. 297 * 298 * @param baseDN The base DN of the entry container to close. 299 * @return The entry container that was unregistered or NULL if a entry 300 * container for the base DN was not registered. 301 */ 302 EntryContainer unregisterEntryContainer(DN baseDN) 303 { 304 return entryContainers.remove(baseDN); 305 } 306 307 /** 308 * Retrieves the compressed schema manager for this backend. 309 * 310 * @return The compressed schema manager for this backend. 311 */ 312 public JECompressedSchema getCompressedSchema() 313 { 314 return compressedSchema; 315 } 316 317 /** 318 * Get the DatabaseEnvironmentMonitor object for JE environment used by this 319 * root container. 320 * 321 * @return The DatabaseEnvironmentMonito object. 322 */ 323 public DatabaseEnvironmentMonitor getMonitorProvider() 324 { 325 if(monitor == null) 326 { 327 String monitorName = backend.getBackendID() + " Database Environment"; 328 monitor = new DatabaseEnvironmentMonitor(monitorName, this); 329 } 330 331 return monitor; 332 } 333 334 /** 335 * Preload the database cache. There is no preload if the configured preload 336 * time limit is zero. 337 * 338 * @param timeLimit The time limit for the preload process. 339 */ 340 public void preload(long timeLimit) 341 { 342 if (timeLimit > 0) 343 { 344 // Get a list of all the databases used by the backend. 345 ArrayList<DatabaseContainer> dbList = new ArrayList<>(); 346 for (EntryContainer ec : entryContainers.values()) 347 { 348 ec.sharedLock.lock(); 349 try 350 { 351 ec.listDatabases(dbList); 352 } 353 finally 354 { 355 ec.sharedLock.unlock(); 356 } 357 } 358 359 // Sort the list in order of priority. 360 Collections.sort(dbList, new DbPreloadComparator()); 361 362 // Preload each database until we reach the time limit or the cache 363 // is filled. 364 try 365 { 366 // Configure preload of Leaf Nodes (LNs) containing the data values. 367 PreloadConfig preloadConfig = new PreloadConfig(); 368 preloadConfig.setLoadLNs(true); 369 370 logger.info(NOTE_CACHE_PRELOAD_STARTED, backend.getBackendID()); 371 372 boolean isInterrupted = false; 373 374 long timeEnd = System.currentTimeMillis() + timeLimit; 375 376 for (DatabaseContainer db : dbList) 377 { 378 // Calculate the remaining time. 379 long timeRemaining = timeEnd - System.currentTimeMillis(); 380 if (timeRemaining <= 0) 381 { 382 break; 383 } 384 385 preloadConfig.setMaxMillisecs(timeRemaining); 386 PreloadStats preloadStats = db.preload(preloadConfig); 387 388 if(logger.isTraceEnabled()) 389 { 390 logger.trace("file=" + db.getName() + " LNs=" + preloadStats.getNLNsLoaded()); 391 } 392 393 // Stop if the cache is full or the time limit has been exceeded. 394 PreloadStatus preloadStatus = preloadStats.getStatus(); 395 if (preloadStatus != PreloadStatus.SUCCESS) 396 { 397 if (preloadStatus == PreloadStatus.EXCEEDED_TIME) { 398 logger.info(NOTE_CACHE_PRELOAD_INTERRUPTED_BY_TIME, backend.getBackendID(), db.getName()); 399 } else if (preloadStatus == PreloadStatus.FILLED_CACHE) { 400 logger.info(NOTE_CACHE_PRELOAD_INTERRUPTED_BY_SIZE, backend.getBackendID(), db.getName()); 401 } else { 402 logger.info(NOTE_CACHE_PRELOAD_INTERRUPTED_UNKNOWN, backend.getBackendID(), db.getName()); 403 } 404 405 isInterrupted = true; 406 break; 407 } 408 409 logger.info(NOTE_CACHE_DB_PRELOADED, db.getName()); 410 } 411 412 if (!isInterrupted) { 413 logger.info(NOTE_CACHE_PRELOAD_DONE, backend.getBackendID()); 414 } 415 416 // Log an informational message about the size of the cache. 417 EnvironmentStats stats = env.getStats(new StatsConfig()); 418 long total = stats.getCacheTotalBytes(); 419 420 logger.info(NOTE_CACHE_SIZE_AFTER_PRELOAD, total / (1024 * 1024)); 421 } 422 catch (DatabaseException e) 423 { 424 logger.traceException(e); 425 426 logger.error(ERR_CACHE_PRELOAD, backend.getBackendID(), 427 stackTraceToSingleLineString(e.getCause() != null ? e.getCause() : e)); 428 } 429 } 430 } 431 432 /** 433 * Closes this root container. 434 * 435 * @throws DatabaseException If an error occurs while attempting to close 436 * the root container. 437 */ 438 public void close() throws DatabaseException 439 { 440 for(DN baseDN : entryContainers.keySet()) 441 { 442 EntryContainer ec = unregisterEntryContainer(baseDN); 443 ec.exclusiveLock.lock(); 444 try 445 { 446 ec.close(); 447 } 448 finally 449 { 450 ec.exclusiveLock.unlock(); 451 } 452 } 453 454 compressedSchema.close(); 455 config.removeLocalDBChangeListener(this); 456 457 if (env != null) 458 { 459 env.close(); 460 env = null; 461 } 462 } 463 464 /** 465 * Return all the entry containers in this root container. 466 * 467 * @return The entry containers in this root container. 468 */ 469 public Collection<EntryContainer> getEntryContainers() 470 { 471 return entryContainers.values(); 472 } 473 474 /** 475 * Returns all the baseDNs this root container stores. 476 * 477 * @return The set of DNs this root container stores. 478 */ 479 public Set<DN> getBaseDNs() 480 { 481 return entryContainers.keySet(); 482 } 483 484 /** 485 * Return the entry container for a specific base DN. 486 * 487 * @param baseDN The base DN of the entry container to retrieve. 488 * @return The entry container for the base DN. 489 */ 490 public EntryContainer getEntryContainer(DN baseDN) 491 { 492 EntryContainer ec = null; 493 DN nodeDN = baseDN; 494 495 while (ec == null && nodeDN != null) 496 { 497 ec = entryContainers.get(nodeDN); 498 if (ec == null) 499 { 500 nodeDN = nodeDN.getParentDNInSuffix(); 501 } 502 } 503 504 return ec; 505 } 506 507 /** 508 * Get the environment stats of the JE environment used in this root 509 * container. 510 * 511 * @param statsConfig The configuration to use for the EnvironmentStats 512 * object. 513 * @return The environment status of the JE environment. 514 * @throws DatabaseException If an error occurs while retrieving the stats 515 * object. 516 */ 517 public EnvironmentStats getEnvironmentStats(StatsConfig statsConfig) 518 throws DatabaseException 519 { 520 return env.getStats(statsConfig); 521 } 522 523 /** 524 * Get the environment transaction stats of the JE environment used 525 * in this root container. 526 * 527 * @param statsConfig The configuration to use for the EnvironmentStats 528 * object. 529 * @return The environment status of the JE environment. 530 * @throws DatabaseException If an error occurs while retrieving the stats 531 * object. 532 */ 533 public TransactionStats getEnvironmentTransactionStats( 534 StatsConfig statsConfig) throws DatabaseException 535 { 536 return env.getTransactionStats(statsConfig); 537 } 538 539 /** 540 * Get the environment config of the JE environment used in this root 541 * container. 542 * 543 * @return The environment config of the JE environment. 544 * @throws DatabaseException If an error occurs while retrieving the 545 * configuration object. 546 */ 547 public EnvironmentConfig getEnvironmentConfig() throws DatabaseException 548 { 549 return env.getConfig(); 550 } 551 552 /** 553 * Get the backend configuration used by this root container. 554 * 555 * @return The JE backend configuration used by this root container. 556 */ 557 public LocalDBBackendCfg getConfiguration() 558 { 559 return config; 560 } 561 562 /** 563 * Get the total number of entries in this root container. 564 * 565 * @return The number of entries in this root container 566 * @throws DatabaseException If an error occurs while retrieving the entry 567 * count. 568 */ 569 public long getEntryCount() throws DatabaseException 570 { 571 long entryCount = 0; 572 for(EntryContainer ec : this.entryContainers.values()) 573 { 574 ec.sharedLock.lock(); 575 try 576 { 577 entryCount += ec.getEntryCount(); 578 } 579 finally 580 { 581 ec.sharedLock.unlock(); 582 } 583 } 584 585 return entryCount; 586 } 587 588 /** 589 * Assign the next entry ID. 590 * 591 * @return The assigned entry ID. 592 */ 593 public EntryID getNextEntryID() 594 { 595 return new EntryID(nextid.getAndIncrement()); 596 } 597 598 /** 599 * Return the lowest entry ID assigned. 600 * 601 * @return The lowest entry ID assigned. 602 */ 603 public Long getLowestEntryID() 604 { 605 return 1L; 606 } 607 608 /** 609 * Resets the next entry ID counter to zero. This should only be used after 610 * clearing all databases. 611 */ 612 public void resetNextEntryID() 613 { 614 nextid.set(1); 615 } 616 617 618 619 /** {@inheritDoc} */ 620 @Override 621 public boolean isConfigurationChangeAcceptable( 622 LocalDBBackendCfg cfg, 623 List<LocalizableMessage> unacceptableReasons) 624 { 625 boolean acceptable = true; 626 627 File parentDirectory = getFileForPath(config.getDBDirectory()); 628 File backendDirectory = new File(parentDirectory, config.getBackendId()); 629 630 //Make sure the directory either already exists or is able to create. 631 if (!backendDirectory.exists()) 632 { 633 if(!backendDirectory.mkdirs()) 634 { 635 unacceptableReasons.add(ERR_CREATE_FAIL.get(backendDirectory.getPath())); 636 acceptable = false; 637 } 638 else 639 { 640 backendDirectory.delete(); 641 } 642 } 643 //Make sure the directory is valid. 644 else if (!backendDirectory.isDirectory()) 645 { 646 unacceptableReasons.add(ERR_DIRECTORY_INVALID.get(backendDirectory.getPath())); 647 acceptable = false; 648 } 649 650 try 651 { 652 FilePermission newBackendPermission = 653 FilePermission.decodeUNIXMode(cfg.getDBDirectoryPermissions()); 654 655 //Make sure the mode will allow the server itself access to 656 //the database 657 if(!newBackendPermission.isOwnerWritable() || 658 !newBackendPermission.isOwnerReadable() || 659 !newBackendPermission.isOwnerExecutable()) 660 { 661 LocalizableMessage message = ERR_CONFIG_BACKEND_INSANE_MODE.get( 662 cfg.getDBDirectoryPermissions()); 663 unacceptableReasons.add(message); 664 acceptable = false; 665 } 666 } 667 catch(Exception e) 668 { 669 unacceptableReasons.add(ERR_CONFIG_BACKEND_MODE_INVALID.get(cfg.dn())); 670 acceptable = false; 671 } 672 673 try 674 { 675 ConfigurableEnvironment.parseConfigEntry(cfg); 676 } 677 catch (Exception e) 678 { 679 unacceptableReasons.add(LocalizableMessage.raw(e.getLocalizedMessage())); 680 acceptable = false; 681 } 682 683 return acceptable; 684 } 685 686 687 688 /** {@inheritDoc} */ 689 @Override 690 public ConfigChangeResult applyConfigurationChange(LocalDBBackendCfg cfg) 691 { 692 final ConfigChangeResult ccr = new ConfigChangeResult(); 693 694 try 695 { 696 if(env != null) 697 { 698 // Check if any JE non-mutable properties were changed. 699 EnvironmentConfig oldEnvConfig = env.getConfig(); 700 EnvironmentConfig newEnvConfig = 701 ConfigurableEnvironment.parseConfigEntry(cfg); 702 Map<?,?> paramsMap = EnvironmentParams.SUPPORTED_PARAMS; 703 704 // Iterate through native JE properties. 705 SortedSet<String> jeProperties = cfg.getJEProperty(); 706 for (String jeEntry : jeProperties) { 707 // There is no need to validate properties yet again. 708 StringTokenizer st = new StringTokenizer(jeEntry, "="); 709 if (st.countTokens() == 2) { 710 String jePropertyName = st.nextToken(); 711 String jePropertyValue = st.nextToken(); 712 ConfigParam param = (ConfigParam) paramsMap.get(jePropertyName); 713 if (!param.isMutable()) { 714 String oldValue = oldEnvConfig.getConfigParam(param.getName()); 715 if (!oldValue.equalsIgnoreCase(jePropertyValue)) { 716 ccr.setAdminActionRequired(true); 717 ccr.addMessage(INFO_CONFIG_JE_PROPERTY_REQUIRES_RESTART.get(jePropertyName)); 718 if(logger.isTraceEnabled()) { 719 logger.trace("The change to the following property " + 720 "will take effect when the component is restarted: " + 721 jePropertyName); 722 } 723 } 724 } 725 } 726 } 727 728 // Iterate through JE configuration attributes. 729 for (Object o : paramsMap.values()) 730 { 731 ConfigParam param = (ConfigParam) o; 732 if (!param.isMutable()) 733 { 734 String oldValue = oldEnvConfig.getConfigParam(param.getName()); 735 String newValue = newEnvConfig.getConfigParam(param.getName()); 736 if (!oldValue.equalsIgnoreCase(newValue)) 737 { 738 ccr.setAdminActionRequired(true); 739 String configAttr = ConfigurableEnvironment. 740 getAttributeForProperty(param.getName()); 741 if (configAttr != null) 742 { 743 ccr.addMessage(NOTE_JEB_CONFIG_ATTR_REQUIRES_RESTART.get(configAttr)); 744 } 745 else 746 { 747 ccr.addMessage(NOTE_JEB_CONFIG_ATTR_REQUIRES_RESTART.get(param.getName())); 748 } 749 if(logger.isTraceEnabled()) 750 { 751 logger.trace("The change to the following property will " + 752 "take effect when the backend is restarted: " + 753 param.getName()); 754 } 755 } 756 } 757 } 758 759 // This takes care of changes to the JE environment for those 760 // properties that are mutable at runtime. 761 env.setMutableConfig(newEnvConfig); 762 763 logger.trace("JE database configuration: %s", env.getConfig()); 764 } 765 766 // Create the directory if it doesn't exist. 767 if(!cfg.getDBDirectory().equals(this.config.getDBDirectory())) 768 { 769 File parentDirectory = getFileForPath(cfg.getDBDirectory()); 770 File backendDirectory = 771 new File(parentDirectory, cfg.getBackendId()); 772 773 if (!backendDirectory.exists()) 774 { 775 if(!backendDirectory.mkdirs()) 776 { 777 ccr.addMessage(ERR_CREATE_FAIL.get(backendDirectory.getPath())); 778 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 779 return ccr; 780 } 781 } 782 //Make sure the directory is valid. 783 else if (!backendDirectory.isDirectory()) 784 { 785 ccr.addMessage(ERR_DIRECTORY_INVALID.get(backendDirectory.getPath())); 786 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 787 return ccr; 788 } 789 790 ccr.setAdminActionRequired(true); 791 ccr.addMessage(NOTE_CONFIG_DB_DIR_REQUIRES_RESTART.get(this.config.getDBDirectory(), cfg.getDBDirectory())); 792 } 793 794 if(!cfg.getDBDirectoryPermissions().equalsIgnoreCase( 795 config.getDBDirectoryPermissions()) || 796 !cfg.getDBDirectory().equals(this.config.getDBDirectory())) 797 { 798 FilePermission backendPermission; 799 try 800 { 801 backendPermission = 802 FilePermission.decodeUNIXMode(cfg.getDBDirectoryPermissions()); 803 } 804 catch(Exception e) 805 { 806 ccr.addMessage(ERR_CONFIG_BACKEND_MODE_INVALID.get(config.dn())); 807 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 808 return ccr; 809 } 810 811 //Make sure the mode will allow the server itself access to 812 //the database 813 if(!backendPermission.isOwnerWritable() || 814 !backendPermission.isOwnerReadable() || 815 !backendPermission.isOwnerExecutable()) 816 { 817 ccr.addMessage(ERR_CONFIG_BACKEND_INSANE_MODE.get( 818 cfg.getDBDirectoryPermissions())); 819 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 820 return ccr; 821 } 822 823 // Get the backend database backendDirectory permissions and apply 824 File parentDirectory = getFileForPath(config.getDBDirectory()); 825 File backendDirectory = new File(parentDirectory, config.getBackendId()); 826 try 827 { 828 if (!FilePermission.setPermissions(backendDirectory, backendPermission)) 829 { 830 logger.warn(WARN_UNABLE_SET_PERMISSIONS, backendPermission, backendDirectory); 831 } 832 } 833 catch(Exception e) 834 { 835 // Log an warning that the permissions were not set. 836 logger.warn(WARN_SET_PERMISSIONS_FAILED, backendDirectory, e); 837 } 838 } 839 840 getMonitorProvider().enableFilterUseStats( 841 cfg.isIndexFilterAnalyzerEnabled()); 842 getMonitorProvider() 843 .setMaxEntries(cfg.getIndexFilterAnalyzerMaxFilters()); 844 845 this.config = cfg; 846 } 847 catch (Exception e) 848 { 849 ccr.addMessage(LocalizableMessage.raw(stackTraceToSingleLineString(e))); 850 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 851 return ccr; 852 } 853 854 return ccr; 855 } 856 857 /** 858 * Returns whether this container JE database environment is 859 * open, valid and can be used. 860 * 861 * @return {@code true} if valid, or {@code false} otherwise. 862 */ 863 public boolean isValid() { 864 return env.isValid(); 865 } 866}