BackendImpl.java revision 4e47cc1e8ce6f654769127249a363cf0e9a64eec
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2007-2010 Sun Microsystems, Inc.
* Portions Copyright 2013-2015 ForgeRock AS
*/
package org.opends.server.backends.pluggable;
import static org.forgerock.util.Reject.*;
import static org.opends.messages.BackendMessages.*;
import static org.opends.server.core.DirectoryServer.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ConditionResult;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.util.Reject;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.PluggableBackendCfg;
import org.opends.server.api.Backend;
import org.opends.server.api.MonitorProvider;
import org.opends.server.backends.RebuildConfig;
import org.opends.server.backends.VerifyConfig;
import org.opends.server.backends.pluggable.spi.AccessMode;
import org.opends.server.backends.pluggable.spi.Storage;
import org.opends.server.backends.pluggable.spi.StorageInUseException;
import org.opends.server.backends.pluggable.spi.StorageRuntimeException;
import org.opends.server.backends.pluggable.spi.WriteOperation;
import org.opends.server.backends.pluggable.spi.WriteableTransaction;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.ServerContext;
import org.opends.server.types.AttributeType;
import org.opends.server.types.BackupConfig;
import org.opends.server.types.BackupDirectory;
import org.opends.server.types.CanceledOperationException;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.IndexType;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.LDIFImportResult;
import org.opends.server.types.OpenDsException;
import org.opends.server.types.Operation;
import org.opends.server.types.RestoreConfig;
import org.opends.server.util.CollectionUtils;
import org.opends.server.util.LDIFException;
import org.opends.server.util.RuntimeInformation;
import com.forgerock.opendj.util.StaticUtils;
/**
* This is an implementation of a Directory Server Backend which stores entries locally
* in a pluggable storage.
*
* @param <C>
* the type of the BackendCfg for the current backend
*/
public abstract class BackendImpl<C extends PluggableBackendCfg> extends Backend<C> implements
ConfigurationChangeListener<PluggableBackendCfg>
{
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
/** The configuration of this backend. */
private PluggableBackendCfg cfg;
/** The root container to use for this backend. */
private RootContainer rootContainer;
// FIXME: this is broken. Replace with read-write lock.
/** A count of the total operation threads currently in the backend. */
private final AtomicInteger threadTotalCount = new AtomicInteger(0);
/** The base DNs defined for this backend instance. */
private DN[] baseDNs;
private MonitorProvider<?> rootContainerMonitor;
/** The underlying storage engine. */
private Storage storage;
/** The controls supported by this backend. */
private static final Set<String> supportedControls = CollectionUtils.newHashSet(
OID_SUBTREE_DELETE_CONTROL,
OID_PAGED_RESULTS_CONTROL,
OID_MANAGE_DSAIT_CONTROL,
OID_SERVER_SIDE_SORT_REQUEST_CONTROL,
OID_VLV_REQUEST_CONTROL);
/**
* Begin a Backend API method that accesses the {@link EntryContainer} for <code>entryDN</code>
* and returns it.
* @param operation requesting the storage
* @param entryDN the target DN for the operation
* @return <code>EntryContainer</code> where <code>entryDN</code> resides
*/
private EntryContainer accessBegin(Operation operation, DN entryDN) throws DirectoryException
{
checkRootContainerInitialized();
rootContainer.checkForEnoughResources(operation);
EntryContainer ec = rootContainer.getEntryContainer(entryDN);
if (ec == null)
{
throw new DirectoryException(ResultCode.UNDEFINED, ERR_BACKEND_ENTRY_DOESNT_EXIST.get(entryDN, getBackendID()));
}
threadTotalCount.getAndIncrement();
return ec;
}
/** End a Backend API method that accesses the EntryContainer. */
private void accessEnd()
{
threadTotalCount.getAndDecrement();
}
/**
* Wait until there are no more threads accessing the storage. It is assumed
* that new threads have been prevented from entering the storage at the time
* this method is called.
*/
private void waitUntilQuiescent()
{
while (threadTotalCount.get() > 0)
{
// Still have threads accessing the storage so sleep a little
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
logger.traceException(e);
}
}
}
/** {@inheritDoc} */
@Override
public void configureBackend(C cfg, ServerContext serverContext) throws ConfigException
{
Reject.ifNull(cfg, "cfg must not be null");
this.cfg = cfg;
baseDNs = this.cfg.getBaseDN().toArray(new DN[0]);
storage = new TracedStorage(configureStorage(cfg, serverContext), cfg.getBackendId());
}
/** {@inheritDoc} */
@Override
public void openBackend() throws ConfigException, InitializationException
{
if (mustOpenRootContainer())
{
rootContainer = newRootContainer(AccessMode.READ_WRITE);
}
// Preload the tree cache.
rootContainer.preload(cfg.getPreloadTimeLimit());
try
{
// Log an informational message about the number of entries.
logger.info(NOTE_BACKEND_STARTED, cfg.getBackendId(), getEntryCount());
}
catch (StorageRuntimeException e)
{
LocalizableMessage message = WARN_GET_ENTRY_COUNT_FAILED.get(e.getMessage());
throw new InitializationException(message, e);
}
for (DN dn : cfg.getBaseDN())
{
try
{
DirectoryServer.registerBaseDN(dn, this, false);
}
catch (Exception e)
{
throw new InitializationException(ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(dn, e), e);
}
}
// Register a monitor provider for the environment.
rootContainerMonitor = rootContainer.getMonitorProvider();
DirectoryServer.registerMonitorProvider(rootContainerMonitor);
// Register this backend as a change listener.
cfg.addPluggableChangeListener(this);
}
/** {@inheritDoc} */
@Override
public void closeBackend()
{
cfg.removePluggableChangeListener(this);
// Deregister our base DNs.
for (DN dn : rootContainer.getBaseDNs())
{
try
{
DirectoryServer.deregisterBaseDN(dn);
}
catch (Exception e)
{
logger.traceException(e);
}
}
DirectoryServer.deregisterMonitorProvider(rootContainerMonitor);
// We presume the server will prevent more operations coming into this
// backend, but there may be existing operations already in the
// backend. We need to wait for them to finish.
waitUntilQuiescent();
// Close RootContainer and Storage.
try
{
rootContainer.close();
rootContainer = null;
}
catch (StorageRuntimeException e)
{
logger.traceException(e);
logger.error(ERR_DATABASE_EXCEPTION, e.getMessage());
}
// Make sure the thread counts are zero for next initialization.
threadTotalCount.set(0);
// Log an informational message.
logger.info(NOTE_BACKEND_OFFLINE, cfg.getBackendId());
}
/** {@inheritDoc} */
@Override
public boolean isIndexed(AttributeType attributeType, IndexType indexType)
{
try
{
EntryContainer ec = rootContainer.getEntryContainer(baseDNs[0]);
AttributeIndex ai = ec.getAttributeIndex(attributeType);
return ai != null ? ai.isIndexed(indexType) : false;
}
catch (Exception e)
{
logger.traceException(e);
return false;
}
}
/** {@inheritDoc} */
@Override
public boolean supports(BackendOperation backendOperation)
{
switch (backendOperation)
{
case BACKUP:
case RESTORE:
// Responsibility of the underlying storage.
return storage.supportsBackupAndRestore();
default: // INDEXING, LDIF_EXPORT, LDIF_IMPORT
// Responsibility of this pluggable backend.
return true;
}
}
/** {@inheritDoc} */
@Override
public Set<String> getSupportedFeatures()
{
return Collections.emptySet();
}
/** {@inheritDoc} */
@Override
public Set<String> getSupportedControls()
{
return supportedControls;
}
/** {@inheritDoc} */
@Override
public DN[] getBaseDNs()
{
return baseDNs;
}
/** {@inheritDoc} */
@Override
public long getEntryCount()
{
if (rootContainer != null)
{
try
{
return rootContainer.getEntryCount();
}
catch (Exception e)
{
logger.traceException(e);
}
}
return -1;
}
/** {@inheritDoc} */
@Override
public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException
{
EntryContainer container;
try {
container = accessBegin(null, entryDN);
}
catch (DirectoryException de)
{
if (de.getResultCode() == ResultCode.UNDEFINED)
{
return ConditionResult.UNDEFINED;
}
throw de;
}
container.sharedLock.lock();
try
{
return ConditionResult.valueOf(container.hasSubordinates(entryDN));
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
finally
{
container.sharedLock.unlock();
accessEnd();
}
}
/** {@inheritDoc} */
@Override
public long getNumberOfEntriesInBaseDN(DN baseDN) throws DirectoryException
{
checkNotNull(baseDN, "baseDN must not be null");
final EntryContainer ec = accessBegin(null, baseDN);
ec.sharedLock.lock();
try
{
return ec.getNumberOfEntriesInBaseDN();
}
catch (Exception e)
{
throw new DirectoryException(getServerErrorResultCode(), LocalizableMessage.raw(e.getMessage()), e);
}
finally
{
ec.sharedLock.unlock();
accessEnd();
}
}
/** {@inheritDoc} */
@Override
public long getNumberOfChildren(DN parentDN) throws DirectoryException
{
checkNotNull(parentDN, "parentDN must not be null");
EntryContainer ec;
/*
* Only place where we need special handling. Should return -1 instead of an
* error if the EntryContainer is null...
*/
try {
ec = accessBegin(null, parentDN);
}
catch (DirectoryException de)
{
if (de.getResultCode() == ResultCode.UNDEFINED)
{
return -1;
}
throw de;
}
ec.sharedLock.lock();
try
{
return ec.getNumberOfChildren(parentDN);
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
finally
{
ec.sharedLock.unlock();
accessEnd();
}
}
/** {@inheritDoc} */
@Override
public boolean entryExists(final DN entryDN) throws DirectoryException
{
EntryContainer ec = accessBegin(null, entryDN);
ec.sharedLock.lock();
try
{
return ec.entryExists(entryDN);
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
finally
{
ec.sharedLock.unlock();
accessEnd();
}
}
/** {@inheritDoc} */
@Override
public Entry getEntry(DN entryDN) throws DirectoryException
{
EntryContainer ec = accessBegin(null, entryDN);
ec.sharedLock.lock();
try
{
return ec.getEntry(entryDN);
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
finally
{
ec.sharedLock.unlock();
accessEnd();
}
}
/** {@inheritDoc} */
@Override
public void addEntry(Entry entry, AddOperation addOperation) throws DirectoryException, CanceledOperationException
{
EntryContainer ec = accessBegin(addOperation, entry.getName());
ec.sharedLock.lock();
try
{
ec.addEntry(entry, addOperation);
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
finally
{
ec.sharedLock.unlock();
accessEnd();
}
}
/** {@inheritDoc} */
@Override
public void deleteEntry(DN entryDN, DeleteOperation deleteOperation)
throws DirectoryException, CanceledOperationException
{
EntryContainer ec = accessBegin(deleteOperation, entryDN);
ec.sharedLock.lock();
try
{
ec.deleteEntry(entryDN, deleteOperation);
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
finally
{
ec.sharedLock.unlock();
accessEnd();
}
}
/** {@inheritDoc} */
@Override
public void replaceEntry(Entry oldEntry, Entry newEntry, ModifyOperation modifyOperation)
throws DirectoryException, CanceledOperationException
{
EntryContainer ec = accessBegin(modifyOperation, newEntry.getName());
ec.sharedLock.lock();
try
{
ec.replaceEntry(oldEntry, newEntry, modifyOperation);
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
finally
{
ec.sharedLock.unlock();
accessEnd();
}
}
/** {@inheritDoc} */
@Override
public void renameEntry(DN currentDN, Entry entry, ModifyDNOperation modifyDNOperation)
throws DirectoryException, CanceledOperationException
{
EntryContainer currentContainer = accessBegin(modifyDNOperation, currentDN);
EntryContainer container = rootContainer.getEntryContainer(entry.getName());
if (currentContainer != container)
{
accessEnd();
// FIXME: No reason why we cannot implement a move between containers
// since the containers share the same "container"
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, WARN_FUNCTION_NOT_SUPPORTED.get());
}
currentContainer.sharedLock.lock();
try
{
currentContainer.renameEntry(currentDN, entry, modifyDNOperation);
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
finally
{
currentContainer.sharedLock.unlock();
accessEnd();
}
}
/** {@inheritDoc} */
@Override
public void search(SearchOperation searchOperation) throws DirectoryException, CanceledOperationException
{
EntryContainer ec = accessBegin(searchOperation, searchOperation.getBaseDN());
ec.sharedLock.lock();
try
{
ec.search(searchOperation);
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
finally
{
ec.sharedLock.unlock();
accessEnd();
}
}
private void checkRootContainerInitialized() throws DirectoryException
{
if (rootContainer == null)
{
LocalizableMessage msg = ERR_ROOT_CONTAINER_NOT_INITIALIZED.get(getBackendID());
throw new DirectoryException(getServerErrorResultCode(), msg);
}
}
/** {@inheritDoc} */
@Override
public void exportLDIF(LDIFExportConfig exportConfig)
throws DirectoryException
{
// If the backend already has the root container open, we must use the same
// underlying root container
boolean openRootContainer = mustOpenRootContainer();
try
{
if (openRootContainer)
{
rootContainer = getReadOnlyRootContainer();
}
ExportJob exportJob = new ExportJob(exportConfig);
exportJob.exportLDIF(rootContainer);
}
catch (IOException ioe)
{
throw new DirectoryException(getServerErrorResultCode(), ERR_EXPORT_IO_ERROR.get(ioe.getMessage()), ioe);
}
catch (StorageRuntimeException de)
{
throw createDirectoryException(de);
}
catch (ConfigException | InitializationException | LDIFException e)
{
throw new DirectoryException(getServerErrorResultCode(), e.getMessageObject(), e);
}
finally
{
closeTemporaryRootContainer(openRootContainer);
}
}
private boolean mustOpenRootContainer()
{
return rootContainer == null;
}
/** {@inheritDoc} */
@Override
public LDIFImportResult importLDIF(LDIFImportConfig importConfig, ServerContext serverContext)
throws DirectoryException
{
if (importConfig.appendToExistingData() || importConfig.replaceExistingEntries())
{
throw new UnsupportedOperationException("append/replace mode is not supported by this backend.");
}
RuntimeInformation.logInfo();
// If the rootContainer is open, the backend is initialized by something else.
// We can't do import while the backend is online.
if (rootContainer != null)
{
throw new DirectoryException(getServerErrorResultCode(), ERR_IMPORT_BACKEND_ONLINE.get());
}
try
{
try
{
if (importConfig.clearBackend())
{
// clear all files before opening the root container
storage.removeStorageFiles();
}
}
catch (Exception e)
{
throw new DirectoryException(getServerErrorResultCode(), ERR_REMOVE_FAIL.get(e.getMessage()), e);
}
rootContainer = newRootContainer(AccessMode.READ_WRITE);
rootContainer.getStorage().close();
return getImportStrategy(serverContext, rootContainer).importLDIF(importConfig);
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
catch (DirectoryException e)
{
throw e;
}
catch (OpenDsException | ConfigException e)
{
throw new DirectoryException(getServerErrorResultCode(), e.getMessageObject(), e);
}
catch (Exception e)
{
throw new DirectoryException(getServerErrorResultCode(), LocalizableMessage.raw(StaticUtils
.stackTraceToSingleLineString(e, false)), e);
}
finally
{
try
{
if (rootContainer != null)
{
long startTime = System.currentTimeMillis();
rootContainer.close();
long finishTime = System.currentTimeMillis();
long closeTime = (finishTime - startTime) / 1000;
logger.info(NOTE_IMPORT_LDIF_ROOTCONTAINER_CLOSE, closeTime);
rootContainer = null;
}
logger.info(NOTE_IMPORT_CLOSING_DATABASE);
}
catch (StorageRuntimeException de)
{
logger.traceException(de);
}
}
}
private ImportStrategy getImportStrategy(ServerContext serverContext, RootContainer rootContainer)
{
return new OnDiskMergeImporter.StrategyImpl(serverContext, rootContainer, cfg);
}
/** {@inheritDoc} */
@Override
public long verifyBackend(VerifyConfig verifyConfig)
throws InitializationException, ConfigException, DirectoryException
{
// If the backend already has the root container open, we must use the same
// underlying root container
final boolean openRootContainer = mustOpenRootContainer();
try
{
if (openRootContainer)
{
rootContainer = getReadOnlyRootContainer();
}
return new VerifyJob(rootContainer, verifyConfig).verifyBackend();
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
finally
{
closeTemporaryRootContainer(openRootContainer);
}
}
/**
* If a root container was opened in the calling method method as read only,
* close it to leave the backend in the same state.
*/
private void closeTemporaryRootContainer(boolean openRootContainer)
{
if (openRootContainer && rootContainer != null)
{
try
{
rootContainer.close();
rootContainer = null;
}
catch (StorageRuntimeException e)
{
logger.traceException(e);
}
}
}
/** {@inheritDoc} */
@Override
public void rebuildBackend(RebuildConfig rebuildConfig, ServerContext serverContext)
throws InitializationException, ConfigException, DirectoryException
{
// If the backend already has the root container open, we must use the same
// underlying root container
boolean openRootContainer = mustOpenRootContainer();
/*
* If the rootContainer is open, the backend is initialized by something else.
* We can't do any rebuild of system indexes while others are using this backend.
*/
if (!openRootContainer && rebuildConfig.includesSystemIndex())
{
throw new DirectoryException(getServerErrorResultCode(), ERR_REBUILD_BACKEND_ONLINE.get());
}
try
{
if (openRootContainer)
{
rootContainer = newRootContainer(AccessMode.READ_WRITE);
}
getImportStrategy(serverContext, rootContainer).rebuildIndex(rebuildConfig);
}
catch (ExecutionException execEx)
{
throw new DirectoryException(getServerErrorResultCode(), ERR_EXECUTION_ERROR.get(execEx.getMessage()), execEx);
}
catch (InterruptedException intEx)
{
throw new DirectoryException(getServerErrorResultCode(), ERR_INTERRUPTED_ERROR.get(intEx.getMessage()), intEx);
}
catch (ConfigException ce)
{
throw new DirectoryException(getServerErrorResultCode(), ce.getMessageObject(), ce);
}
catch (StorageRuntimeException e)
{
throw createDirectoryException(e);
}
catch (InitializationException e)
{
throw e;
}
catch (Exception ex)
{
throw new DirectoryException(getServerErrorResultCode(), LocalizableMessage.raw(stackTraceToSingleLineString(ex)),
ex);
}
finally
{
closeTemporaryRootContainer(openRootContainer);
}
}
/** {@inheritDoc} */
@Override
public void createBackup(BackupConfig backupConfig) throws DirectoryException
{
storage.createBackup(backupConfig);
}
/** {@inheritDoc} */
@Override
public void removeBackup(BackupDirectory backupDirectory, String backupID)
throws DirectoryException
{
storage.removeBackup(backupDirectory, backupID);
}
/** {@inheritDoc} */
@Override
public void restoreBackup(RestoreConfig restoreConfig) throws DirectoryException
{
storage.restoreBackup(restoreConfig);
}
/**
* Creates the storage engine which will be used by this pluggable backend. Implementations should
* create and configure a new storage engine but not open it.
*
* @param cfg
* the configuration object
* @param serverContext
* this Directory Server intsance's server context
* @return The storage engine to be used by this pluggable backend.
* @throws ConfigException
* If there is an error in the configuration.
*/
protected abstract Storage configureStorage(C cfg, ServerContext serverContext) throws ConfigException;
/** {@inheritDoc} */
@Override
public boolean isConfigurationAcceptable(C config, List<LocalizableMessage> unacceptableReasons,
ServerContext serverContext)
{
return isConfigurationChangeAcceptable(config, unacceptableReasons);
}
/** {@inheritDoc} */
@Override
public boolean isConfigurationChangeAcceptable(PluggableBackendCfg cfg, List<LocalizableMessage> unacceptableReasons)
{
return true;
}
/** {@inheritDoc} */
@Override
public ConfigChangeResult applyConfigurationChange(final PluggableBackendCfg newCfg)
{
final ConfigChangeResult ccr = new ConfigChangeResult();
try
{
if(rootContainer != null)
{
rootContainer.getStorage().write(new WriteOperation()
{
@Override
public void run(WriteableTransaction txn) throws Exception
{
SortedSet<DN> newBaseDNs = newCfg.getBaseDN();
DN[] newBaseDNsArray = newBaseDNs.toArray(new DN[newBaseDNs.size()]);
// Check for changes to the base DNs.
removeDeletedBaseDNs(newBaseDNs, txn);
if (!createNewBaseDNs(newBaseDNsArray, ccr, txn))
{
return;
}
baseDNs = newBaseDNsArray;
// Put the new configuration in place.
cfg = newCfg;
}
});
}
}
catch (Exception e)
{
ccr.setResultCode(getServerErrorResultCode());
ccr.addMessage(LocalizableMessage.raw(stackTraceToSingleLineString(e)));
}
return ccr;
}
private void removeDeletedBaseDNs(SortedSet<DN> newBaseDNs, WriteableTransaction txn) throws DirectoryException
{
for (DN baseDN : cfg.getBaseDN())
{
if (!newBaseDNs.contains(baseDN))
{
// The base DN was deleted.
DirectoryServer.deregisterBaseDN(baseDN);
EntryContainer ec = rootContainer.unregisterEntryContainer(baseDN);
ec.close();
ec.delete(txn);
}
}
}
private boolean createNewBaseDNs(DN[] newBaseDNsArray, ConfigChangeResult ccr, WriteableTransaction txn)
{
for (DN baseDN : newBaseDNsArray)
{
if (!rootContainer.getBaseDNs().contains(baseDN))
{
try
{
// The base DN was added.
EntryContainer ec = rootContainer.openEntryContainer(baseDN, txn, AccessMode.READ_WRITE);
rootContainer.registerEntryContainer(baseDN, ec);
DirectoryServer.registerBaseDN(baseDN, this, false);
}
catch (Exception e)
{
logger.traceException(e);
ccr.setResultCode(getServerErrorResultCode());
ccr.addMessage(ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(baseDN, e));
return false;
}
}
}
return true;
}
/**
* Returns a handle to the root container currently used by this backend.
* The rootContainer could be NULL if the backend is not initialized.
*
* @return The RootContainer object currently used by this backend.
*/
public final RootContainer getRootContainer()
{
return rootContainer;
}
/**
* Returns a new read-only handle to the root container for this backend.
* The caller is responsible for closing the root container after use.
*
* @return The read-only RootContainer object for this backend.
*
* @throws ConfigException If an unrecoverable problem arises during
* initialization.
* @throws InitializationException If a problem occurs during initialization
* that is not related to the server
* configuration.
*/
RootContainer getReadOnlyRootContainer() throws ConfigException, InitializationException
{
return newRootContainer(AccessMode.READ_ONLY);
}
/**
* Creates a customized DirectoryException from the StorageRuntimeException
* thrown by the backend.
*
* @param e
* The StorageRuntimeException to be converted.
* @return DirectoryException created from exception.
*/
private DirectoryException createDirectoryException(StorageRuntimeException e)
{
Throwable cause = e.getCause();
if (cause instanceof OpenDsException)
{
return new DirectoryException(getServerErrorResultCode(), (OpenDsException) cause);
}
else
{
return new DirectoryException(getServerErrorResultCode(), LocalizableMessage.raw(e.getMessage()), e);
}
}
private RootContainer newRootContainer(AccessMode accessMode)
throws ConfigException, InitializationException {
// Open the storage
try {
final RootContainer rc = new RootContainer(getBackendID(), storage, cfg);
rc.open(accessMode);
return rc;
}
catch (StorageInUseException e) {
throw new InitializationException(ERR_VERIFY_BACKEND_ONLINE.get(), e);
}
catch (StorageRuntimeException e)
{
throw new InitializationException(ERR_OPEN_ENV_FAIL.get(e.getMessage()), e);
}
}
}