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 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2017 ForgeRock AS. 016 */ 017package org.opends.server.core; 018 019import static org.forgerock.opendj.ldap.ResultCode.*; 020import static org.opends.messages.ConfigMessages.*; 021import static org.opends.server.core.DirectoryServer.*; 022import static org.opends.server.util.StaticUtils.*; 023 024import java.util.Collection; 025import java.util.Iterator; 026import java.util.LinkedHashSet; 027import java.util.List; 028import java.util.Set; 029import java.util.concurrent.ConcurrentHashMap; 030 031import org.forgerock.i18n.LocalizableMessage; 032import org.forgerock.i18n.slf4j.LocalizedLogger; 033import org.forgerock.opendj.config.server.ConfigChangeResult; 034import org.forgerock.opendj.config.server.ConfigException; 035import org.forgerock.opendj.config.server.ConfigurationAddListener; 036import org.forgerock.opendj.config.server.ConfigurationChangeListener; 037import org.forgerock.opendj.config.server.ConfigurationDeleteListener; 038import org.forgerock.opendj.ldap.DN; 039import org.forgerock.opendj.ldap.ResultCode; 040import org.forgerock.opendj.server.config.meta.BackendCfgDefn; 041import org.forgerock.opendj.server.config.server.BackendCfg; 042import org.forgerock.opendj.server.config.server.RootCfg; 043import org.opends.server.api.Backend; 044import org.opends.server.api.BackendInitializationListener; 045import org.opends.server.backends.ConfigurationBackend; 046import org.opends.server.config.ConfigConstants; 047import org.opends.server.types.DirectoryException; 048import org.opends.server.types.Entry; 049import org.opends.server.types.InitializationException; 050import org.opends.server.types.WritabilityMode; 051 052/** 053 * This class defines a utility that will be used to manage the configuration 054 * for the set of backends defined in the Directory Server. It will perform 055 * the necessary initialization of those backends when the server is first 056 * started, and then will manage any changes to them while the server is 057 * running. 058 */ 059public class BackendConfigManager implements 060 ConfigurationChangeListener<BackendCfg>, 061 ConfigurationAddListener<BackendCfg>, 062 ConfigurationDeleteListener<BackendCfg> 063{ 064 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 065 066 /** The mapping between configuration entry DNs and their corresponding backend implementations. */ 067 private final ConcurrentHashMap<DN, Backend<? extends BackendCfg>> registeredBackends = new ConcurrentHashMap<>(); 068 private final ServerContext serverContext; 069 070 /** 071 * Creates a new instance of this backend config manager. 072 * 073 * @param serverContext 074 * The server context. 075 */ 076 public BackendConfigManager(ServerContext serverContext) 077 { 078 this.serverContext = serverContext; 079 } 080 081 /** 082 * Initializes the configuration associated with the Directory Server 083 * backends. This should only be called at Directory Server startup. 084 * 085 * @param backendIDsToStart 086 * The list of backendID to start. Everything will be started if empty. 087 * @throws ConfigException 088 * If a critical configuration problem prevents the backend 089 * initialization from succeeding. 090 * @throws InitializationException 091 * If a problem occurs while initializing the backends that is not 092 * related to the server configuration. 093 */ 094 public void initializeBackendConfig(Collection<String> backendIDsToStart) 095 throws ConfigException, InitializationException 096 { 097 initializeConfigurationBackend(); 098 099 // Register add and delete listeners. 100 RootCfg root = serverContext.getRootConfig(); 101 root.addBackendAddListener(this); 102 root.addBackendDeleteListener(this); 103 104 // Get the configuration entry that is at the root of all the backends in 105 // the server. 106 Entry backendRoot; 107 try 108 { 109 DN configEntryDN = DN.valueOf(ConfigConstants.DN_BACKEND_BASE); 110 backendRoot = DirectoryServer.getConfigEntry(configEntryDN); 111 } 112 catch (Exception e) 113 { 114 logger.traceException(e); 115 116 LocalizableMessage message = 117 ERR_CONFIG_BACKEND_CANNOT_GET_CONFIG_BASE.get(getExceptionMessage(e)); 118 throw new ConfigException(message, e); 119 } 120 121 122 // If the configuration root entry is null, then assume it doesn't exist. 123 // In that case, then fail. At least that entry must exist in the 124 // configuration, even if there are no backends defined below it. 125 if (backendRoot == null) 126 { 127 throw new ConfigException(ERR_CONFIG_BACKEND_BASE_DOES_NOT_EXIST.get()); 128 } 129 initializeBackends(backendIDsToStart, root); 130 } 131 132 /** 133 * Initializes specified backends. If a backend has been already initialized, do nothing. 134 * This should only be called at Directory Server startup, after #initializeBackendConfig() 135 * 136 * @param backendIDsToStart 137 * The list of backendID to start. Everything will be started if empty. 138 * @param root 139 * The configuration of the server's Root backend 140 * @throws ConfigException 141 * If a critical configuration problem prevents the backend 142 * initialization from succeeding. 143 */ 144 public void initializeBackends(Collection<String> backendIDsToStart, RootCfg root) throws ConfigException 145 { 146 // Initialize existing backends. 147 for (String name : root.listBackends()) 148 { 149 // Get the handler's configuration. 150 // This will decode and validate its properties. 151 final BackendCfg backendCfg = root.getBackend(name); 152 final String backendID = backendCfg.getBackendId(); 153 if (!backendIDsToStart.isEmpty() && !backendIDsToStart.contains(backendID)) 154 { 155 continue; 156 } 157 if (DirectoryServer.hasBackend(backendID)) 158 { 159 // Skip this backend if it is already initialized and registered as available. 160 continue; 161 } 162 163 // Register as a change listener for this backend so that we can be 164 // notified when it is disabled or enabled. 165 backendCfg.addChangeListener(this); 166 167 final DN backendDN = backendCfg.dn(); 168 if (!backendCfg.isEnabled()) 169 { 170 logger.debug(INFO_CONFIG_BACKEND_DISABLED, backendDN); 171 continue; 172 } 173 174 // See if the entry contains an attribute that specifies the class name 175 // for the backend implementation. If it does, then load it and make 176 // sure that it's a valid backend implementation. There is no such 177 // attribute, the specified class cannot be loaded, or it does not 178 // contain a valid backend implementation, then log an error and skip it. 179 String className = backendCfg.getJavaClass(); 180 181 Backend<? extends BackendCfg> backend; 182 try 183 { 184 backend = loadBackendClass(className).newInstance(); 185 } 186 catch (Exception e) 187 { 188 logger.traceException(e); 189 logger.error(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE, className, backendDN, stackTraceToSingleLineString(e)); 190 continue; 191 } 192 193 initializeBackend(backend, backendCfg); 194 } 195 } 196 197 private void initializeConfigurationBackend() throws InitializationException 198 { 199 final ConfigurationBackend configBackend = 200 new ConfigurationBackend(serverContext, DirectoryServer.getConfigurationHandler()); 201 initializeBackend(configBackend, configBackend.getBackendCfg()); 202 } 203 204 private void initializeBackend(Backend<? extends BackendCfg> backend, BackendCfg backendCfg) 205 { 206 ConfigChangeResult ccr = new ConfigChangeResult(); 207 initializeBackend(backend, backendCfg, ccr); 208 for (LocalizableMessage msg : ccr.getMessages()) 209 { 210 logger.error(msg); 211 } 212 } 213 214 private void initializeBackend(Backend<? extends BackendCfg> backend, BackendCfg backendCfg, ConfigChangeResult ccr) 215 { 216 backend.setBackendID(backendCfg.getBackendId()); 217 backend.setWritabilityMode(toWritabilityMode(backendCfg.getWritabilityMode())); 218 219 if (acquireSharedLock(backend, backendCfg.getBackendId(), ccr) && configureAndOpenBackend(backend, backendCfg, ccr)) 220 { 221 registerBackend(backend, backendCfg, ccr); 222 } 223 } 224 225 /** 226 * Acquire a shared lock on this backend. This will prevent operations like LDIF import or restore 227 * from occurring while the backend is active. 228 */ 229 private boolean acquireSharedLock(Backend<?> backend, String backendID, final ConfigChangeResult ccr) 230 { 231 try 232 { 233 String lockFile = LockFileManager.getBackendLockFileName(backend); 234 StringBuilder failureReason = new StringBuilder(); 235 if (!LockFileManager.acquireSharedLock(lockFile, failureReason)) 236 { 237 cannotAcquireLock(backendID, ccr, failureReason); 238 return false; 239 } 240 return true; 241 } 242 catch (Exception e) 243 { 244 logger.traceException(e); 245 246 cannotAcquireLock(backendID, ccr, stackTraceToSingleLineString(e)); 247 return false; 248 } 249 } 250 251 private void cannotAcquireLock(String backendID, final ConfigChangeResult ccr, CharSequence failureReason) 252 { 253 LocalizableMessage message = ERR_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK.get(backendID, failureReason); 254 logger.error(message); 255 256 // FIXME -- Do we need to send an admin alert? 257 ccr.setResultCode(ResultCode.CONSTRAINT_VIOLATION); 258 ccr.setAdminActionRequired(true); 259 ccr.addMessage(message); 260 } 261 262 private void releaseSharedLock(Backend<?> backend, String backendID) 263 { 264 try 265 { 266 String lockFile = LockFileManager.getBackendLockFileName(backend); 267 StringBuilder failureReason = new StringBuilder(); 268 if (! LockFileManager.releaseLock(lockFile, failureReason)) 269 { 270 logger.warn(WARN_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK, backendID, failureReason); 271 // FIXME -- Do we need to send an admin alert? 272 } 273 } 274 catch (Exception e2) 275 { 276 logger.traceException(e2); 277 278 logger.warn(WARN_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK, backendID, stackTraceToSingleLineString(e2)); 279 // FIXME -- Do we need to send an admin alert? 280 } 281 } 282 283 @Override 284 public boolean isConfigurationChangeAcceptable( 285 BackendCfg configEntry, 286 List<LocalizableMessage> unacceptableReason) 287 { 288 DN backendDN = configEntry.dn(); 289 290 291 Set<DN> baseDNs = configEntry.getBaseDN(); 292 293 // See if the backend is registered with the server. If it is, then 294 // see what's changed and whether those changes are acceptable. 295 Backend<?> backend = registeredBackends.get(backendDN); 296 if (backend != null) 297 { 298 LinkedHashSet<DN> removedDNs = new LinkedHashSet<>(backend.getBaseDNs()); 299 LinkedHashSet<DN> addedDNs = new LinkedHashSet<>(baseDNs); 300 Iterator<DN> iterator = removedDNs.iterator(); 301 while (iterator.hasNext()) 302 { 303 DN dn = iterator.next(); 304 if (addedDNs.remove(dn)) 305 { 306 iterator.remove(); 307 } 308 } 309 310 // Copy the directory server's base DN registry and make the 311 // requested changes to see if it complains. 312 BaseDnRegistry reg = DirectoryServer.copyBaseDnRegistry(); 313 for (DN dn : removedDNs) 314 { 315 try 316 { 317 reg.deregisterBaseDN(dn); 318 } 319 catch (DirectoryException de) 320 { 321 logger.traceException(de); 322 323 unacceptableReason.add(de.getMessageObject()); 324 return false; 325 } 326 } 327 328 for (DN dn : addedDNs) 329 { 330 try 331 { 332 reg.registerBaseDN(dn, backend, false); 333 } 334 catch (DirectoryException de) 335 { 336 logger.traceException(de); 337 338 unacceptableReason.add(de.getMessageObject()); 339 return false; 340 } 341 } 342 } 343 else if (configEntry.isEnabled()) 344 { 345 /* 346 * If the backend was not enabled, it has not been registered with directory server, so 347 * no listeners will be registered at the lower layers. Verify as it was an add. 348 */ 349 String className = configEntry.getJavaClass(); 350 try 351 { 352 Class<Backend<BackendCfg>> backendClass = loadBackendClass(className); 353 if (! Backend.class.isAssignableFrom(backendClass)) 354 { 355 unacceptableReason.add(ERR_CONFIG_BACKEND_CLASS_NOT_BACKEND.get(className, backendDN)); 356 return false; 357 } 358 359 Backend<BackendCfg> b = backendClass.newInstance(); 360 if (! b.isConfigurationAcceptable(configEntry, unacceptableReason, serverContext)) 361 { 362 return false; 363 } 364 } 365 catch (Exception e) 366 { 367 logger.traceException(e); 368 unacceptableReason.add( 369 ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE.get(className, backendDN, stackTraceToSingleLineString(e))); 370 return false; 371 } 372 } 373 374 // If we've gotten to this point, then it is acceptable as far as we are 375 // concerned. If it is unacceptable according to the configuration for that 376 // backend, then the backend itself will need to make that determination. 377 return true; 378 } 379 380 @Override 381 public ConfigChangeResult applyConfigurationChange(BackendCfg cfg) 382 { 383 DN backendDN = cfg.dn(); 384 Backend<? extends BackendCfg> backend = registeredBackends.get(backendDN); 385 final ConfigChangeResult ccr = new ConfigChangeResult(); 386 387 // See if the entry contains an attribute that indicates whether the 388 // backend should be enabled. 389 boolean needToEnable = false; 390 try 391 { 392 if (cfg.isEnabled()) 393 { 394 // The backend is marked as enabled. See if that is already true. 395 if (backend == null) 396 { 397 needToEnable = true; 398 } // else already enabled, no need to do anything. 399 } 400 else 401 { 402 // The backend is marked as disabled. See if that is already true. 403 if (backend != null) 404 { 405 // It isn't disabled, so we will do so now and deregister it from the 406 // Directory Server. 407 deregisterBackend(backendDN, backend); 408 409 backend.finalizeBackend(); 410 411 releaseSharedLock(backend, backend.getBackendID()); 412 413 return ccr; 414 } // else already disabled, no need to do anything. 415 } 416 } 417 catch (Exception e) 418 { 419 logger.traceException(e); 420 421 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 422 ccr.addMessage(ERR_CONFIG_BACKEND_UNABLE_TO_DETERMINE_ENABLED_STATE.get(backendDN, 423 stackTraceToSingleLineString(e))); 424 return ccr; 425 } 426 427 // See if the entry contains an attribute that specifies the class name 428 // for the backend implementation. If it does, then load it and make sure 429 // that it's a valid backend implementation. There is no such attribute, 430 // the specified class cannot be loaded, or it does not contain a valid 431 // backend implementation, then log an error and skip it. 432 String className = cfg.getJavaClass(); 433 434 // See if this backend is currently active and if so if the name of the class is the same. 435 if (backend != null && !className.equals(backend.getClass().getName())) 436 { 437 // It is not the same. Try to load it and see if it is a valid backend implementation. 438 try 439 { 440 Class<?> backendClass = DirectoryServer.loadClass(className); 441 if (Backend.class.isAssignableFrom(backendClass)) 442 { 443 // It appears to be a valid backend class. We'll return that the 444 // change is successful, but indicate that some administrative 445 // action is required. 446 ccr.addMessage(NOTE_CONFIG_BACKEND_ACTION_REQUIRED_TO_CHANGE_CLASS.get( 447 backendDN, backend.getClass().getName(), className)); 448 ccr.setAdminActionRequired(true); 449 } 450 else 451 { 452 // It is not a valid backend class. This is an error. 453 ccr.setResultCode(ResultCode.CONSTRAINT_VIOLATION); 454 ccr.addMessage(ERR_CONFIG_BACKEND_CLASS_NOT_BACKEND.get(className, backendDN)); 455 } 456 return ccr; 457 } 458 catch (Exception e) 459 { 460 logger.traceException(e); 461 462 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 463 ccr.addMessage(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE.get( 464 className, backendDN, stackTraceToSingleLineString(e))); 465 return ccr; 466 } 467 } 468 469 470 // If we've gotten here, then that should mean that we need to enable the 471 // backend. Try to do so. 472 if (needToEnable) 473 { 474 try 475 { 476 backend = loadBackendClass(className).newInstance(); 477 } 478 catch (Exception e) 479 { 480 // It is not a valid backend class. This is an error. 481 ccr.setResultCode(ResultCode.CONSTRAINT_VIOLATION); 482 ccr.addMessage(ERR_CONFIG_BACKEND_CLASS_NOT_BACKEND.get(className, backendDN)); 483 return ccr; 484 } 485 486 initializeBackend(backend, cfg, ccr); 487 return ccr; 488 } 489 else if (ccr.getResultCode() == ResultCode.SUCCESS && backend != null) 490 { 491 backend.setWritabilityMode(toWritabilityMode(cfg.getWritabilityMode())); 492 } 493 494 return ccr; 495 } 496 497 private boolean registerBackend(Backend<? extends BackendCfg> backend, BackendCfg backendCfg, ConfigChangeResult ccr) 498 { 499 for (BackendInitializationListener listener : getBackendInitializationListeners()) 500 { 501 listener.performBackendPreInitializationProcessing(backend); 502 } 503 504 try 505 { 506 DirectoryServer.registerBackend(backend); 507 } 508 catch (Exception e) 509 { 510 logger.traceException(e); 511 512 LocalizableMessage message = 513 WARN_CONFIG_BACKEND_CANNOT_REGISTER_BACKEND.get(backendCfg.getBackendId(), getExceptionMessage(e)); 514 logger.error(message); 515 516 // FIXME -- Do we need to send an admin alert? 517 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 518 ccr.addMessage(message); 519 return false; 520 } 521 522 for (BackendInitializationListener listener : getBackendInitializationListeners()) 523 { 524 listener.performBackendPostInitializationProcessing(backend); 525 } 526 527 registeredBackends.put(backendCfg.dn(), backend); 528 return true; 529 } 530 531 @Override 532 public boolean isConfigurationAddAcceptable( 533 BackendCfg configEntry, 534 List<LocalizableMessage> unacceptableReason) 535 { 536 DN backendDN = configEntry.dn(); 537 538 539 // See if the entry contains an attribute that specifies the backend ID. If 540 // it does not, then skip it. 541 String backendID = configEntry.getBackendId(); 542 if (DirectoryServer.hasBackend(backendID)) 543 { 544 unacceptableReason.add(WARN_CONFIG_BACKEND_DUPLICATE_BACKEND_ID.get(backendDN, backendID)); 545 return false; 546 } 547 548 549 // See if the entry contains an attribute that specifies the set of base DNs 550 // for the backend. If it does not, then skip it. 551 Set<DN> baseList = configEntry.getBaseDN(); 552 DN[] baseDNs = new DN[baseList.size()]; 553 baseList.toArray(baseDNs); 554 555 556 // See if the entry contains an attribute that specifies the class name 557 // for the backend implementation. If it does, then load it and make sure 558 // that it's a valid backend implementation. There is no such attribute, 559 // the specified class cannot be loaded, or it does not contain a valid 560 // backend implementation, then log an error and skip it. 561 String className = configEntry.getJavaClass(); 562 563 Backend<BackendCfg> backend; 564 try 565 { 566 backend = loadBackendClass(className).newInstance(); 567 } 568 catch (Exception e) 569 { 570 logger.traceException(e); 571 572 unacceptableReason.add(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE.get( 573 className, backendDN, stackTraceToSingleLineString(e))); 574 return false; 575 } 576 577 578 // Make sure that all of the base DNs are acceptable for use in the server. 579 BaseDnRegistry reg = DirectoryServer.copyBaseDnRegistry(); 580 for (DN baseDN : baseDNs) 581 { 582 if (baseDN.isRootDN()) 583 { 584 unacceptableReason.add(ERR_CONFIG_BACKEND_BASE_IS_EMPTY.get(backendDN)); 585 return false; 586 } 587 try 588 { 589 reg.registerBaseDN(baseDN, backend, false); 590 } 591 catch (DirectoryException de) 592 { 593 unacceptableReason.add(de.getMessageObject()); 594 return false; 595 } 596 catch (Exception e) 597 { 598 unacceptableReason.add(getExceptionMessage(e)); 599 return false; 600 } 601 } 602 603 return backend.isConfigurationAcceptable(configEntry, unacceptableReason, serverContext); 604 } 605 606 @Override 607 public ConfigChangeResult applyConfigurationAdd(BackendCfg cfg) 608 { 609 DN backendDN = cfg.dn(); 610 final ConfigChangeResult ccr = new ConfigChangeResult(); 611 612 // Register as a change listener for this backend entry so that we will 613 // be notified of any changes that may be made to it. 614 cfg.addChangeListener(this); 615 616 // See if the entry contains an attribute that indicates whether the backend should be enabled. 617 // If it does not, or if it is not set to "true", then skip it. 618 if (!cfg.isEnabled()) 619 { 620 // The backend is explicitly disabled. We will log a message to 621 // indicate that it won't be enabled and return. 622 LocalizableMessage message = INFO_CONFIG_BACKEND_DISABLED.get(backendDN); 623 logger.debug(message); 624 ccr.addMessage(message); 625 return ccr; 626 } 627 628 629 630 // See if the entry contains an attribute that specifies the backend ID. If 631 // it does not, then skip it. 632 String backendID = cfg.getBackendId(); 633 if (DirectoryServer.hasBackend(backendID)) 634 { 635 LocalizableMessage message = WARN_CONFIG_BACKEND_DUPLICATE_BACKEND_ID.get(backendDN, backendID); 636 logger.warn(message); 637 ccr.addMessage(message); 638 return ccr; 639 } 640 641 642 // See if the entry contains an attribute that specifies the class name 643 // for the backend implementation. If it does, then load it and make sure 644 // that it's a valid backend implementation. There is no such attribute, 645 // the specified class cannot be loaded, or it does not contain a valid 646 // backend implementation, then log an error and skip it. 647 String className = cfg.getJavaClass(); 648 649 Backend<? extends BackendCfg> backend; 650 try 651 { 652 backend = loadBackendClass(className).newInstance(); 653 } 654 catch (Exception e) 655 { 656 logger.traceException(e); 657 658 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 659 ccr.addMessage(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE.get( 660 className, backendDN, stackTraceToSingleLineString(e))); 661 return ccr; 662 } 663 664 initializeBackend(backend, cfg, ccr); 665 return ccr; 666 } 667 668 private boolean configureAndOpenBackend(Backend<?> backend, BackendCfg cfg, ConfigChangeResult ccr) 669 { 670 try 671 { 672 configureAndOpenBackend(backend, cfg); 673 return true; 674 } 675 catch (Exception e) 676 { 677 logger.traceException(e); 678 679 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 680 ccr.addMessage(ERR_CONFIG_BACKEND_CANNOT_INITIALIZE.get( 681 cfg.getJavaClass(), cfg.dn(), stackTraceToSingleLineString(e))); 682 683 releaseSharedLock(backend, cfg.getBackendId()); 684 return false; 685 } 686 } 687 688 @SuppressWarnings({ "unchecked", "rawtypes" }) 689 private void configureAndOpenBackend(Backend backend, BackendCfg cfg) throws ConfigException, InitializationException 690 { 691 backend.configureBackend(cfg, serverContext); 692 backend.openBackend(); 693 } 694 695 @SuppressWarnings("unchecked") 696 private Class<Backend<BackendCfg>> loadBackendClass(String className) throws Exception 697 { 698 return (Class<Backend<BackendCfg>>) DirectoryServer.loadClass(className); 699 } 700 701 private WritabilityMode toWritabilityMode(BackendCfgDefn.WritabilityMode writabilityMode) 702 { 703 switch (writabilityMode) 704 { 705 case DISABLED: 706 return WritabilityMode.DISABLED; 707 case ENABLED: 708 return WritabilityMode.ENABLED; 709 case INTERNAL_ONLY: 710 return WritabilityMode.INTERNAL_ONLY; 711 default: 712 return WritabilityMode.ENABLED; 713 } 714 } 715 716 @Override 717 public boolean isConfigurationDeleteAcceptable( 718 BackendCfg configEntry, 719 List<LocalizableMessage> unacceptableReason) 720 { 721 DN backendDN = configEntry.dn(); 722 723 724 // See if this backend config manager has a backend registered with the 725 // provided DN. If not, then we don't care if the entry is deleted. If we 726 // do know about it, then that means that it is enabled and we will not 727 // allow removing a backend that is enabled. 728 Backend<?> backend = registeredBackends.get(backendDN); 729 if (backend == null) 730 { 731 return true; 732 } 733 734 735 // See if the backend has any subordinate backends. If so, then it is not 736 // acceptable to remove it. Otherwise, it should be fine. 737 Backend<?>[] subBackends = backend.getSubordinateBackends(); 738 if (subBackends != null && subBackends.length != 0) 739 { 740 unacceptableReason.add(NOTE_CONFIG_BACKEND_CANNOT_REMOVE_BACKEND_WITH_SUBORDINATES.get(backendDN)); 741 return false; 742 } 743 return true; 744 } 745 746 @Override 747 public ConfigChangeResult applyConfigurationDelete(BackendCfg configEntry) 748 { 749 DN backendDN = configEntry.dn(); 750 final ConfigChangeResult ccr = new ConfigChangeResult(); 751 752 // See if this backend config manager has a backend registered with the 753 // provided DN. If not, then we don't care if the entry is deleted. 754 Backend<?> backend = registeredBackends.get(backendDN); 755 if (backend == null) 756 { 757 return ccr; 758 } 759 760 // See if the backend has any subordinate backends. If so, then it is not 761 // acceptable to remove it. Otherwise, it should be fine. 762 Backend<?>[] subBackends = backend.getSubordinateBackends(); 763 if (subBackends != null && subBackends.length > 0) 764 { 765 ccr.setResultCode(UNWILLING_TO_PERFORM); 766 ccr.addMessage(NOTE_CONFIG_BACKEND_CANNOT_REMOVE_BACKEND_WITH_SUBORDINATES.get(backendDN)); 767 return ccr; 768 } 769 770 deregisterBackend(backendDN, backend); 771 772 try 773 { 774 backend.finalizeBackend(); 775 } 776 catch (Exception e) 777 { 778 logger.traceException(e); 779 } 780 781 configEntry.removeChangeListener(this); 782 783 releaseSharedLock(backend, backend.getBackendID()); 784 785 return ccr; 786 } 787 788 private void deregisterBackend(DN backendDN, Backend<?> backend) 789 { 790 for (BackendInitializationListener listener : getBackendInitializationListeners()) 791 { 792 listener.performBackendPreFinalizationProcessing(backend); 793 } 794 795 registeredBackends.remove(backendDN); 796 DirectoryServer.deregisterBackend(backend); 797 798 for (BackendInitializationListener listener : getBackendInitializationListeners()) 799 { 800 listener.performBackendPostFinalizationProcessing(backend); 801 } 802 } 803}