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