BackendImpl.java revision 7ccaa46b4a749896b2daabda390d8ddd3ae7743f
/*
* 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
* 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
*/
/**
* This is an implementation of a Directory Server Backend which stores entries
* locally in a Berkeley DB JE database.
*/
{
/** The configuration of this JE backend. */
private LocalDBBackendCfg cfg;
/** The root JE container to use for this backend. */
private RootContainer rootContainer;
/** A count of the total operation threads currently in the backend. */
/** A count of the write operation threads currently in the backend. */
/** The base DNs defined for this backend instance. */
private MonitorProvider<?> rootContainerMonitor;
private DiskSpaceMonitor diskMonitor;
/**
* The controls supported by this backend.
*/
/** Begin a Backend API method that reads the database. */
private void readerBegin()
{
}
/** End a Backend API method that reads the database. */
private void readerEnd()
{
}
/** Begin a Backend API method that writes the database. */
private void writerBegin()
{
}
/** End a Backend API method that writes the database. */
private void writerEnd()
{
}
/**
* Wait until there are no more threads accessing the database. It is assumed
* that new threads have been prevented from entering the database at the time
* this method is called.
*/
private void waitUntilQuiescent()
{
{
// Still have threads in the database so sleep a little
try
{
}
catch (InterruptedException e)
{
logger.traceException(e);
}
}
}
/** {@inheritDoc} */
public void configureBackend(LocalDBBackendCfg cfg, ServerContext serverContext) throws ConfigException
{
}
/** {@inheritDoc} */
public void openBackend()
{
if (mustOpenRootContainer())
{
}
// Preload the database cache.
try
{
// Log an informational message about the number of entries.
}
{
throw new InitializationException(
}
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
}
// Register a monitor provider for the environment.
// Register as disk space monitor handler
cfg.getDiskFullThreshold(), this);
//Register as an AlertGenerator.
// Register this backend as a change listener.
cfg.addLocalDBChangeListener(this);
}
private File getDirectory()
{
}
/** {@inheritDoc} */
public void closeBackend()
{
cfg.removeLocalDBChangeListener(this);
// Deregister our base DNs.
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
}
// 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.
// Close the database.
try
{
}
catch (DatabaseException e)
{
logger.traceException(e);
}
// Make sure the thread counts are zero for next initialization.
// Log an informational message.
}
/** {@inheritDoc} */
{
try
{
{
return false;
}
switch (indexType)
{
case PRESENCE:
case EQUALITY:
case SUBSTRING:
case SUBINITIAL:
case SUBANY:
case SUBFINAL:
case GREATER_OR_EQUAL:
case LESS_OR_EQUAL:
case APPROXIMATE:
default:
return false;
}
}
catch (Exception e)
{
logger.traceException(e);
return false;
}
}
/** {@inheritDoc} */
{
// it supports all the operations so far
return true;
}
/** {@inheritDoc} */
{
return Collections.emptySet();
}
/** {@inheritDoc} */
{
return supportedControls;
}
/** {@inheritDoc} */
public DN[] getBaseDNs()
{
return baseDNs;
}
/** {@inheritDoc} */
public long getEntryCount()
{
if (rootContainer != null)
{
try
{
return rootContainer.getEntryCount();
}
catch (Exception e)
{
logger.traceException(e);
}
}
return -1;
}
/** {@inheritDoc} */
throws DirectoryException
{
if(ret < 0)
{
return ConditionResult.UNDEFINED;
}
}
/** {@inheritDoc} */
throws DirectoryException
{
{
return -1;
}
readerBegin();
try
{
{
// The index entry limit has exceeded and there is no count maintained.
return -1;
}
return count;
}
catch (DatabaseException e)
{
logger.traceException(e);
throw createDirectoryException(e);
}
finally
{
readerEnd();
}
}
/** {@inheritDoc} */
{
readerBegin();
try
{
}
catch (DatabaseException e)
{
logger.traceException(e);
throw createDirectoryException(e);
}
finally
{
readerEnd();
}
return entry;
}
/** {@inheritDoc} */
{
writerBegin();
try
{
}
catch (DatabaseException e)
{
logger.traceException(e);
throw createDirectoryException(e);
}
finally
{
writerEnd();
}
}
/** {@inheritDoc} */
{
writerBegin();
try
{
}
catch (DatabaseException e)
{
logger.traceException(e);
throw createDirectoryException(e);
}
finally
{
writerEnd();
}
}
/** {@inheritDoc} */
{
writerBegin();
try
{
}
catch (DatabaseException e)
{
logger.traceException(e);
throw createDirectoryException(e);
}
finally
{
writerEnd();
}
}
/** {@inheritDoc} */
{
writerBegin();
if (currentContainer != container)
{
// FIXME: No reason why we cannot implement a move between containers
// since the containers share the same database environment.
}
try
{
}
catch (DatabaseException e)
{
logger.traceException(e);
throw createDirectoryException(e);
}
finally
{
writerEnd();
}
}
/** {@inheritDoc} */
{
readerBegin();
try
{
}
catch (DatabaseException e)
{
logger.traceException(e);
throw createDirectoryException(e);
}
finally
{
readerEnd();
}
}
private void checkRootContainerInitialized() throws DirectoryException
{
if (rootContainer == null)
{
}
}
/** {@inheritDoc} */
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)
{
}
}
catch (IOException ioe)
{
}
catch (DatabaseException de)
{
throw createDirectoryException(de);
}
catch (ConfigException ce)
{
}
catch (IdentifiedException e)
{
if (e instanceof DirectoryException)
{
throw (DirectoryException) e;
}
logger.traceException(e);
}
finally
{
}
}
private boolean mustOpenRootContainer()
{
return rootContainer == null;
}
/** {@inheritDoc} */
throws DirectoryException
{
// If the backend already has the root container open, we must use the same
// underlying root container
// If the rootContainer is open, the backend is initialized by something else.
// We can't do import while the backend is online.
if(!openRootContainer)
{
}
try
{
{
// We have the writer lock on the environment, now delete the
// environment and re-open it. Only do this when we are
// importing to all the base DNs in the backend or if the backend only
// have one base DN.
// If the backend does not exist the import will create it.
if (backendDirectory.exists())
{
}
}
}
catch (ExecutionException execEx)
{
{
}
}
catch (InterruptedException intEx)
{
}
catch (JebException je)
{
}
catch (InitializationException ie)
{
}
catch (ConfigException ce)
{
}
finally
{
// leave the backend in the same state.
try
{
if (rootContainer != null)
{
}
// Sync the environment to disk.
}
catch (DatabaseException de)
{
}
}
}
private EnvironmentConfig getEnvConfigForImport()
{
envConfig.setAllowCreate(true);
envConfig.setTransactional(false);
return envConfig;
}
/** {@inheritDoc} */
{
// If the backend already has the root container open, we must use the same
// underlying root container
final boolean openRootContainer = mustOpenRootContainer();
try
{
if (openRootContainer)
{
}
}
catch (DatabaseException e)
{
logger.traceException(e);
throw createDirectoryException(e);
}
catch (JebException e)
{
logger.traceException(e);
e.getMessageObject());
}
finally
{
}
}
/** {@inheritDoc} */
{
// 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.
*/
{
}
try
{
final EnvironmentConfig envConfig;
if (openRootContainer)
{
}
else
{
}
}
catch (ExecutionException execEx)
{
}
catch (InterruptedException intEx)
{
}
catch (ConfigException ce)
{
}
catch (JebException e)
{
logger.traceException(e);
}
catch (InitializationException e)
{
logger.traceException(e);
throw new InitializationException(e.getMessageObject());
}
finally
{
}
}
/**
* 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)
{
{
try
{
}
catch (DatabaseException e)
{
logger.traceException(e);
}
}
}
/** {@inheritDoc} */
{
}
/** {@inheritDoc} */
throws DirectoryException
{
}
/** {@inheritDoc} */
throws DirectoryException
{
}
/** {@inheritDoc} */
{
}
/** {@inheritDoc} */
public boolean isConfigurationChangeAcceptable(
{
// Make sure that the logging level value is acceptable.
try {
return true;
} catch (Exception e) {
return false;
}
}
/** {@inheritDoc} */
{
try
{
if(rootContainer != null)
{
// Check for changes to the base DNs.
{
return failure;
}
}
// Put the new configuration in place.
}
catch (Exception e)
{
}
return ccr;
}
{
diskMonitor.registerMonitoredDirectory(getBackendID(), getDirectory(), newCfg.getDiskLowThreshold(),
newCfg.getDiskFullThreshold(), this);
}
{
{
{
// The base DN was deleted.
}
}
}
{
{
{
try
{
// The base DN was added.
}
catch (Exception e)
{
logger.traceException(e);
return ccr;
}
}
}
return null;
}
/**
* Returns a handle to the JE 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 RootContainer getRootContainer()
{
return rootContainer;
}
/**
* Returns a new read-only handle to the JE 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.
*/
public RootContainer getReadOnlyRootContainer()
{
envConfig.setReadOnly(true);
envConfig.setAllowCreate(false);
envConfig.setTransactional(false);
return initializeRootContainer(envConfig);
}
/**
* Clears all the entries from the backend. This method is for test cases
* that use the JE backend.
*
* @throws ConfigException If an unrecoverable problem arises in the
* process of performing the initialization.
*
* @throws JebException If an error occurs while removing the data.
*/
public void clearBackend()
throws ConfigException, JebException
{
// Determine the backend database directory.
}
/**
* Creates a customized DirectoryException from the DatabaseException thrown
* by JE backend.
*
* @param e The DatabaseException to be converted.
* @return DirectoryException created from exception.
*/
}
}
return new DirectoryException(
}
/** {@inheritDoc} */
public String getClassName() {
return BackendImpl.class.getName();
}
/** {@inheritDoc} */
{
return alerts;
}
/** {@inheritDoc} */
public DN getComponentEntryDN() {
}
throws ConfigException, InitializationException {
// Open the database environment
try {
return rc;
}
catch (DatabaseException e) {
logger.traceException(e);
throw new InitializationException(message, e);
}
}
/** {@inheritDoc} */
public void preloadEntryCache() throws
}
/** {@inheritDoc} */
thresholdInBytes, getBackendID()));
}
/** {@inheritDoc} */
thresholdInBytes, getBackendID()));
}
/** {@inheritDoc} */
public void diskSpaceRestored(File directory, long lowThresholdInBytes, long fullThresholdInBytes) {
}
{
if(storageStatus.isUnusable() ||
{
}
}
}