Refactoring of the JEB backend to simplify the container and entryContainer abstraction. This also elimates exposing the JE interface to backendImpl by creating a new RootContainer class. It provides a higher-level interface to access raw data in JE from anywhere in the server (ie. unit tests).
Fix for issue 727.
1 files deleted
1 files added
15 files modified
| | |
| | | new DatabaseEntry("+".getBytes()); |
| | | |
| | | /** |
| | | * The container in which this attribute index resides. |
| | | * The entryContainer in which this attribute index resides. |
| | | */ |
| | | Container container; |
| | | EntryContainer entryContainer; |
| | | |
| | | /** |
| | | * The attribute index configuration. |
| | |
| | | |
| | | /** |
| | | * Create a new attribute index object. |
| | | * @param entryContainer The entryContainer of this attribute index. |
| | | * @param indexConfig The attribute index configuration. |
| | | * @param container The container of this attribute index. |
| | | */ |
| | | public AttributeIndex(IndexConfig indexConfig, Container container) |
| | | public AttributeIndex(EntryContainer entryContainer, IndexConfig indexConfig) |
| | | { |
| | | this.entryContainer = entryContainer; |
| | | this.indexConfig = indexConfig; |
| | | this.container = container; |
| | | |
| | | AttributeType attrType = indexConfig.getAttributeType(); |
| | | String name = attrType.getNameOrOID(); |
| | |
| | | if (indexConfig.isEqualityIndex()) |
| | | { |
| | | Indexer equalityIndexer = new EqualityIndexer(indexConfig); |
| | | this.equalityIndex = new Index(container, name + ".equality", |
| | | this.equalityIndex = new Index(this.entryContainer, name + ".equality", |
| | | equalityIndexer, |
| | | indexConfig.getEqualityEntryLimit(), |
| | | indexConfig.getCursorEntryLimit()); |
| | |
| | | if (indexConfig.isPresenceIndex()) |
| | | { |
| | | Indexer presenceIndexer = new PresenceIndexer(indexConfig); |
| | | this.presenceIndex = new Index(container, name + ".presence", |
| | | this.presenceIndex = new Index(this.entryContainer, name + ".presence", |
| | | presenceIndexer, |
| | | indexConfig.getPresenceEntryLimit(), |
| | | indexConfig.getCursorEntryLimit()); |
| | |
| | | if (indexConfig.isSubstringIndex()) |
| | | { |
| | | Indexer substringIndexer = new SubstringIndexer(indexConfig); |
| | | this.substringIndex = new Index(container, name + ".substring", |
| | | this.substringIndex = new Index(this.entryContainer, name + ".substring", |
| | | substringIndexer, |
| | | indexConfig.getSubstringEntryLimit(), |
| | | indexConfig.getCursorEntryLimit()); |
| | |
| | | if (indexConfig.isOrderingIndex()) |
| | | { |
| | | Indexer orderingIndexer = new OrderingIndexer(indexConfig); |
| | | this.orderingIndex = new Index(container, name + ".ordering", |
| | | this.orderingIndex = new Index(this.entryContainer, name + ".ordering", |
| | | orderingIndexer, |
| | | indexConfig.getEqualityEntryLimit(), |
| | | indexConfig.getCursorEntryLimit()); |
| | |
| | | */ |
| | | public void close() |
| | | { |
| | | // The container is responsible for closing the JE databases. |
| | | // The entryContainer is responsible for closing the JE databases. |
| | | } |
| | | |
| | | /** |
| | |
| | | String name = attrType.getNameOrOID(); |
| | | if (indexConfig.isEqualityIndex()) |
| | | { |
| | | container.removeDatabase(name + ".equality"); |
| | | entryContainer.removeDatabase(name + ".equality"); |
| | | } |
| | | if (indexConfig.isPresenceIndex()) |
| | | { |
| | | container.removeDatabase(name + ".presence"); |
| | | entryContainer.removeDatabase(name + ".presence"); |
| | | } |
| | | if (indexConfig.isSubstringIndex()) |
| | | { |
| | | container.removeDatabase(name + ".substring"); |
| | | entryContainer.removeDatabase(name + ".substring"); |
| | | } |
| | | if (indexConfig.isOrderingIndex()) |
| | | { |
| | | container.removeDatabase(name + ".ordering"); |
| | | entryContainer.removeDatabase(name + ".ordering"); |
| | | } |
| | | } |
| | | |
| | |
| | | */ |
| | | package org.opends.server.backends.jeb; |
| | | |
| | | import java.io.File; |
| | | import java.io.FilenameFilter; |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | import com.sleepycat.je.CheckpointConfig; |
| | | import com.sleepycat.je.Database; |
| | | import com.sleepycat.je.Environment; |
| | | import com.sleepycat.je.EnvironmentConfig; |
| | | import com.sleepycat.je.EnvironmentStats; |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.PreloadConfig; |
| | | import com.sleepycat.je.PreloadStats; |
| | | import com.sleepycat.je.PreloadStatus; |
| | | import com.sleepycat.je.StatsConfig; |
| | | import com.sleepycat.je.config.EnvironmentParams; |
| | | import com.sleepycat.je.config.ConfigParam; |
| | | |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.api.ConfigurableComponent; |
| | |
| | | import org.opends.server.types.BackupDirectory; |
| | | import org.opends.server.types.CancelledOperationException; |
| | | import org.opends.server.types.ConfigChangeResult; |
| | | import org.opends.server.types.DebugLogCategory; |
| | | import org.opends.server.types.DebugLogSeverity; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.Entry; |
| | |
| | | import org.opends.server.types.LDIFExportConfig; |
| | | import org.opends.server.types.RestoreConfig; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.FilePermission; |
| | | import org.opends.server.monitors.DatabaseEnvironmentMonitor; |
| | | import org.opends.server.util.LDIFException; |
| | | import org.opends.server.loggers.Debug; |
| | | |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | import static org.opends.server.messages.JebMessages.*; |
| | |
| | | private Config config; |
| | | |
| | | /** |
| | | * The directory containing persistent storage for the backend. |
| | | * The root JE container to use for this backend. |
| | | */ |
| | | private File backendDirectory; |
| | | |
| | | /** |
| | | * The permissions mode for the directory containing persistent storage for |
| | | * the backend. |
| | | */ |
| | | private FilePermission backendPermission; |
| | | |
| | | /** |
| | | * The base DNs contained in this backend. |
| | | */ |
| | | private ConcurrentHashMap<DN, EntryContainer> baseDNs; |
| | | |
| | | /** |
| | | * The JE environment for this backend instance. Each instance has its own |
| | | * environment. |
| | | */ |
| | | private com.sleepycat.je.Environment dbEnv; |
| | | private RootContainer rootContainer; |
| | | |
| | | /** |
| | | * A configurable component to handle changes to the configuration of |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Determine the container of an entry DN. |
| | | * |
| | | * @param dn The DN of an entry. |
| | | * @return The container of the entry. |
| | | * @throws DirectoryException If the entry DN does not match any of the base |
| | | * DNs. |
| | | */ |
| | | private EntryContainer getContainer(DN dn) throws DirectoryException |
| | | { |
| | | assert debugEnter(CLASS_NAME, "getContainer"); |
| | | |
| | | EntryContainer ec = null; |
| | | DN nodeDN = dn; |
| | | |
| | | while (ec == null && nodeDN != null) |
| | | { |
| | | ec = baseDNs.get(nodeDN); |
| | | if (ec == null) |
| | | { |
| | | nodeDN = nodeDN.getParent(); |
| | | } |
| | | } |
| | | |
| | | if (ec == null) |
| | | { |
| | | // The operation should not have been routed to this backend. |
| | | String message = getMessage(MSGID_JEB_INCORRECT_ROUTING, |
| | | dn.toString()); |
| | | throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, |
| | | MSGID_JEB_INCORRECT_ROUTING); |
| | | } |
| | | |
| | | return ec; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * This method constructs a container name from a base DN. Only alphanumeric |
| | | * characters are preserved, all other characters are replaced with an |
| | |
| | | config = new Config(); |
| | | config.initializeConfig(configEntry, baseDNs); |
| | | |
| | | // Get the backend database directory. |
| | | backendDirectory = config.getBackendDirectory(); |
| | | if (!backendDirectory.isDirectory()) |
| | | { |
| | | String message = getMessage(MSGID_JEB_DIRECTORY_INVALID, |
| | | backendDirectory.getPath()); |
| | | throw new InitializationException(MSGID_JEB_DIRECTORY_INVALID, |
| | | message); |
| | | } |
| | | |
| | | // Get the backend database directory permissions and apply |
| | | try |
| | | { |
| | | backendPermission = config.getBackendPermission(); |
| | | if(!FilePermission.setPermissions(backendDirectory, backendPermission)) |
| | | { |
| | | throw new Exception(); |
| | | } |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | // Log an warning that the permissions were not set. |
| | | int msgID = MSGID_JEB_SET_PERMISSIONS_FAILED; |
| | | String message = getMessage(msgID, backendDirectory.getPath()); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_WARNING, |
| | | message, msgID); |
| | | } |
| | | |
| | | // FIXME: Currently assuming every base DN is also a suffix. |
| | | for (DN dn : baseDNs) |
| | | { |
| | |
| | | // Open the database environment |
| | | try |
| | | { |
| | | dbEnv = new Environment(backendDirectory, |
| | | config.getEnvironmentConfig()); |
| | | |
| | | Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO, |
| | | CLASS_NAME, "initializeBackend", |
| | | dbEnv.getConfig().toString()); |
| | | |
| | | // cleanDatabase(); |
| | | rootContainer = new RootContainer(config, this); |
| | | rootContainer.open(); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | |
| | | throw new InitializationException(MSGID_JEB_OPEN_ENV_FAIL, message, e); |
| | | } |
| | | |
| | | // Create and register a monitor provider for the environment. |
| | | String monitorName = this.getBackendID() + " Database Environment"; |
| | | // Register a monitor provider for the environment. |
| | | MonitorProvider monitorProvider = |
| | | new DatabaseEnvironmentMonitor(monitorName, dbEnv); |
| | | rootContainer.getMonitorProvider(); |
| | | monitorProviders.add(monitorProvider); |
| | | DirectoryServer.registerMonitorProvider(monitorProvider); |
| | | |
| | | // Open all the databases. |
| | | this.baseDNs = new ConcurrentHashMap<DN, EntryContainer>(baseDNs.length); |
| | | for (DN dn : baseDNs) |
| | | try |
| | | { |
| | | String containerName = getContainerName(dn); |
| | | Container container = new Container(dbEnv, containerName); |
| | | EntryContainer ec = new EntryContainer(this, config, container); |
| | | |
| | | try |
| | | { |
| | | ec.open(); |
| | | } |
| | | catch (DatabaseException databaseException) |
| | | { |
| | | assert debugException(CLASS_NAME, "initializeBackend", |
| | | databaseException); |
| | | String message = getMessage(MSGID_JEB_OPEN_DATABASE_FAIL, |
| | | databaseException.getMessage()); |
| | | throw new InitializationException(MSGID_JEB_OPEN_DATABASE_FAIL, message, |
| | | databaseException); |
| | | } |
| | | |
| | | this.baseDNs.put(dn, ec); |
| | | rootContainer.openEntryContainers(baseDNs); |
| | | } |
| | | catch (DatabaseException databaseException) |
| | | { |
| | | assert debugException(CLASS_NAME, "initializeBackend", |
| | | databaseException); |
| | | String message = getMessage(MSGID_JEB_OPEN_DATABASE_FAIL, |
| | | databaseException.getMessage()); |
| | | throw new InitializationException(MSGID_JEB_OPEN_DATABASE_FAIL, message, |
| | | databaseException); |
| | | } |
| | | |
| | | // Preload the database cache. |
| | | preload(); |
| | | rootContainer.preload(); |
| | | |
| | | // Determine the next entry ID and the total number of entries. |
| | | EntryID highestID = null; |
| | | long entryCount = 0; |
| | | for (EntryContainer ec : this.baseDNs.values()) |
| | | for (EntryContainer ec : rootContainer.getEntryContainers()) |
| | | { |
| | | try |
| | | { |
| | |
| | | DirectoryServer.registerConfigurableComponent(this); |
| | | |
| | | // Register the database environment as a configurable component. |
| | | DN envConfigDN = config.getEnvConfigDN(); |
| | | if (envConfigDN != null) |
| | | ConfigurableEnvironment configurableEnv = |
| | | rootContainer.getConfigurableEnvironment(); |
| | | if (configurableEnv != null) |
| | | { |
| | | configurableEnv = new ConfigurableEnvironment(envConfigDN, dbEnv); |
| | | DirectoryServer.registerConfigurableComponent(configurableEnv); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Synchronously invokes the cleaner on the database environment then forces a |
| | | * checkpoint to delete the log files that are no longer in use. |
| | | * |
| | | * @throws DatabaseException If an error occurs while cleaning the database |
| | | * environment. |
| | | */ |
| | | private void cleanDatabase() |
| | | throws DatabaseException |
| | | { |
| | | assert debugEnter(CLASS_NAME, "cleanDatabase"); |
| | | |
| | | int msgID; |
| | | String message; |
| | | |
| | | FilenameFilter filenameFilter = new FilenameFilter() |
| | | { |
| | | public boolean accept(File d, String name) |
| | | { |
| | | return name.endsWith(".jdb"); |
| | | } |
| | | }; |
| | | |
| | | int beforeLogfileCount = backendDirectory.list(filenameFilter).length; |
| | | |
| | | msgID = MSGID_JEB_CLEAN_DATABASE_START; |
| | | message = getMessage(msgID, beforeLogfileCount, backendDirectory.getPath()); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message, |
| | | msgID); |
| | | |
| | | int currentCleaned = 0; |
| | | int totalCleaned = 0; |
| | | while ((currentCleaned = dbEnv.cleanLog()) > 0) |
| | | { |
| | | totalCleaned += currentCleaned; |
| | | } |
| | | |
| | | msgID = MSGID_JEB_CLEAN_DATABASE_MARKED; |
| | | message = getMessage(msgID, totalCleaned); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message, |
| | | msgID); |
| | | |
| | | if (totalCleaned > 0) |
| | | { |
| | | CheckpointConfig force = new CheckpointConfig(); |
| | | force.setForce(true); |
| | | dbEnv.checkpoint(force); |
| | | } |
| | | |
| | | int afterLogfileCount = backendDirectory.list(filenameFilter).length; |
| | | |
| | | msgID = MSGID_JEB_CLEAN_DATABASE_FINISH; |
| | | message = getMessage(msgID, afterLogfileCount); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message, |
| | | msgID); |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs any necessary work to finalize this backend, including closing any |
| | | * underlying databases or connections and deregistering any suffixes that it |
| | |
| | | |
| | | // Deregister our suffixes. |
| | | // FIXME: Currently assuming every base DN is also a suffix. |
| | | for (DN dn : baseDNs.keySet()) |
| | | for (DN dn : rootContainer.getBaseDNs()) |
| | | { |
| | | try |
| | | { |
| | |
| | | // Close the database. |
| | | try |
| | | { |
| | | for (EntryContainer ec : baseDNs.values()) |
| | | { |
| | | ec.close(); |
| | | } |
| | | dbEnv.close(); |
| | | rootContainer.close(); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | |
| | | readerBegin(); |
| | | try |
| | | { |
| | | EntryContainer ec = getContainer(entryDN); |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | Entry entry; |
| | | try |
| | | { |
| | |
| | | try |
| | | { |
| | | DN entryDN = entry.getDN(); |
| | | EntryContainer ec = getContainer(entryDN); |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | |
| | | try |
| | | { |
| | |
| | | writerBegin(); |
| | | try |
| | | { |
| | | EntryContainer ec = getContainer(entryDN); |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | try |
| | | { |
| | | ec.deleteEntry(entryDN, deleteOperation); |
| | |
| | | try |
| | | { |
| | | DN entryDN = entry.getDN(); |
| | | EntryContainer ec = getContainer(entryDN); |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | |
| | | try |
| | | { |
| | |
| | | writerBegin(); |
| | | try |
| | | { |
| | | EntryContainer currentContainer = getContainer(currentDN); |
| | | EntryContainer container = getContainer(entry.getDN()); |
| | | EntryContainer currentContainer = rootContainer.getEntryContainer( |
| | | currentDN); |
| | | EntryContainer container = rootContainer.getEntryContainer(entry.getDN()); |
| | | |
| | | if (currentContainer != container) |
| | | { |
| | | // No reason why we cannot implement a move between containers |
| | | // FIXME: No reason why we cannot implement a move between containers |
| | | // since the containers share the same database environment. |
| | | int msgID = MSGID_JEB_FUNCTION_NOT_SUPPORTED; |
| | | String msg = getMessage(MSGID_JEB_FUNCTION_NOT_SUPPORTED); |
| | |
| | | readerBegin(); |
| | | try |
| | | { |
| | | EntryContainer ec = getContainer(searchOperation.getBaseDN()); |
| | | EntryContainer ec = rootContainer.getEntryContainer( |
| | | searchOperation.getBaseDN()); |
| | | ec.search(searchOperation); |
| | | } |
| | | catch (DatabaseException e) |
| | |
| | | e.getMessageID()); |
| | | } |
| | | |
| | | // If the backend already has the environment open, we must use the same |
| | | // underlying environment instance and its configuration. |
| | | Environment env; |
| | | try |
| | | { |
| | | EnvironmentConfig envConfig; |
| | | Environment existingEnv = dbEnv; |
| | | if (existingEnv == null) |
| | | { |
| | | // Open the environment read-only. |
| | | envConfig = config.getEnvironmentConfig(); |
| | | envConfig.setReadOnly(true); |
| | | envConfig.setAllowCreate(false); |
| | | envConfig.setTransactional(false); |
| | | } |
| | | else |
| | | { |
| | | envConfig = existingEnv.getConfig(); |
| | | } |
| | | |
| | | // Open a new environment handle. |
| | | File backendDirectory = config.getBackendDirectory(); |
| | | env = new Environment(backendDirectory, envConfig); |
| | | |
| | | Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO, |
| | | CLASS_NAME, "exportLDIF", |
| | | env.getConfig().toString()); |
| | | |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | assert debugException(CLASS_NAME, "exportLDIF", e); |
| | | String message = getMessage(MSGID_JEB_DATABASE_EXCEPTION, |
| | | e.getMessage()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, MSGID_JEB_DATABASE_EXCEPTION); |
| | | } |
| | | // If the backend already has the root container open, we must use the same |
| | | // underlying root container |
| | | boolean openRootContainer = rootContainer == null; |
| | | |
| | | try |
| | | { |
| | | ExportJob exportJob = new ExportJob(this, config, exportConfig); |
| | | exportJob.exportLDIF(env); |
| | | if (openRootContainer) |
| | | { |
| | | // Open the database environment |
| | | rootContainer = new RootContainer(config, this); |
| | | rootContainer.open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | true, false, false, false, true, true); |
| | | rootContainer.openEntryContainers(baseDNs); |
| | | } |
| | | |
| | | ExportJob exportJob = new ExportJob(exportConfig); |
| | | exportJob.exportLDIF(rootContainer); |
| | | } |
| | | catch (IOException ioe) |
| | | { |
| | |
| | | } |
| | | finally |
| | | { |
| | | try |
| | | //If a root container was opened in this method as read only, close it |
| | | //to leave the backend in the same state. |
| | | if (openRootContainer && rootContainer != null) |
| | | { |
| | | env.close(); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | assert debugException(CLASS_NAME, "exportLDIF", e); |
| | | try |
| | | { |
| | | rootContainer.close(); |
| | | rootContainer = null; |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | assert debugException(CLASS_NAME, "exportLDIF", e); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | config = new Config(); |
| | | config.initializeConfig(configEntry, baseDNs); |
| | | |
| | | VerifyJob verifyJob = new VerifyJob(this, config, verifyConfig); |
| | | // If the backend already has the root container open, we must use the same |
| | | // underlying root container |
| | | boolean openRootContainer = rootContainer == null; |
| | | |
| | | try |
| | | { |
| | | verifyJob.verifyBackend(); |
| | | if (openRootContainer) |
| | | { |
| | | // Open the database environment |
| | | rootContainer = new RootContainer(config, this); |
| | | rootContainer.open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | true, false, false, false, true, true); |
| | | rootContainer.openEntryContainers(baseDNs); |
| | | } |
| | | |
| | | VerifyJob verifyJob = new VerifyJob(config, verifyConfig); |
| | | verifyJob.verifyBackend(rootContainer); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | finally |
| | | { |
| | | //If a root container was opened in this method as read only, close it |
| | | //to leave the backend in the same state. |
| | | if (openRootContainer && rootContainer != null) |
| | | { |
| | | try |
| | | { |
| | | rootContainer.close(); |
| | | rootContainer = null; |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | assert debugException(CLASS_NAME, "verifyBackend", e); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | // operation threads with a handle on the entry container being |
| | | // closed. |
| | | DirectoryServer.deregisterSuffix(baseDN); |
| | | EntryContainer ec = this.baseDNs.remove(baseDN); |
| | | ec.close(); |
| | | ec.removeContainer(); |
| | | rootContainer.removeEntryContainer(baseDN); |
| | | } |
| | | } |
| | | |
| | | for (DN baseDN : newConfig.getBaseDNs()) |
| | | { |
| | | if (!this.baseDNs.containsKey(baseDN)) |
| | | if (!rootContainer.getBaseDNs().contains(baseDN)) |
| | | { |
| | | // The base DN was added. |
| | | String containerName = getContainerName(baseDN); |
| | | Container container = new Container(dbEnv, containerName); |
| | | EntryContainer ec = new EntryContainer(this, config, container); |
| | | ec.open(); |
| | | this.baseDNs.put(baseDN, ec); |
| | | rootContainer.openEntryContainer(baseDN); |
| | | DirectoryServer.registerSuffix(baseDN, this); |
| | | } |
| | | } |
| | | |
| | | // Check for changes to the database directory permissions |
| | | FilePermission oldPermission = config.getBackendPermission(); |
| | | FilePermission newPermission = newConfig.getBackendPermission(); |
| | | |
| | | if(!FilePermission.toUNIXMode(oldPermission).equals( |
| | | FilePermission.toUNIXMode(newPermission))) |
| | | { |
| | | try |
| | | { |
| | | if(!FilePermission.setPermissions(newConfig.getBackendDirectory(), |
| | | newPermission)) |
| | | { |
| | | throw new Exception(); |
| | | } |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | // Log an warning that the permissions were not set. |
| | | int msgID = MSGID_JEB_SET_PERMISSIONS_FAILED; |
| | | String message = getMessage(msgID, backendDirectory.getPath()); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_WARNING, |
| | | message, msgID); |
| | | } |
| | | } |
| | | |
| | | // Check if any JE non-mutable properties were changed. |
| | | EnvironmentConfig oldEnvConfig = config.getEnvironmentConfig(); |
| | | EnvironmentConfig newEnvConfig = newConfig.getEnvironmentConfig(); |
| | | Map paramsMap = EnvironmentParams.SUPPORTED_PARAMS; |
| | | for (Object o : paramsMap.values()) |
| | | { |
| | | ConfigParam param = (ConfigParam)o; |
| | | if (!param.isMutable()) |
| | | { |
| | | String oldValue = oldEnvConfig.getConfigParam(param.getName()); |
| | | String newValue = newEnvConfig.getConfigParam(param.getName()); |
| | | if (!oldValue.equalsIgnoreCase(newValue)) |
| | | { |
| | | System.out.println("The change to the following property will " + |
| | | "take effect when the backend is restarted: " + |
| | | param.getName()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // This takes care of changes to the JE environment for those |
| | | // properties that are mutable at runtime. |
| | | dbEnv.setMutableConfig(newConfig.getEnvironmentConfig()); |
| | | |
| | | Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO, |
| | | CLASS_NAME, "applyNewConfiguration", |
| | | dbEnv.getConfig().toString()); |
| | | // Apply new JE configuration |
| | | rootContainer.applyNewConfig(config); |
| | | |
| | | // Put the new configuration in place. |
| | | config = newConfig; |
| | |
| | | return ccr; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Preload the database cache. There is no preload if the configured preload |
| | | * time limit is zero. |
| | | * 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. |
| | | */ |
| | | private void preload() |
| | | public RootContainer getRootContainer() |
| | | { |
| | | assert debugEnter(CLASS_NAME, "preload"); |
| | | |
| | | long timeLimit = config.getPreloadTimeLimit(); |
| | | |
| | | if (timeLimit > 0) |
| | | { |
| | | // Get a list of all the databases used by the backend. |
| | | ArrayList<Database> dbList = new ArrayList<Database>(); |
| | | for (EntryContainer ec : baseDNs.values()) |
| | | { |
| | | ec.listDatabases(dbList); |
| | | } |
| | | |
| | | // Sort the list in order of priority. |
| | | Collections.sort(dbList, new DbPreloadComparator()); |
| | | |
| | | // Preload each database until we reach the time limit or the cache |
| | | // is filled. |
| | | try |
| | | { |
| | | long timeEnd = System.currentTimeMillis() + timeLimit; |
| | | |
| | | // Configure preload of Leaf Nodes (LNs) containing the data values. |
| | | PreloadConfig preloadConfig = new PreloadConfig(); |
| | | preloadConfig.setLoadLNs(true); |
| | | |
| | | for (Database db : dbList) |
| | | { |
| | | // Calculate the remaining time. |
| | | long timeRemaining = timeEnd - System.currentTimeMillis(); |
| | | if (timeRemaining <= 0) |
| | | { |
| | | break; |
| | | } |
| | | |
| | | preloadConfig.setMaxMillisecs(timeRemaining); |
| | | PreloadStats preloadStats = db.preload(preloadConfig); |
| | | /* |
| | | System.out.println("file=" + db.getDatabaseName() + |
| | | " LNs=" + preloadStats.getNLNsLoaded()); |
| | | */ |
| | | |
| | | // Stop if the cache is full or the time limit has been exceeded. |
| | | if (preloadStats.getStatus() != PreloadStatus.SUCCESS) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // Log an informational message about the size of the cache. |
| | | EnvironmentStats stats = dbEnv.getStats(new StatsConfig()); |
| | | long total = stats.getCacheTotalBytes(); |
| | | |
| | | int msgID = MSGID_JEB_CACHE_SIZE_AFTER_PRELOAD; |
| | | String message = getMessage(msgID, total / (1024 * 1024)); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message, |
| | | msgID); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | assert debugException(CLASS_NAME, "preload", e); |
| | | } |
| | | } |
| | | assert debugEnter(CLASS_NAME, "getRootContainer"); |
| | | return rootContainer; |
| | | } |
| | | } |
| | |
| | | throw new ConfigException(msgID, message); |
| | | } |
| | | backendDirectory = getFileForPath(backendDirectoryAttr.activeValue()); |
| | | //Make sure the directory is valid. |
| | | if (!backendDirectory.isDirectory()) |
| | | { |
| | | int msgID = MSGID_JEB_DIRECTORY_INVALID; |
| | | String message = getMessage(msgID, backendDirectory.getPath()); |
| | | throw new ConfigException(MSGID_JEB_DIRECTORY_INVALID, message); |
| | | } |
| | | |
| | | // ds-cfg-backend-mode |
| | | // Optional, single-valued config attribute requiring admin action on change |
| | |
| | | public class DN2ID |
| | | { |
| | | /** |
| | | * The database container. |
| | | * The database entryContainer. |
| | | */ |
| | | private Container container; |
| | | private EntryContainer entryContainer; |
| | | |
| | | /** |
| | | * The JE database configuration. |
| | |
| | | private DatabaseConfig dbConfig; |
| | | |
| | | /** |
| | | * The name of the database within the container. |
| | | * The name of the database within the entryContainer. |
| | | */ |
| | | private String name; |
| | | |
| | |
| | | new ThreadLocal<Database>(); |
| | | |
| | | /** |
| | | * Create a DN2ID instance for the DN database in a given container. |
| | | * Create a DN2ID instance for the DN database in a given entryContainer. |
| | | * |
| | | * @param container The container of the DN database. |
| | | * @param entryContainer The entryContainer of the DN database. |
| | | * @param dbConfig The JE database configuration which will be used to |
| | | * open the database. |
| | | * @param name The name of the DN database ("dn2id"). |
| | | */ |
| | | public DN2ID(Container container, DatabaseConfig dbConfig, String name) |
| | | public DN2ID(EntryContainer entryContainer, DatabaseConfig dbConfig, |
| | | String name) |
| | | { |
| | | this.container = container; |
| | | this.entryContainer = entryContainer; |
| | | this.dbConfig = dbConfig; |
| | | this.name = name; |
| | | } |
| | |
| | | |
| | | /** |
| | | * Get a handle to the database. It returns a per-thread handle to avoid |
| | | * any thread contention on the database handle. The container is |
| | | * any thread contention on the database handle. The entryContainer is |
| | | * responsible for closing all handles. |
| | | * |
| | | * @return A database handle. |
| | |
| | | Database database = threadLocalDatabase.get(); |
| | | if (database == null) |
| | | { |
| | | database = container.openDatabase(dbConfig, name); |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | } |
| | | return database; |
| | |
| | | |
| | | OperationStatus status; |
| | | |
| | | status = Container.insert(getDatabase(), txn, key, data); |
| | | status = EntryContainer.insert(getDatabase(), txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | DatabaseEntry data = id.getDatabaseEntry(); |
| | | |
| | | OperationStatus status; |
| | | status = Container.put(getDatabase(), txn, key, data); |
| | | status = EntryContainer.put(getDatabase(), txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status; |
| | | status = Container.put(getDatabase(), txn, key, data); |
| | | status = EntryContainer.put(getDatabase(), txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | { |
| | | DatabaseEntry key = DNdata(dn); |
| | | |
| | | OperationStatus status = Container.delete(getDatabase(), txn, key); |
| | | OperationStatus status = EntryContainer.delete(getDatabase(), txn, key); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | OperationStatus status; |
| | | status = Container.read(getDatabase(), txn, key, data, LockMode.DEFAULT); |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, |
| | | LockMode.DEFAULT); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return null; |
| | |
| | | DirectoryServer.getAttributeType(ATTR_REFERRAL_URL); |
| | | |
| | | /** |
| | | * The database container. |
| | | * The database entryContainer. |
| | | */ |
| | | private Container container; |
| | | private EntryContainer entryContainer; |
| | | |
| | | /** |
| | | * The JE database configuration. |
| | |
| | | private DatabaseConfig dbConfig; |
| | | |
| | | /** |
| | | * The name of the database within the container. |
| | | * The name of the database within the entryContainer. |
| | | */ |
| | | private String name; |
| | | |
| | |
| | | new ThreadLocal<Database>(); |
| | | |
| | | /** |
| | | * Create a new object representing a referral database in a given container. |
| | | * Create a new object representing a referral database in a given |
| | | * entryContainer. |
| | | * |
| | | * @param container The container of the referral database. |
| | | * @param entryContainer The entryContainer of the referral database. |
| | | * @param dbConfig The JE database configuration which will be used to |
| | | * open the database. |
| | | * @param name The name of the referral database. |
| | | */ |
| | | public DN2URI(Container container, DatabaseConfig dbConfig, String name) |
| | | public DN2URI(EntryContainer entryContainer, DatabaseConfig dbConfig, |
| | | String name) |
| | | { |
| | | this.container = container; |
| | | this.entryContainer = entryContainer; |
| | | this.dbConfig = dbConfig; |
| | | this.name = name; |
| | | } |
| | |
| | | |
| | | /** |
| | | * Get a handle to the database. It returns a per-thread handle to avoid |
| | | * any thread contention on the database handle. The container is |
| | | * any thread contention on the database handle. The entryContainer is |
| | | * responsible for closing all handles. |
| | | * |
| | | * @return A database handle. |
| | |
| | | Database database = threadLocalDatabase.get(); |
| | | if (database == null) |
| | | { |
| | | database = container.openDatabase(dbConfig, name); |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | } |
| | | return database; |
| | |
| | | |
| | | // The JE insert method does not permit duplicate keys so we must use the |
| | | // put method. |
| | | status = Container.put(getDatabase(), txn, key, data); |
| | | status = EntryContainer.put(getDatabase(), txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | DatabaseEntry key = new DatabaseEntry(normDN); |
| | | OperationStatus status; |
| | | |
| | | status = Container.delete(getDatabase(), txn, key); |
| | | status = EntryContainer.delete(getDatabase(), txn, key); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | */ |
| | | package org.opends.server.backends.jeb; |
| | | |
| | | import com.sleepycat.je.Cursor; |
| | | import com.sleepycat.je.Database; |
| | | import com.sleepycat.je.DatabaseConfig; |
| | | import com.sleepycat.je.DatabaseEntry; |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.DatabaseNotFoundException; |
| | | import com.sleepycat.je.DeadlockException; |
| | | import com.sleepycat.je.LockMode; |
| | | import com.sleepycat.je.OperationStatus; |
| | | import com.sleepycat.je.Transaction; |
| | | import com.sleepycat.je.*; |
| | | |
| | | import org.opends.server.api.AttributeSyntax; |
| | | import org.opends.server.api.Backend; |
| | |
| | | import org.opends.server.core.ModifyDNOperation; |
| | | import org.opends.server.core.Operation; |
| | | import org.opends.server.core.SearchOperation; |
| | | import org.opends.server.loggers.Debug; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.protocols.ldap.LDAPException; |
| | | import org.opends.server.controls.PagedResultsControl; |
| | |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.CancelledOperationException; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DebugLogCategory; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.messages.JebMessages.*; |
| | | import static org.opends.server.loggers.Debug.debugException; |
| | | import static org.opends.server.types.DebugLogSeverity.VERBOSE; |
| | | import static org.opends.server.types.DebugLogCategory.DATABASE_ACCESS; |
| | | import static org.opends.server.types.DebugLogCategory.DATABASE_WRITE; |
| | | import static org.opends.server.types.DebugLogCategory.DATABASE_READ; |
| | | import static org.opends.server.util.ServerConstants.OID_SUBTREE_DELETE_CONTROL; |
| | | import static org.opends.server.util.ServerConstants.OID_PAGED_RESULTS_CONTROL; |
| | | |
| | |
| | | private static final String CLASS_NAME = |
| | | "org.opends.server.backends.jeb.EntryContainer"; |
| | | |
| | | /** |
| | | * The JE database environment. |
| | | */ |
| | | private static Environment env; |
| | | |
| | | /** |
| | | * The backend configuration. |
| | | */ |
| | | private static Config config; |
| | | |
| | | /** |
| | | * The name of the entry database. |
| | | */ |
| | |
| | | public static final String ATTR_DEBUG_SEARCH_INDEX = "debugsearchindex"; |
| | | |
| | | /** |
| | | * The backend to which this entry container belongs. |
| | | * The backend to which this entry entryContainer belongs. |
| | | */ |
| | | private Backend backend; |
| | | |
| | | /** |
| | | * The database container. |
| | | * The baseDN this entry container is responsible for. |
| | | */ |
| | | private Container container; |
| | | private DN baseDN; |
| | | |
| | | /** |
| | | * The backend configuration. |
| | | * A list of JE database handles opened through this entryContainer. |
| | | * They will be closed by the entryContainer. |
| | | */ |
| | | private Config config; |
| | | private ArrayList<Database> databases; |
| | | |
| | | /** |
| | | * A list of JE cursor handles registered with this entryContainer. |
| | | * They will be closed by the entryContainer. |
| | | */ |
| | | private ArrayList<Cursor> cursors; |
| | | |
| | | /** |
| | | * The DN database maps a normalized DN string to an entry ID (8 bytes). |
| | |
| | | private HashMap<AttributeType, AttributeIndex> attrIndexMap; |
| | | |
| | | /** |
| | | * Create a new entry container object. This method does not actually create |
| | | * anything in the JE database environment. |
| | | * Create a new entry entryContainer object. |
| | | * |
| | | * @param baseDN The baseDN this entry container will be responsible for |
| | | * storing on disk. |
| | | * @param backend A reference to the JE backend that is creating this entry |
| | | * container. It is needed by the Directory Server entry cache methods. |
| | | * container. It is needed by the Directory Server entry cache |
| | | * methods. |
| | | * @param config The configuration of the JE backend. |
| | | * @param container The databases reside in this container. |
| | | * @param env The JE environment to create this entryContainer in |
| | | */ |
| | | public EntryContainer(Backend backend, Config config, Container container) |
| | | public EntryContainer(DN baseDN, Backend backend, Config config, |
| | | Environment env) |
| | | { |
| | | this.backend = backend; |
| | | this.baseDN = baseDN; |
| | | this.config = config; |
| | | this.container = container; |
| | | this.env = env; |
| | | |
| | | // Instantiate database and cursor lists |
| | | databases = new ArrayList<Database>(); |
| | | cursors = new ArrayList<Cursor>(); |
| | | |
| | | // Instantiate indexes for id2children and id2subtree. |
| | | id2children = new Index(container, ID2CHILDREN_DATABASE_NAME, |
| | | id2children = new Index(this, ID2CHILDREN_DATABASE_NAME, |
| | | new ID2CIndexer(), |
| | | config.getBackendIndexEntryLimit(), |
| | | 0); |
| | | id2subtree = new Index(container, ID2SUBTREE_DATABASE_NAME, |
| | | id2subtree = new Index(this, ID2SUBTREE_DATABASE_NAME, |
| | | new ID2SIndexer(), |
| | | config.getBackendIndexEntryLimit(), |
| | | 0); |
| | |
| | | { |
| | | for (IndexConfig indexConfig : config.getIndexConfigMap().values()) |
| | | { |
| | | AttributeIndex index = new AttributeIndex(indexConfig, container); |
| | | AttributeIndex index = new AttributeIndex(this, indexConfig); |
| | | attrIndexMap.put(indexConfig.getAttributeType(), index); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * Opens the container for reading and writing. |
| | | * Opens the entryContainer for reading and writing. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | |
| | | DatabaseConfig dbNodupsConfig = new DatabaseConfig(); |
| | | dbNodupsConfig.setAllowCreate(true); |
| | | dbNodupsConfig.setTransactional(true); |
| | | container.open(); |
| | | |
| | | try |
| | | { |
| | | id2entry = new ID2Entry(container, dbNodupsConfig, entryDataConfig, |
| | | id2entry = new ID2Entry(this, dbNodupsConfig, entryDataConfig, |
| | | ID2ENTRY_DATABASE_NAME); |
| | | id2entry.open(); |
| | | |
| | |
| | | dn2idConfig.setAllowCreate(true); |
| | | dn2idConfig.setTransactional(true); |
| | | dn2idConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2id = new DN2ID(container, dn2idConfig, DN2ID_DATABASE_NAME); |
| | | dn2id = new DN2ID(this, dn2idConfig, DN2ID_DATABASE_NAME); |
| | | dn2id.open(); |
| | | |
| | | id2children.open(dbNodupsConfig); |
| | |
| | | dn2uriConfig.setAllowCreate(true); |
| | | dn2uriConfig.setTransactional(true); |
| | | dn2uriConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2uri = new DN2URI(container, dn2uriConfig, REFERRAL_DATABASE_NAME); |
| | | dn2uri = new DN2URI(this, dn2uriConfig, REFERRAL_DATABASE_NAME); |
| | | dn2uri.open(); |
| | | |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | |
| | | catch (DatabaseException e) |
| | | { |
| | | assert debugException(CLASS_NAME, "open", e); |
| | | container.close(); |
| | | close(); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Opens the container for reading and writing without transactions. |
| | | * Opens the entryContainer for reading and writing without transactions. |
| | | * |
| | | * @param deferredWrite Indicates whether to open the container using the |
| | | * deferred write mode. |
| | | * @param deferredWrite Indicates whether to open the entryContainer using |
| | | * the deferred write mode. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | |
| | | dbNodupsConfig.setTransactional(false); |
| | | dbNodupsConfig.setDeferredWrite(deferredWrite); |
| | | |
| | | container.open(); |
| | | try |
| | | { |
| | | id2entry = new ID2Entry(container, dbNodupsConfig, entryDataConfig, |
| | | id2entry = new ID2Entry(this, dbNodupsConfig, entryDataConfig, |
| | | ID2ENTRY_DATABASE_NAME); |
| | | id2entry.open(); |
| | | |
| | |
| | | dn2idConfig.setTransactional(false); |
| | | dn2idConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2idConfig.setDeferredWrite(deferredWrite); |
| | | dn2id = new DN2ID(container, dn2idConfig, DN2ID_DATABASE_NAME); |
| | | dn2id = new DN2ID(this, dn2idConfig, DN2ID_DATABASE_NAME); |
| | | dn2id.open(); |
| | | |
| | | id2children.open(dbNodupsConfig); |
| | |
| | | dn2uriConfig.setTransactional(false); |
| | | dn2uriConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2uriConfig.setDeferredWrite(deferredWrite); |
| | | dn2uri = new DN2URI(container, dn2uriConfig, REFERRAL_DATABASE_NAME); |
| | | dn2uri = new DN2URI(this, dn2uriConfig, REFERRAL_DATABASE_NAME); |
| | | dn2uri.open(); |
| | | |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | |
| | | catch (DatabaseException e) |
| | | { |
| | | assert debugException(CLASS_NAME, "open", e); |
| | | container.close(); |
| | | close(); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Opens the container for reading only. |
| | | * Opens the entryContainer for reading only. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | |
| | | dbNodupsConfig.setAllowCreate(false); |
| | | dbNodupsConfig.setTransactional(false); |
| | | |
| | | container.open(); |
| | | try |
| | | { |
| | | id2entry = new ID2Entry(container, dbNodupsConfig, entryDataConfig, |
| | | id2entry = new ID2Entry(this, dbNodupsConfig, entryDataConfig, |
| | | ID2ENTRY_DATABASE_NAME); |
| | | id2entry.open(); |
| | | |
| | |
| | | dn2idConfig.setAllowCreate(false); |
| | | dn2idConfig.setTransactional(false); |
| | | dn2idConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2id = new DN2ID(container, dn2idConfig, DN2ID_DATABASE_NAME); |
| | | dn2id = new DN2ID(this, dn2idConfig, DN2ID_DATABASE_NAME); |
| | | dn2id.open(); |
| | | |
| | | id2children.open(dbNodupsConfig); |
| | |
| | | dn2uriConfig.setAllowCreate(false); |
| | | dn2uriConfig.setTransactional(false); |
| | | dn2uriConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2uri = new DN2URI(container, dn2uriConfig, REFERRAL_DATABASE_NAME); |
| | | dn2uri = new DN2URI(this, dn2uriConfig, REFERRAL_DATABASE_NAME); |
| | | dn2uri.open(); |
| | | |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | |
| | | catch (DatabaseException e) |
| | | { |
| | | assert debugException(CLASS_NAME, "openReadOnly", e); |
| | | container.close(); |
| | | close(); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Closes the entry container. |
| | | * Closes the entry entryContainer. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void close() |
| | | throws DatabaseException |
| | | { |
| | | // The database container is responsible for closing the JE databases. |
| | | container.close(); |
| | | // Close each cursor that has been registered. |
| | | for (Cursor cursor : cursors) |
| | | { |
| | | cursor.close(); |
| | | } |
| | | |
| | | // Close each database handle that has been opened. |
| | | for (Database database : databases) |
| | | { |
| | | if (database.getConfig().getDeferredWrite()) |
| | | { |
| | | database.sync(); |
| | | } |
| | | |
| | | database.close(); |
| | | } |
| | | |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | | { |
| | | index.close(); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the DN database used by this entry container. The container must |
| | | * have been opened. |
| | | * Get the DN database used by this entry entryContainer. The entryContainer |
| | | * must have been opened. |
| | | * |
| | | * @return The DN database. |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the entry database used by this entry container. The container must |
| | | * have been opened. |
| | | * Get the entry database used by this entry entryContainer. The |
| | | * entryContainer must have been opened. |
| | | * |
| | | * @return The entry database. |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the referral database used by this entry container. The container must |
| | | * have been opened. |
| | | * Get the referral database used by this entry entryContainer. The |
| | | * entryContainer must have been opened. |
| | | * |
| | | * @return The referral database. |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the children database used by this entry container. |
| | | * The container must have been opened. |
| | | * Get the children database used by this entry entryContainer. |
| | | * The entryContainer must have been opened. |
| | | * |
| | | * @return The children database. |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the subtree database used by this entry container. |
| | | * The container must have been opened. |
| | | * Get the subtree database used by this entry entryContainer. |
| | | * The entryContainer must have been opened. |
| | | * |
| | | * @return The subtree database. |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Determine the highest entryID in the container. |
| | | * The container must already be open. |
| | | * Determine the highest entryID in the entryContainer. |
| | | * The entryContainer must already be open. |
| | | * |
| | | * @return The highest entry ID. |
| | | * @throws JebException If an error occurs in the JE backend. |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the specified search in this container. |
| | | * Processes the specified search in this entryContainer. |
| | | * Matching entries should be provided back to the core server using the |
| | | * <CODE>SearchOperation.returnEntry</CODE> method. |
| | | * |
| | |
| | | operation.invokeOperation(txn); |
| | | |
| | | // Commit the transaction. |
| | | Container.transactionCommit(txn); |
| | | EntryContainer.transactionCommit(txn); |
| | | completed = true; |
| | | } |
| | | catch (DeadlockException deadlockException) |
| | | { |
| | | Container.transactionAbort(txn); |
| | | EntryContainer.transactionAbort(txn); |
| | | if (retryRemaining-- <= 0) |
| | | { |
| | | throw deadlockException; |
| | |
| | | } |
| | | catch (DatabaseException databaseException) |
| | | { |
| | | Container.transactionAbort(txn); |
| | | EntryContainer.transactionAbort(txn); |
| | | throw databaseException; |
| | | } |
| | | catch (DirectoryException directoryException) |
| | | { |
| | | Container.transactionAbort(txn); |
| | | EntryContainer.transactionAbort(txn); |
| | | throw directoryException; |
| | | } |
| | | catch (JebException jebException) |
| | | { |
| | | Container.transactionAbort(txn); |
| | | EntryContainer.transactionAbort(txn); |
| | | throw jebException; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Container.transactionAbort(txn); |
| | | EntryContainer.transactionAbort(txn); |
| | | |
| | | int messageID = MSGID_JEB_UNCHECKED_EXCEPTION; |
| | | String message = getMessage(messageID); |
| | |
| | | */ |
| | | public Transaction beginTransaction() throws DatabaseException |
| | | { |
| | | return container.beginTransaction(); |
| | | return EntryContainer.beginTransaction(); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public Transaction beginTransaction() throws DatabaseException |
| | | { |
| | | return container.beginTransaction(); |
| | | return EntryContainer.beginTransaction(); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public Transaction beginTransaction() throws DatabaseException |
| | | { |
| | | return container.beginTransaction(); |
| | | return EntryContainer.beginTransaction(); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public Transaction beginTransaction() throws DatabaseException |
| | | { |
| | | return container.beginTransaction(); |
| | | return EntryContainer.beginTransaction(); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get a count of the number of entries stored in this entry container. |
| | | * Get a count of the number of entries stored in this entry entryContainer. |
| | | * |
| | | * @return The number of entries stored in this entry container. |
| | | * @return The number of entries stored in this entry entryContainer. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public long getEntryCount() throws DatabaseException |
| | |
| | | } |
| | | |
| | | /** |
| | | * Remove the entry container from disk. The container must not be open. |
| | | * Remove the entry entryContainer from disk. The entryContainer must not be |
| | | * open. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | |
| | | { |
| | | try |
| | | { |
| | | container.removeDatabase(DN2ID_DATABASE_NAME); |
| | | removeDatabase(DN2ID_DATABASE_NAME); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | |
| | | } |
| | | try |
| | | { |
| | | container.removeDatabase(ID2ENTRY_DATABASE_NAME); |
| | | removeDatabase(ID2ENTRY_DATABASE_NAME); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | |
| | | } |
| | | try |
| | | { |
| | | container.removeDatabase(ID2CHILDREN_DATABASE_NAME); |
| | | removeDatabase(ID2CHILDREN_DATABASE_NAME); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | |
| | | } |
| | | try |
| | | { |
| | | container.removeDatabase(ID2SUBTREE_DATABASE_NAME); |
| | | removeDatabase(ID2SUBTREE_DATABASE_NAME); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | |
| | | |
| | | /** |
| | | * Get the number of values for which the entry limit has been exceeded |
| | | * since the entry container was opened. |
| | | * since the entry entryContainer was opened. |
| | | * @return The number of values for which the entry limit has been exceeded. |
| | | */ |
| | | public int getEntryLimitExceededCount() |
| | |
| | | } |
| | | |
| | | /** |
| | | * Begin a leaf transaction. |
| | | * @return A JE transaction handle. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | protected Transaction beginTransaction() |
| | | throws DatabaseException |
| | | { |
| | | return container.beginTransaction(); |
| | | } |
| | | |
| | | /** |
| | | * Commit a transaction. |
| | | * @param txn The JE transaction handle. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | protected void transactionCommit(Transaction txn) |
| | | throws DatabaseException |
| | | { |
| | | Container.transactionCommit(txn); |
| | | } |
| | | |
| | | /** |
| | | * Abort a transaction. |
| | | * @param txn The JE transaction handle. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | protected void transactionAbort(Transaction txn) |
| | | throws DatabaseException |
| | | { |
| | | Container.transactionAbort(txn); |
| | | } |
| | | |
| | | /** |
| | | * Get a list of the databases opened by this container. There will be |
| | | * Get a list of the databases opened by this entryContainer. There will be |
| | | * only one handle in the list for each database, regardless of the number |
| | | * of handles open for a given database. |
| | | * @param dbList A list of JE database handles. |
| | | */ |
| | | public void listDatabases(List<Database> dbList) |
| | | { |
| | | // The container has a list of all handles opened. |
| | | List<Database> dbCompleteList = container.getDatabaseList(); |
| | | |
| | | // There may be more than one handle open for a given database |
| | | // so we eliminate duplicates here. |
| | | HashSet<String> set = new HashSet<String>(); |
| | | for (Database db : dbCompleteList) |
| | | for (Database db : databases) |
| | | { |
| | | try |
| | | { |
| | |
| | | return false; |
| | | } |
| | | |
| | | } |
| | | /** |
| | | * Constructs a full JE database name incorporating a entryContainer name. |
| | | * |
| | | * @param builder A string builder to which the full name will be appended. |
| | | * @param name The short database name. |
| | | */ |
| | | private void buildDatabaseName(StringBuilder builder, String name) |
| | | { |
| | | builder.append(getContainerName()); |
| | | builder.append('_'); |
| | | builder.append(name); |
| | | } |
| | | |
| | | /** |
| | | * Opens a JE database in this entryContainer. The resulting database handle |
| | | * must not be closed by the caller, as it will be closed by the |
| | | * entryContainer. If the provided database configuration is transactional, |
| | | * a transaction will be created and used to perform the open. |
| | | * <p> |
| | | * Note that a database can be opened multiple times and will result in |
| | | * multiple unique handles to the database. This is used for example to |
| | | * give each server thread its own database handle to eliminate contention |
| | | * that could occur on a single handle. |
| | | * |
| | | * @param dbConfig The JE database configuration to be used to open the |
| | | * database. |
| | | * @param name The short database name, to which the entryContainer name |
| | | * will be added. |
| | | * @return A new JE database handle. |
| | | * @throws DatabaseException If an error occurs while attempting to open the |
| | | * database. |
| | | */ |
| | | public synchronized Database openDatabase(DatabaseConfig dbConfig, |
| | | String name) |
| | | throws DatabaseException |
| | | { |
| | | Database database; |
| | | |
| | | StringBuilder builder = new StringBuilder(); |
| | | buildDatabaseName(builder, name); |
| | | String fullName = builder.toString(); |
| | | |
| | | if (dbConfig.getTransactional()) |
| | | { |
| | | // Open the database under a transaction. |
| | | Transaction txn = beginTransaction(); |
| | | try |
| | | { |
| | | database = env.openDatabase(txn, fullName, dbConfig); |
| | | assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME, |
| | | "openDatabase", |
| | | "open db=" + database.getDatabaseName() + |
| | | " txnid=" + txn.getId()); |
| | | transactionCommit(txn); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | transactionAbort(txn); |
| | | throw e; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | database = env.openDatabase(null, fullName, dbConfig); |
| | | assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME, |
| | | "openDatabase", |
| | | "open db=" + database.getDatabaseName() + |
| | | " txnid=none"); |
| | | } |
| | | |
| | | // Insert into the list of database handles. |
| | | databases.add(database); |
| | | |
| | | return database; |
| | | } |
| | | |
| | | /** |
| | | * Register a cursor with the entryContainer. The entryContainer will then |
| | | * take care of closing the cursor when the entryContainer is closed. |
| | | * |
| | | * @param cursor A cursor to one of the databases in the entryContainer. |
| | | */ |
| | | public synchronized void addCursor(Cursor cursor) |
| | | { |
| | | cursors.add(cursor); |
| | | } |
| | | |
| | | /** |
| | | * Begin a leaf transaction using the default configuration. |
| | | * Provides assertion debug logging. |
| | | * @return A JE transaction handle. |
| | | * @throws DatabaseException If an error occurs while attempting to begin |
| | | * a new transaction. |
| | | */ |
| | | public static Transaction beginTransaction() |
| | | throws DatabaseException |
| | | { |
| | | Transaction parentTxn = null; |
| | | TransactionConfig txnConfig = null; |
| | | Transaction txn = env.beginTransaction(parentTxn, txnConfig); |
| | | assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME, |
| | | "beginTransaction", "begin txnid=" + txn.getId()); |
| | | return txn; |
| | | } |
| | | |
| | | /** |
| | | * Commit a transaction. |
| | | * Provides assertion debug logging. |
| | | * @param txn The JE transaction handle. |
| | | * @throws DatabaseException If an error occurs while attempting to commit |
| | | * the transaction. |
| | | */ |
| | | public static void transactionCommit(Transaction txn) |
| | | throws DatabaseException |
| | | { |
| | | if (txn != null) |
| | | { |
| | | txn.commit(); |
| | | assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME, |
| | | "transactionCommit", "commit txnid=" + |
| | | txn.getId()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Abort a transaction. |
| | | * Provides assertion debug logging. |
| | | * @param txn The JE transaction handle. |
| | | * @throws DatabaseException If an error occurs while attempting to abort the |
| | | * transaction. |
| | | */ |
| | | public static void transactionAbort(Transaction txn) |
| | | throws DatabaseException |
| | | { |
| | | if (txn != null) |
| | | { |
| | | txn.abort(); |
| | | assert Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME, |
| | | "transactionAbort", "abort txnid=" + |
| | | txn.getId()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Debug log a read or write access to the database. |
| | | * @param operation The operation label: "read", "put", "insert". |
| | | * @param category The log category for raw data value logging |
| | | * @param status The JE return status code of the operation. |
| | | * @param database The JE database handle operated on. |
| | | * @param txn The JE transaction handle used in the operation. |
| | | * @param key The database key operated on. |
| | | * @param data The database value read or written. |
| | | * @return true so that the method can be used in an assertion |
| | | * @throws DatabaseException If an error occurs while retrieving information |
| | | * about the JE objects provided to the method. |
| | | */ |
| | | private static boolean debugAccess(String operation, |
| | | DebugLogCategory category, |
| | | OperationStatus status, |
| | | Database database, |
| | | Transaction txn, |
| | | DatabaseEntry key, DatabaseEntry data) |
| | | throws DatabaseException |
| | | { |
| | | // Build the string that is common to category DATABASE_ACCESS and |
| | | // DATABASE_READ/DATABASE_WRITE |
| | | StringBuilder builder = new StringBuilder(); |
| | | builder.append(operation); |
| | | if (status == OperationStatus.SUCCESS) |
| | | { |
| | | builder.append(" (ok)"); |
| | | } |
| | | else |
| | | { |
| | | builder.append(" ("); |
| | | builder.append(status.toString()); |
| | | builder.append(")"); |
| | | } |
| | | builder.append(" db="); |
| | | builder.append(database.getDatabaseName()); |
| | | if (txn != null) |
| | | { |
| | | builder.append(" txnid="); |
| | | builder.append(txn.getId()); |
| | | } |
| | | else |
| | | { |
| | | builder.append(" txnid=none"); |
| | | } |
| | | Debug.debugMessage(DATABASE_ACCESS, VERBOSE, CLASS_NAME, |
| | | "debugAccess", builder.toString()); |
| | | |
| | | // If the operation was successful we log the same common information |
| | | // plus the key and data under category DATABASE_READ or DATABASE_WRITE |
| | | if (status == OperationStatus.SUCCESS) |
| | | { |
| | | builder.append(" key:"); |
| | | builder.append(ServerConstants.EOL); |
| | | StaticUtils.byteArrayToHexPlusAscii(builder, key.getData(), 0); |
| | | if (data != null) |
| | | { |
| | | builder.append("data(len="); |
| | | builder.append(data.getSize()); |
| | | builder.append("):"); |
| | | builder.append(ServerConstants.EOL); |
| | | StaticUtils.byteArrayToHexPlusAscii(builder, data.getData(), 0); |
| | | } |
| | | Debug.debugMessage(category, VERBOSE, CLASS_NAME, |
| | | "debugAccess", builder.toString()); |
| | | /* |
| | | if (category == DATABASE_WRITE) |
| | | { |
| | | System.out.println(builder.toString()); |
| | | } |
| | | */ |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Insert a record into a JE database, with optional debug logging. This is a |
| | | * simple wrapper around the JE Database.putNoOverwrite method. |
| | | * @param database The JE database handle. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The record key. |
| | | * @param data The record value. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus insert(Database database, Transaction txn, |
| | | DatabaseEntry key, DatabaseEntry data) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = database.putNoOverwrite(txn, key, data); |
| | | assert debugAccess("insert", DATABASE_WRITE, |
| | | status, database, txn, key, data); |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Insert a record into a JE database through a cursor, with optional debug |
| | | * logging. This is a simple wrapper around the JE Cursor.putNoOverwrite |
| | | * method. |
| | | * @param cursor The JE cursor handle. |
| | | * @param key The record key. |
| | | * @param data The record value. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus cursorInsert(Cursor cursor, |
| | | DatabaseEntry key, |
| | | DatabaseEntry data) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = cursor.putNoOverwrite(key, data); |
| | | assert debugAccess("cursorInsert", DATABASE_WRITE, |
| | | status, cursor.getDatabase(), null, key, data); |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Replace or insert a record into a JE database, with optional debug logging. |
| | | * This is a simple wrapper around the JE Database.put method. |
| | | * @param database The JE database handle. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The record key. |
| | | * @param data The record value. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus put(Database database, Transaction txn, |
| | | DatabaseEntry key, DatabaseEntry data) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = database.put(txn, key, data); |
| | | assert debugAccess("put", DATABASE_WRITE, |
| | | status, database, txn, key, data); |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Replace or insert a record into a JE database through a cursor, with |
| | | * optional debug logging. This is a simple wrapper around the JE Cursor.put |
| | | * method. |
| | | * @param cursor The JE cursor handle. |
| | | * @param key The record key. |
| | | * @param data The record value. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus cursorPut(Cursor cursor, |
| | | DatabaseEntry key, |
| | | DatabaseEntry data) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = cursor.put(key, data); |
| | | assert debugAccess("cursorPut", DATABASE_WRITE, |
| | | status, cursor.getDatabase(), null, key, data); |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Read a record from a JE database, with optional debug logging. This is a |
| | | * simple wrapper around the JE Database.get method. |
| | | * @param database The JE database handle. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The key of the record to be read. |
| | | * @param data The record value returned as output. Its byte array does not |
| | | * need to be initialized by the caller. |
| | | * @param lockMode The JE locking mode to be used for the read. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus read(Database database, Transaction txn, |
| | | DatabaseEntry key, DatabaseEntry data, |
| | | LockMode lockMode) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = database.get(txn, key, data, lockMode); |
| | | assert debugAccess("read", DATABASE_READ, |
| | | status, database, txn, key, data); |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Read a record from a JE database through a cursor, with optional debug |
| | | * logging. This is a simple wrapper around the JE Cursor.getSearchKey method. |
| | | * @param cursor The JE cursor handle. |
| | | * @param key The key of the record to be read. |
| | | * @param data The record value returned as output. Its byte array does not |
| | | * need to be initialized by the caller. |
| | | * @param lockMode The JE locking mode to be used for the read. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus cursorRead(Cursor cursor, |
| | | DatabaseEntry key, |
| | | DatabaseEntry data, |
| | | LockMode lockMode) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = cursor.getSearchKey(key, data, lockMode); |
| | | assert debugAccess("cursorRead", DATABASE_READ, |
| | | status, cursor.getDatabase(), null, key, data); |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Delete a record from a JE database, with optional debug logging. This is a |
| | | * simple wrapper around the JE Database.delete method. |
| | | * @param database The JE database handle. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The key of the record to be read. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus delete(Database database, Transaction txn, |
| | | DatabaseEntry key) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = database.delete(txn, key); |
| | | assert debugAccess("delete", DATABASE_WRITE, |
| | | status, database, txn, key, null); |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Remove a database from disk. |
| | | * |
| | | * @param name The short database name, to which the entryContainer name will |
| | | * be added. |
| | | * @throws DatabaseException If an error occurs while attempting to delete the |
| | | * database. |
| | | */ |
| | | public void removeDatabase(String name) throws DatabaseException |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | buildDatabaseName(builder, name); |
| | | String fullName = builder.toString(); |
| | | env.removeDatabase(null, fullName); |
| | | } |
| | | |
| | | /** |
| | | * Remove from disk all the databases in this entryContainer. |
| | | * |
| | | * @throws DatabaseException If an error occurs while attempting to delete any |
| | | * database. |
| | | */ |
| | | private void removeAllDatabases() throws DatabaseException |
| | | { |
| | | for(Database database : databases) |
| | | { |
| | | String name = database.getDatabaseName(); |
| | | env.removeDatabase(null, name); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This method constructs a container name from a base DN. Only alphanumeric |
| | | * characters are preserved, all other characters are replaced with an |
| | | * underscore. |
| | | * |
| | | * @return The container name for the base DN. |
| | | */ |
| | | public String getContainerName() |
| | | { |
| | | |
| | | String normStr = baseDN.toNormalizedString(); |
| | | StringBuilder builder = new StringBuilder(normStr.length()); |
| | | for (int i = 0; i < normStr.length(); i++) |
| | | { |
| | | char ch = normStr.charAt(i); |
| | | if (Character.isLetterOrDigit(ch)) |
| | | { |
| | | builder.append(ch); |
| | | } |
| | | else |
| | | { |
| | | builder.append('_'); |
| | | } |
| | | } |
| | | return builder.toString(); |
| | | } |
| | | |
| | | /** |
| | | * Get the baseDN this entry container is responsible for. |
| | | * |
| | | * @return The Base DN for this entry container. |
| | | */ |
| | | public DN getBaseDN() |
| | | { |
| | | return baseDN; |
| | | } |
| | | } |
| | |
| | | import com.sleepycat.je.CursorConfig; |
| | | import com.sleepycat.je.DatabaseEntry; |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.Environment; |
| | | import com.sleepycat.je.LockMode; |
| | | import com.sleepycat.je.OperationStatus; |
| | | |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | |
| | | import org.opends.server.util.StaticUtils; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Timer; |
| | | import java.util.TimerTask; |
| | | import java.util.*; |
| | | |
| | | import static org.opends.server.loggers.Debug.debugException; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | |
| | | private LDIFExportConfig exportConfig; |
| | | |
| | | /** |
| | | * The JE backend instance to be exported. |
| | | */ |
| | | private Backend backend; |
| | | |
| | | /** |
| | | * The configuration of the JE backend instance. |
| | | */ |
| | | private Config config; |
| | | |
| | | /** |
| | | * The number of milliseconds between job progress reports. |
| | | */ |
| | | private long progressInterval = 10000; |
| | |
| | | /** |
| | | * Create a new export job. |
| | | * |
| | | * @param backend The JE backend performing the export job. |
| | | * @param config The JE backend configuration. |
| | | * @param exportConfig The requested LDIF export configuration. |
| | | */ |
| | | public ExportJob(Backend backend, Config config, |
| | | LDIFExportConfig exportConfig) |
| | | public ExportJob(LDIFExportConfig exportConfig) |
| | | { |
| | | this.exportConfig = exportConfig; |
| | | this.backend = backend; |
| | | this.config = config; |
| | | } |
| | | |
| | | /** |
| | | * Export entries from the backend to an LDIF file. |
| | | * @param env A handle to the JE database environment of the backend. |
| | | * @param rootContainer The root container to export. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws IOException If an I/O error occurs while writing an entry. |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | * @throws LDIFException If an error occurs while trying to determine whether |
| | | * to write an entry. |
| | | */ |
| | | public void exportLDIF(Environment env) |
| | | public void exportLDIF(RootContainer rootContainer) |
| | | throws IOException, LDIFException, DatabaseException, JebException |
| | | { |
| | | // Open the containers read-only. |
| | | List<DN> includeBranches = exportConfig.getIncludeBranches(); |
| | | DN baseDNs[] = config.getBaseDNs(); |
| | | ArrayList<EntryContainer> containers = |
| | | new ArrayList<EntryContainer>(baseDNs.length); |
| | | for (DN baseDN : baseDNs) |
| | | DN baseDN; |
| | | ArrayList<EntryContainer> exportContainers = |
| | | new ArrayList<EntryContainer>(); |
| | | |
| | | for (EntryContainer entryContainer : rootContainer.getEntryContainers()) |
| | | { |
| | | // Skip containers that are not covered by the include branches. |
| | | baseDN = entryContainer.getBaseDN(); |
| | | |
| | | boolean includeBase = false; |
| | | if (includeBranches == null || includeBranches.isEmpty()) |
| | | { |
| | | includeBase = true; |
| | | exportContainers.add(entryContainer); |
| | | } |
| | | else |
| | | { |
| | |
| | | if (includeBranch.isDescendantOf(baseDN) || |
| | | includeBranch.isAncestorOf(baseDN)) |
| | | { |
| | | includeBase = true; |
| | | exportContainers.add(entryContainer); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (includeBase) |
| | | { |
| | | String containerName = BackendImpl.getContainerName(baseDN); |
| | | Container container = new Container(env, containerName); |
| | | EntryContainer entryContainer = |
| | | new EntryContainer(backend, config, container); |
| | | if (env.getConfig().getReadOnly()) |
| | | { |
| | | entryContainer.openReadOnly(); |
| | | } |
| | | else |
| | | { |
| | | entryContainer.open(); |
| | | } |
| | | containers.add(entryContainer); |
| | | } |
| | | |
| | | } |
| | | |
| | | // Make a note of the time we started. |
| | | long startTime = System.currentTimeMillis(); |
| | | |
| | | // Start a timer for the progress report. |
| | | Timer timer = new Timer(); |
| | | TimerTask progressTask = new ProgressTask(); |
| | | timer.scheduleAtFixedRate(progressTask, progressInterval, |
| | | progressInterval); |
| | | |
| | | // Iterate through the containers. |
| | | try |
| | | { |
| | | // Start a timer for the progress report. |
| | | Timer timer = new Timer(); |
| | | TimerTask progressTask = new ProgressTask(); |
| | | timer.scheduleAtFixedRate(progressTask, progressInterval, |
| | | progressInterval); |
| | | |
| | | // Iterate through the containers. |
| | | try |
| | | for (EntryContainer exportContainer : exportContainers) |
| | | { |
| | | for (EntryContainer ec : containers) |
| | | { |
| | | exportContainer(ec); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | timer.cancel(); |
| | | exportContainer(exportContainer); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | for (EntryContainer ec : containers) |
| | | { |
| | | ec.close(); |
| | | } |
| | | timer.cancel(); |
| | | } |
| | | |
| | | |
| | | long finishTime = System.currentTimeMillis(); |
| | | long totalTime = (finishTime - startTime); |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * Export the entries in a single entry container, in other words from |
| | | * Export the entries in a single entry entryContainer, in other words from |
| | | * one of the base DNs. |
| | | * @param entryContainer The entry container of those entries to be exported. |
| | | * @param entryContainer The entry container that holds the entries to be |
| | | * exported. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws IOException If an error occurs while writing an entry. |
| | | * @throws LDIFException If an error occurs while trying to determine |
| | |
| | | public class ID2Entry |
| | | { |
| | | /** |
| | | * The database container. |
| | | * The database entryContainer. |
| | | */ |
| | | private Container container; |
| | | private EntryContainer entryContainer; |
| | | |
| | | /** |
| | | * The JE database configuration. |
| | |
| | | private DataConfig dataConfig; |
| | | |
| | | /** |
| | | * The name of the database within the container. |
| | | * The name of the database within the entryContainer. |
| | | */ |
| | | private String name; |
| | | |
| | |
| | | |
| | | /** |
| | | * Create a new ID2Entry object. |
| | | * @param container The container of the entry database. |
| | | * @param entryContainer The entryContainer of the entry database. |
| | | * @param dbConfig The JE database configuration to be used to open the |
| | | * underlying JE database. |
| | | * @param dataConfig The desired compression and encryption options for data |
| | | * stored in the entry database. |
| | | * @param name The name of the entry database. |
| | | */ |
| | | public ID2Entry(Container container, DatabaseConfig dbConfig, |
| | | public ID2Entry(EntryContainer entryContainer, DatabaseConfig dbConfig, |
| | | DataConfig dataConfig, String name) |
| | | { |
| | | this.container = container; |
| | | this.entryContainer = entryContainer; |
| | | this.dbConfig = dbConfig; |
| | | this.name = name; |
| | | this.dataConfig = dataConfig; |
| | |
| | | |
| | | /** |
| | | * Get a handle to the database. It returns a per-thread handle to avoid |
| | | * any thread contention on the database handle. The container is |
| | | * any thread contention on the database handle. The entryContainer is |
| | | * responsible for closing all handles. |
| | | * |
| | | * @return A database handle. |
| | |
| | | Database database = threadLocalDatabase.get(); |
| | | if (database == null) |
| | | { |
| | | database = container.openDatabase(dbConfig, name); |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | } |
| | | return database; |
| | |
| | | DatabaseEntry data = entryData(entry); |
| | | |
| | | OperationStatus status; |
| | | status = Container.insert(getDatabase(), txn, key, data); |
| | | status = EntryContainer.insert(getDatabase(), txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | DatabaseEntry data = entryData(entry); |
| | | |
| | | OperationStatus status; |
| | | status = Container.put(getDatabase(), txn, key, data); |
| | | status = EntryContainer.put(getDatabase(), txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status; |
| | | status = Container.put(getDatabase(), txn, key, data); |
| | | status = EntryContainer.put(getDatabase(), txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | { |
| | | DatabaseEntry key = id.getDatabaseEntry(); |
| | | |
| | | OperationStatus status = Container.delete(getDatabase(), txn, key); |
| | | OperationStatus status = EntryContainer.delete(getDatabase(), txn, key); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | OperationStatus status; |
| | | status = Container.read(getDatabase(), txn, key, data, LockMode.DEFAULT); |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, |
| | | LockMode.DEFAULT); |
| | | |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | |
| | | key = id.getDatabaseEntry(); |
| | | |
| | | // Read the current count, if any. |
| | | OperationStatus status = Container.read(getDatabase(), txn, |
| | | OperationStatus status = EntryContainer.read(getDatabase(), txn, |
| | | key, data, LockMode.DEFAULT); |
| | | |
| | | // Parse the current count. |
| | |
| | | key = id.getDatabaseEntry(); |
| | | |
| | | // Read the current count, if any. |
| | | OperationStatus status = Container.read(getDatabase(), txn, |
| | | OperationStatus status = EntryContainer.read(getDatabase(), txn, |
| | | key, data, LockMode.RMW); |
| | | |
| | | // Parse the current count. |
| | |
| | | // Write it. |
| | | byte[] bytes = JebFormat.entryIDToDatabase(count); |
| | | data.setData(bytes); |
| | | Container.put(getDatabase(), txn, key, data); |
| | | EntryContainer.put(getDatabase(), txn, key, data); |
| | | } |
| | | } |
| | |
| | | public class ImportContext |
| | | { |
| | | /** |
| | | * The name of the container for the destination base DN. |
| | | * The name of the entryContainer for the destination base DN. |
| | | */ |
| | | private String containerName; |
| | | |
| | |
| | | private LDIFReader ldifReader; |
| | | |
| | | /** |
| | | * The entry container for the destination base DN. |
| | | * The entry entryContainer for the destination base DN. |
| | | */ |
| | | private EntryContainer entryContainer; |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * Set the name of the container for the destination base DN. |
| | | * @param containerName The container name. |
| | | * Set the name of the entryContainer for the destination base DN. |
| | | * @param containerName The entryContainer name. |
| | | */ |
| | | public void setContainerName(String containerName) |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the name of the container for the destination base DN. |
| | | * @return The container name. |
| | | * Get the name of the entryContainer for the destination base DN. |
| | | * @return The entryContainer name. |
| | | */ |
| | | public String getContainerName() |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Set the entry container for the destination base DN. |
| | | * @param entryContainer The entry container for the destination base DN. |
| | | * Set the entry entryContainer for the destination base DN. |
| | | * @param entryContainer The entry entryContainer for the destination base DN. |
| | | */ |
| | | public void setEntryContainer(EntryContainer entryContainer) |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the entry container for the destination base DN. |
| | | * @return The entry container for the destination base DN. |
| | | * Get the entry entryContainer for the destination base DN. |
| | | * @return The entry entryContainer for the destination base DN. |
| | | */ |
| | | public EntryContainer getEntryContainer() |
| | | { |
| | |
| | | package org.opends.server.backends.jeb; |
| | | |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.Environment; |
| | | import com.sleepycat.je.EnvironmentConfig; |
| | | import com.sleepycat.je.EnvironmentStats; |
| | | import com.sleepycat.je.StatsConfig; |
| | | import com.sleepycat.je.Transaction; |
| | |
| | | private Config config; |
| | | |
| | | /** |
| | | * The database environment. |
| | | * The root container used for this import job. |
| | | */ |
| | | private Environment env; |
| | | private RootContainer rootContainer; |
| | | |
| | | /** |
| | | * The LDIF import configuration. |
| | |
| | | * reading, or while reading from the LDIF file. |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | public void importLDIF() throws DatabaseException, IOException, JebException |
| | | public void importLDIF() |
| | | throws DatabaseException, IOException, JebException |
| | | { |
| | | File backendDirectory = config.getBackendDirectory(); |
| | | |
| | | EnvironmentConfig envConfig = config.getEnvironmentConfig(); |
| | | envConfig.setConfigParam("je.env.runCheckpointer", "false"); |
| | | /* |
| | | envConfig.setConfigParam("je.env.runCleaner", "false"); |
| | | envConfig.setConfigParam("je.log.numBuffers", "2"); |
| | |
| | | envConfig.setConfigParam("je.log.totalBufferBytes", "30000000"); |
| | | envConfig.setConfigParam("je.log.fileMax", "100000000"); |
| | | */ |
| | | |
| | | rootContainer = new RootContainer(config, backend); |
| | | if (ldifImportConfig.appendToExistingData()) |
| | | { |
| | | envConfig.setTransactional(true); |
| | | envConfig.setTxnNoSync(true); |
| | | rootContainer.open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | false, true, true, true, true, false); |
| | | } |
| | | else |
| | | { |
| | | envConfig.setTransactional(false); |
| | | envConfig.setConfigParam("je.env.isLocking", "false"); |
| | | rootContainer.open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | false, true, false, false, false, false); |
| | | } |
| | | |
| | | env = new Environment(backendDirectory, envConfig); |
| | | |
| | | if (!ldifImportConfig.appendToExistingData()) |
| | | { |
| | | // 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. |
| | | env.close(); |
| | | EnvManager.removeFiles(backendDirectory.getPath()); |
| | | env = new Environment(backendDirectory, envConfig); |
| | | rootContainer.close(); |
| | | EnvManager.removeFiles(config.getBackendDirectory().getPath()); |
| | | rootContainer.open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | false, true, false, false, false, false); |
| | | } |
| | | |
| | | // Divide the total buffer size by the number of threads |
| | |
| | | message, msgID); |
| | | |
| | | msgID = MSGID_JEB_IMPORT_ENVIRONMENT_CONFIG; |
| | | message = getMessage(msgID, env.getConfig().toString()); |
| | | message = getMessage(msgID, |
| | | rootContainer.getEnvironmentConfig().toString()); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | |
| | | Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO, |
| | | CLASS_NAME, "importLDIF", |
| | | env.getConfig().toString()); |
| | | rootContainer.getEnvironmentConfig().toString()); |
| | | |
| | | |
| | | // Create and open the containers for each base DN. |
| | | rootContainer.openEntryContainers(config.getBaseDNs()); |
| | | |
| | | // Create the import contextes for each base DN. |
| | | EntryID highestID = null; |
| | | for (DN baseDN : config.getBaseDNs()) |
| | | DN baseDN; |
| | | |
| | | for (EntryContainer entryContainer : rootContainer.getEntryContainers()) |
| | | { |
| | | String containerName = BackendImpl.getContainerName(baseDN); |
| | | Container container = new Container(env, containerName); |
| | | EntryContainer entryContainer = |
| | | new EntryContainer(backend, config, container); |
| | | |
| | | if (ldifImportConfig.appendToExistingData()) |
| | | { |
| | | entryContainer.open(); |
| | | } |
| | | else |
| | | { |
| | | // We will need this code when the import can specify a subset |
| | | // of the base DNs in the backend. |
| | | /* |
| | | long t1, t2; |
| | | |
| | | String msg = String.format("Removing existing data for base DN '%s'", |
| | | baseDN); |
| | | System.out.println(msg); |
| | | |
| | | t1 = System.currentTimeMillis(); |
| | | container.removeAllDatabases(); |
| | | t2 = System.currentTimeMillis(); |
| | | |
| | | msg = String.format("Data removed in %d seconds", (t2-t1)/1000); |
| | | System.out.println(msg); |
| | | */ |
| | | |
| | | entryContainer.openNonTransactional(true); |
| | | } |
| | | baseDN = entryContainer.getBaseDN(); |
| | | |
| | | // Keep track of the highest entry ID. |
| | | EntryID id = entryContainer.getHighestEntryID(); |
| | |
| | | importContext.setLDIFImportConfig(this.ldifImportConfig); |
| | | |
| | | importContext.setBaseDN(baseDN); |
| | | importContext.setContainerName(containerName); |
| | | importContext.setContainerName(entryContainer.getContainerName()); |
| | | importContext.setEntryContainer(entryContainer); |
| | | importContext.setBufferSize(bufferSize); |
| | | |
| | |
| | | } |
| | | finally |
| | | { |
| | | for (ImportContext ic : importMap.values()) |
| | | { |
| | | ic.getEntryContainer().close(); |
| | | } |
| | | rootContainer.close(); |
| | | |
| | | // Sync the environment to disk. |
| | | msgID = MSGID_JEB_IMPORT_CLOSING_DATABASE; |
| | | message = getMessage(msgID); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | env.close(); |
| | | } |
| | | |
| | | long finishTime = System.currentTimeMillis(); |
| | |
| | | public ProgressTask() throws DatabaseException |
| | | { |
| | | previousTime = System.currentTimeMillis(); |
| | | prevEnvStats = env.getStats(new StatsConfig()); |
| | | prevEnvStats = rootContainer.getEnvironmentStats(new StatsConfig()); |
| | | } |
| | | |
| | | /** |
| | |
| | | Runtime runtime = Runtime.getRuntime(); |
| | | long freeMemory = runtime.freeMemory() / bytesPerMegabyte; |
| | | |
| | | EnvironmentStats envStats = env.getStats(new StatsConfig()); |
| | | EnvironmentStats envStats = |
| | | rootContainer.getEnvironmentStats(new StatsConfig()); |
| | | long nCacheMiss = |
| | | envStats.getNCacheMiss() - prevEnvStats.getNCacheMiss(); |
| | | |
| | |
| | | private ImportContext importContext; |
| | | |
| | | /** |
| | | * The destination entry container for entries read from the queue. |
| | | * The destination entry entryContainer for entries read from the queue. |
| | | */ |
| | | private EntryContainer entryContainer; |
| | | |
| | | /** |
| | | * The entry database of the destination entry container. |
| | | * The entry database of the destination entry entryContainer. |
| | | */ |
| | | private ID2Entry id2entry; |
| | | |
| | | /** |
| | | * The referral database of the destination entry container. |
| | | * The referral database of the destination entry entryContainer. |
| | | */ |
| | | private DN2URI dn2uri; |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * The database container holding this index database. |
| | | * The database entryContainer holding this index database. |
| | | */ |
| | | private Container container; |
| | | private EntryContainer entryContainer; |
| | | |
| | | /** |
| | | * The JE database configuration. |
| | |
| | | private DatabaseConfig dbConfig; |
| | | |
| | | /** |
| | | * The name of the database within the container. |
| | | * The name of the database within the entryContainer. |
| | | */ |
| | | private String name; |
| | | |
| | |
| | | |
| | | /** |
| | | * Create a new index object. |
| | | * @param container The database container holding this index. |
| | | * @param name The name of the index database within the container. |
| | | * @param entryContainer The database entryContainer holding this index. |
| | | * @param name The name of the index database within the entryContainer. |
| | | * @param indexer The indexer object to construct index keys from LDAP |
| | | * attribute values. |
| | | * @param indexEntryLimit The configured limit on the number of entry IDs |
| | |
| | | * @param cursorEntryLimit The configured limit on the number of entry IDs |
| | | * that may be retrieved by cursoring through an index. |
| | | */ |
| | | public Index(Container container, String name, Indexer indexer, |
| | | public Index(EntryContainer entryContainer, String name, Indexer indexer, |
| | | int indexEntryLimit, int cursorEntryLimit) |
| | | { |
| | | this.container = container; |
| | | this.entryContainer = entryContainer; |
| | | this.name = name; |
| | | this.indexer = indexer; |
| | | this.comparator = indexer.getComparator(); |
| | |
| | | |
| | | /** |
| | | * Get a handle to the database. It returns a per-thread handle to avoid |
| | | * any thread contention on the database handle. The container is |
| | | * any thread contention on the database handle. The entryContainer is |
| | | * responsible for closing all handles. |
| | | * |
| | | * @return A database handle. |
| | |
| | | Database database = threadLocalDatabase.get(); |
| | | if (database == null) |
| | | { |
| | | database = container.openDatabase(dbConfig, name); |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | } |
| | | return database; |
| | |
| | | DatabaseEntry entryIDData = entryID.getDatabaseEntry(); |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | status = Container.read(getDatabase(), txn, key, data, lockMode); |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, lockMode); |
| | | |
| | | if (status == OperationStatus.SUCCESS) |
| | | { |
| | |
| | | |
| | | byte[] after = entryIDList.toDatabase(); |
| | | data.setData(after); |
| | | Container.put(getDatabase(), txn, key, data); |
| | | EntryContainer.put(getDatabase(), txn, key, data); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | Container.put(getDatabase(), txn, key, entryIDData); |
| | | EntryContainer.put(getDatabase(), txn, key, entryIDData); |
| | | } |
| | | } |
| | | |
| | |
| | | LockMode lockMode = LockMode.RMW; |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | status = Container.read(getDatabase(), txn, key, data, lockMode); |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, lockMode); |
| | | |
| | | if (status == OperationStatus.SUCCESS) |
| | | { |
| | |
| | | if (after == null) |
| | | { |
| | | // No more IDs, so remove the key |
| | | Container.delete(getDatabase(), txn, key); |
| | | EntryContainer.delete(getDatabase(), txn, key); |
| | | } |
| | | else |
| | | { |
| | | data.setData(after); |
| | | Container.put(getDatabase(), txn, key, data); |
| | | EntryContainer.put(getDatabase(), txn, key, data); |
| | | } |
| | | } |
| | | } |
| | |
| | | LockMode lockMode = LockMode.DEFAULT; |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | status = Container.read(getDatabase(), txn, key, data, lockMode); |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, lockMode); |
| | | if (status == OperationStatus.SUCCESS) |
| | | { |
| | | EntryIDSet entryIDList = |
| | |
| | | { |
| | | OperationStatus status; |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | status = Container.read(getDatabase(), txn, key, data, lockMode); |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, lockMode); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return new EntryIDSet(key.getData(), null); |
| | |
| | | if (after == null) |
| | | { |
| | | // No more IDs, so remove the key. |
| | | Container.delete(getDatabase(), txn, key); |
| | | EntryContainer.delete(getDatabase(), txn, key); |
| | | } |
| | | else |
| | | { |
| | |
| | | entryLimitExceededCount++; |
| | | } |
| | | data.setData(after); |
| | | Container.put(getDatabase(), txn, key, data); |
| | | EntryContainer.put(getDatabase(), txn, key, data); |
| | | } |
| | | } |
| | | |
| | |
| | | public static final int FILTER_CANDIDATE_THRESHOLD = 10; |
| | | |
| | | /** |
| | | * The entry container holding the attribute indexes. |
| | | * The entry entryContainer holding the attribute indexes. |
| | | */ |
| | | private EntryContainer entryContainer; |
| | | |
| | |
| | | /** |
| | | * Construct an index filter for a search operation. |
| | | * |
| | | * @param entryContainer The entry container. |
| | | * @param entryContainer The entry entryContainer. |
| | | * @param searchOp The search operation to be evaluated. |
| | | * |
| | | * @param debugBuilder If not null, a diagnostic string will be written |
| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2006 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.backends.jeb; |
| | | |
| | | import com.sleepycat.je.Database; |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.Environment; |
| | | import com.sleepycat.je.EnvironmentConfig; |
| | | import com.sleepycat.je.EnvironmentStats; |
| | | import com.sleepycat.je.PreloadConfig; |
| | | import com.sleepycat.je.PreloadStats; |
| | | import com.sleepycat.je.PreloadStatus; |
| | | import com.sleepycat.je.config.EnvironmentParams; |
| | | import com.sleepycat.je.config.ConfigParam; |
| | | import com.sleepycat.je.StatsConfig; |
| | | import com.sleepycat.je.CheckpointConfig; |
| | | |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.*; |
| | | import java.io.File; |
| | | import java.io.FilenameFilter; |
| | | |
| | | import org.opends.server.monitors.DatabaseEnvironmentMonitor; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.loggers.Debug; |
| | | import static org.opends.server.loggers.Error.logError; |
| | | import static org.opends.server.loggers.Debug.debugException; |
| | | import static org.opends.server.loggers.Debug.debugEnter; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.messages.JebMessages. |
| | | MSGID_JEB_CACHE_SIZE_AFTER_PRELOAD; |
| | | import static org.opends.server.messages.JebMessages. |
| | | MSGID_JEB_CLEAN_DATABASE_START; |
| | | import static org.opends.server.messages.JebMessages. |
| | | MSGID_JEB_CLEAN_DATABASE_MARKED; |
| | | import static org.opends.server.messages.JebMessages. |
| | | MSGID_JEB_CLEAN_DATABASE_FINISH; |
| | | import static org.opends.server.messages.JebMessages. |
| | | MSGID_JEB_SET_PERMISSIONS_FAILED; |
| | | import org.opends.server.api.Backend; |
| | | |
| | | /** |
| | | * Wrapper class for the JE environment. Root container holds all the entry |
| | | * containers for each base DN. It also maintains all the openings and closings |
| | | * of the entry containers. |
| | | */ |
| | | public class RootContainer |
| | | { |
| | | /** |
| | | * The fully-qualified name of this class for debugging purposes. |
| | | */ |
| | | private static final String CLASS_NAME = |
| | | "org.opends.server.backends.jeb.RootContainer"; |
| | | |
| | | /** |
| | | * The JE database environment. |
| | | */ |
| | | private Environment env; |
| | | |
| | | /** |
| | | * The backend configuration. |
| | | */ |
| | | private Config config; |
| | | |
| | | /** |
| | | * The backend to which this entry root container belongs. |
| | | */ |
| | | private Backend backend; |
| | | |
| | | /** |
| | | * The database environment monitor for this JE environment. |
| | | */ |
| | | private DatabaseEnvironmentMonitor monitor; |
| | | |
| | | /** |
| | | * A configurable component to handle changes to the configuration of |
| | | * the database environment. |
| | | */ |
| | | private ConfigurableEnvironment configurableEnv; |
| | | |
| | | /** |
| | | * The base DNs contained in this entryContainer. |
| | | */ |
| | | private ConcurrentHashMap<DN, EntryContainer> entryContainers; |
| | | |
| | | /** |
| | | * Creates a new RootContainer object. Each root container represents a JE |
| | | * environment. |
| | | * |
| | | * @param config The configuration of the JE backend. |
| | | * @param backend A reference to the JE back end that is creating this |
| | | * root container. |
| | | */ |
| | | public RootContainer(Config config, Backend backend) |
| | | { |
| | | this.env = null; |
| | | this.configurableEnv = null; |
| | | this.monitor = null; |
| | | this.entryContainers = new ConcurrentHashMap<DN, EntryContainer>(); |
| | | this.backend = backend; |
| | | this.config = config; |
| | | } |
| | | |
| | | /** |
| | | * Helper method to apply database directory permissions and create a new |
| | | * JE environment. |
| | | * |
| | | * @param backendDirectory The environment home directory for JE. |
| | | * @param backendPermission The file permissions for the environment home |
| | | * directory. |
| | | * @param envConfig The JE environment configuration. |
| | | * @throws DatabaseException If an error occurs when creating the environment. |
| | | */ |
| | | private void open(File backendDirectory, |
| | | FilePermission backendPermission, |
| | | EnvironmentConfig envConfig) throws DatabaseException |
| | | { |
| | | // Get the backend database backendDirectory permissions and apply |
| | | try |
| | | { |
| | | if(!FilePermission.setPermissions(backendDirectory, backendPermission)) |
| | | { |
| | | throw new Exception(); |
| | | } |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | // Log an warning that the permissions were not set. |
| | | int msgID = MSGID_JEB_SET_PERMISSIONS_FAILED; |
| | | String message = getMessage(msgID, backendDirectory.getPath()); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_WARNING, |
| | | message, msgID); |
| | | } |
| | | |
| | | // Open the database environment |
| | | env = new Environment(backendDirectory, |
| | | envConfig); |
| | | |
| | | Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO, |
| | | CLASS_NAME, "initializeBackend", |
| | | env.getConfig().toString()); |
| | | } |
| | | |
| | | /** |
| | | * Opens the root container. |
| | | * |
| | | * @throws DatabaseException If an error occurs when opening the container. |
| | | */ |
| | | public void open() throws DatabaseException |
| | | { |
| | | open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | config.getEnvironmentConfig()); |
| | | } |
| | | |
| | | /** |
| | | * Opens the root container using the configuration parameters provided. Any |
| | | * configuration parameters provided will override the parameters in the |
| | | * JE configuration object. |
| | | * |
| | | * @param backendDirectory The environment home directory for JE. |
| | | * @param backendPermission he file permissions for the environment home |
| | | * directory. |
| | | * @param readOnly Open the container in read only mode. |
| | | * @param allowCreate Allow creating new entries in the container. |
| | | * @param transactional Use transactions on operations. |
| | | * @param txnNoSync Use asynchronous transactions. |
| | | * @param isLocking Create the environment with locking. |
| | | * @param runCheckPointer Start the checkpointer. |
| | | * @throws DatabaseException If an error occurs when openinng the container. |
| | | */ |
| | | public void open(File backendDirectory, |
| | | FilePermission backendPermission, |
| | | boolean readOnly, |
| | | boolean allowCreate, |
| | | boolean transactional, |
| | | boolean txnNoSync, |
| | | boolean isLocking, |
| | | boolean runCheckPointer) throws DatabaseException |
| | | { |
| | | |
| | | EnvironmentConfig envConfig; |
| | | if(config.getEnvironmentConfig() != null) |
| | | { |
| | | envConfig = config.getEnvironmentConfig(); |
| | | } |
| | | else |
| | | { |
| | | envConfig = new EnvironmentConfig(); |
| | | } |
| | | envConfig.setReadOnly(readOnly); |
| | | envConfig.setAllowCreate(allowCreate); |
| | | envConfig.setTransactional(transactional); |
| | | envConfig.setTxnNoSync(txnNoSync); |
| | | envConfig.setConfigParam("je.env.isLocking", String.valueOf(isLocking)); |
| | | envConfig.setConfigParam("je.env.runCheckpointer", |
| | | String.valueOf(runCheckPointer)); |
| | | |
| | | open(backendDirectory, backendPermission, envConfig); |
| | | } |
| | | |
| | | /** |
| | | * Opens the entry container for a base DN. If the entry container does not |
| | | * exist for the base DN, it will be created. The entry container will be |
| | | * opened with the same mode as the root container. Any entry containers |
| | | * opened in a read only root container will also be read only. Any entry |
| | | * containers opened in a non transactional root container will also be non |
| | | * transactional. |
| | | * |
| | | * @param baseDN The base DN of the entry container to open. |
| | | * @return The opened entry container. |
| | | * @throws DatabaseException If an error occurs while opening the entry |
| | | * container. |
| | | */ |
| | | public EntryContainer openEntryContainer(DN baseDN) throws DatabaseException |
| | | { |
| | | EntryContainer ec = new EntryContainer(baseDN, backend, config, env); |
| | | if(env.getConfig().getReadOnly()) |
| | | { |
| | | ec.openReadOnly(); |
| | | } |
| | | else if(!env.getConfig().getTransactional()) |
| | | { |
| | | ec.openNonTransactional(true); |
| | | } |
| | | else |
| | | { |
| | | ec.open(); |
| | | } |
| | | this.entryContainers.put(baseDN, ec); |
| | | |
| | | return ec; |
| | | } |
| | | |
| | | /** |
| | | * Opens the entry containers for multiple base DNs. |
| | | * |
| | | * @param baseDNs The base DNs of the entry containers to open. |
| | | * @throws DatabaseException If an error occurs while opening the entry |
| | | * container. |
| | | */ |
| | | public void openEntryContainers(DN[] baseDNs) throws DatabaseException |
| | | { |
| | | for(DN baseDN : baseDNs) |
| | | { |
| | | openEntryContainer(baseDN); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Close the entry container for a base DN. |
| | | * |
| | | * @param baseDN The base DN of the entry container to close. |
| | | * @throws DatabaseException If an error occurs while closing the entry |
| | | * container. |
| | | */ |
| | | public void closeEntryContainer(DN baseDN) throws DatabaseException |
| | | { |
| | | getEntryContainer(baseDN).close(); |
| | | entryContainers.remove(baseDN); |
| | | } |
| | | |
| | | /** |
| | | * Close and remove a entry container for a base DN from disk. |
| | | * |
| | | * @param baseDN The base DN of the entry container to remove. |
| | | * @throws DatabaseException If an error occurs while removing the entry |
| | | * container. |
| | | */ |
| | | public void removeEntryContainer(DN baseDN) throws DatabaseException |
| | | { |
| | | getEntryContainer(baseDN).close(); |
| | | getEntryContainer(baseDN).removeContainer(); |
| | | entryContainers.remove(baseDN); |
| | | } |
| | | |
| | | /** |
| | | * Get the ConfigurableEnvironment object for JE environment used by this |
| | | * root container. |
| | | * |
| | | * @return The ConfigurableEnvironment object. |
| | | */ |
| | | public ConfigurableEnvironment getConfigurableEnvironment() |
| | | { |
| | | if(configurableEnv == null) |
| | | { |
| | | DN envConfigDN = config.getEnvConfigDN(); |
| | | if (envConfigDN != null) |
| | | { |
| | | configurableEnv = new ConfigurableEnvironment(envConfigDN, env); |
| | | } |
| | | } |
| | | |
| | | return configurableEnv; |
| | | } |
| | | |
| | | /** |
| | | * Get the DatabaseEnvironmentMonitor object for JE environment used by this |
| | | * root container. |
| | | * |
| | | * @return The DatabaseEnvironmentMonito object. |
| | | */ |
| | | public DatabaseEnvironmentMonitor getMonitorProvider() |
| | | { |
| | | if(monitor == null) |
| | | { |
| | | String monitorName = backend.getBackendID() + " Database Environment"; |
| | | monitor = new DatabaseEnvironmentMonitor(monitorName, env); |
| | | } |
| | | |
| | | return monitor; |
| | | } |
| | | |
| | | /** |
| | | * Preload the database cache. There is no preload if the configured preload |
| | | * time limit is zero. |
| | | */ |
| | | public void preload() |
| | | { |
| | | assert debugEnter(CLASS_NAME, "preload"); |
| | | |
| | | long timeLimit = config.getPreloadTimeLimit(); |
| | | |
| | | if (timeLimit > 0) |
| | | { |
| | | // Get a list of all the databases used by the backend. |
| | | ArrayList<Database> dbList = new ArrayList<Database>(); |
| | | for (EntryContainer ec : entryContainers.values()) |
| | | { |
| | | ec.listDatabases(dbList); |
| | | } |
| | | |
| | | // Sort the list in order of priority. |
| | | Collections.sort(dbList, new DbPreloadComparator()); |
| | | |
| | | // Preload each database until we reach the time limit or the cache |
| | | // is filled. |
| | | try |
| | | { |
| | | long timeEnd = System.currentTimeMillis() + timeLimit; |
| | | |
| | | // Configure preload of Leaf Nodes (LNs) containing the data values. |
| | | PreloadConfig preloadConfig = new PreloadConfig(); |
| | | preloadConfig.setLoadLNs(true); |
| | | |
| | | for (Database db : dbList) |
| | | { |
| | | // Calculate the remaining time. |
| | | long timeRemaining = timeEnd - System.currentTimeMillis(); |
| | | if (timeRemaining <= 0) |
| | | { |
| | | break; |
| | | } |
| | | |
| | | preloadConfig.setMaxMillisecs(timeRemaining); |
| | | PreloadStats preloadStats = db.preload(preloadConfig); |
| | | /* |
| | | System.out.println("file=" + db.getDatabaseName() + |
| | | " LNs=" + preloadStats.getNLNsLoaded()); |
| | | */ |
| | | |
| | | // Stop if the cache is full or the time limit has been exceeded. |
| | | if (preloadStats.getStatus() != PreloadStatus.SUCCESS) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // Log an informational message about the size of the cache. |
| | | EnvironmentStats stats = env.getStats(new StatsConfig()); |
| | | long total = stats.getCacheTotalBytes(); |
| | | |
| | | int msgID = MSGID_JEB_CACHE_SIZE_AFTER_PRELOAD; |
| | | String message = getMessage(msgID, total / (1024 * 1024)); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message, |
| | | msgID); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | assert debugException(CLASS_NAME, "preload", e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Synchronously invokes the cleaner on the database environment then forces a |
| | | * checkpoint to delete the log files that are no longer in use. |
| | | * |
| | | * @throws DatabaseException If an error occurs while cleaning the database |
| | | * environment. |
| | | */ |
| | | private void cleanDatabase() |
| | | throws DatabaseException |
| | | { |
| | | assert debugEnter(CLASS_NAME, "cleanDatabase"); |
| | | |
| | | int msgID; |
| | | String message; |
| | | |
| | | FilenameFilter filenameFilter = new FilenameFilter() |
| | | { |
| | | public boolean accept(File d, String name) |
| | | { |
| | | return name.endsWith(".jdb"); |
| | | } |
| | | }; |
| | | |
| | | File backendDirectory = env.getHome(); |
| | | int beforeLogfileCount = backendDirectory.list(filenameFilter).length; |
| | | |
| | | msgID = MSGID_JEB_CLEAN_DATABASE_START; |
| | | message = getMessage(msgID, beforeLogfileCount, backendDirectory.getPath()); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message, |
| | | msgID); |
| | | |
| | | int currentCleaned = 0; |
| | | int totalCleaned = 0; |
| | | while ((currentCleaned = env.cleanLog()) > 0) |
| | | { |
| | | totalCleaned += currentCleaned; |
| | | } |
| | | |
| | | msgID = MSGID_JEB_CLEAN_DATABASE_MARKED; |
| | | message = getMessage(msgID, totalCleaned); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message, |
| | | msgID); |
| | | |
| | | if (totalCleaned > 0) |
| | | { |
| | | CheckpointConfig force = new CheckpointConfig(); |
| | | force.setForce(true); |
| | | env.checkpoint(force); |
| | | } |
| | | |
| | | int afterLogfileCount = backendDirectory.list(filenameFilter).length; |
| | | |
| | | msgID = MSGID_JEB_CLEAN_DATABASE_FINISH; |
| | | message = getMessage(msgID, afterLogfileCount); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, message, |
| | | msgID); |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Close the root entryContainer. |
| | | * |
| | | * @throws DatabaseException If an error occurs while attempting to close |
| | | * the entryContainer. |
| | | */ |
| | | public void close() throws DatabaseException |
| | | { |
| | | for(DN baseDN : entryContainers.keySet()) |
| | | { |
| | | entryContainers.get(baseDN).close(); |
| | | entryContainers.remove(baseDN); |
| | | } |
| | | |
| | | env.close(); |
| | | } |
| | | |
| | | /** |
| | | * Return all the entry containers in this root container. |
| | | * |
| | | * @return The entry containers in this root container. |
| | | */ |
| | | public Collection<EntryContainer> getEntryContainers() |
| | | { |
| | | return entryContainers.values(); |
| | | } |
| | | |
| | | /** |
| | | * Returns all the baseDNs this root container stores. |
| | | * |
| | | * @return The set of DNs this root container stores. |
| | | */ |
| | | public Set<DN> getBaseDNs() |
| | | { |
| | | return entryContainers.keySet(); |
| | | } |
| | | |
| | | /** |
| | | * Return the entry container for a specific base DN. |
| | | * |
| | | * @param baseDN The base DN of the entry container to retrive. |
| | | * @return The entry container for the base DN. |
| | | */ |
| | | public EntryContainer getEntryContainer(DN baseDN) |
| | | { |
| | | EntryContainer ec = null; |
| | | DN nodeDN = baseDN; |
| | | |
| | | while (ec == null && nodeDN != null) |
| | | { |
| | | ec = entryContainers.get(nodeDN); |
| | | if (ec == null) |
| | | { |
| | | nodeDN = nodeDN.getParent(); |
| | | } |
| | | } |
| | | |
| | | return ec; |
| | | } |
| | | |
| | | /** |
| | | * Apply new configuration to the JE environment. |
| | | * |
| | | * @param newConfig The new configuration to apply. |
| | | * @throws DatabaseException If an error occurs while applying the new |
| | | * configuration. |
| | | */ |
| | | public void applyNewConfig(Config newConfig) throws DatabaseException |
| | | { |
| | | // Check for changes to the database directory permissions |
| | | FilePermission oldPermission = config.getBackendPermission(); |
| | | FilePermission newPermission = newConfig.getBackendPermission(); |
| | | |
| | | if(!FilePermission.toUNIXMode(oldPermission).equals( |
| | | FilePermission.toUNIXMode(newPermission))) |
| | | { |
| | | try |
| | | { |
| | | if(!FilePermission.setPermissions(newConfig.getBackendDirectory(), |
| | | newPermission)) |
| | | { |
| | | throw new Exception(); |
| | | } |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | // Log an warning that the permissions were not set. |
| | | int msgID = MSGID_JEB_SET_PERMISSIONS_FAILED; |
| | | String message = getMessage(msgID, |
| | | config.getBackendDirectory().getPath()); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_WARNING, |
| | | message, msgID); |
| | | } |
| | | } |
| | | |
| | | // Check if any JE non-mutable properties were changed. |
| | | EnvironmentConfig oldEnvConfig = this.config.getEnvironmentConfig(); |
| | | EnvironmentConfig newEnvConfig = newConfig.getEnvironmentConfig(); |
| | | Map paramsMap = EnvironmentParams.SUPPORTED_PARAMS; |
| | | for (Object o : paramsMap.values()) |
| | | { |
| | | ConfigParam param = (ConfigParam)o; |
| | | if (!param.isMutable()) |
| | | { |
| | | String oldValue = oldEnvConfig.getConfigParam(param.getName()); |
| | | String newValue = newEnvConfig.getConfigParam(param.getName()); |
| | | if (!oldValue.equalsIgnoreCase(newValue)) |
| | | { |
| | | System.out.println("The change to the following property will " + |
| | | "take effect when the backend is restarted: " + |
| | | param.getName()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // This takes care of changes to the JE environment for those |
| | | // properties that are mutable at runtime. |
| | | env.setMutableConfig(newConfig.getEnvironmentConfig()); |
| | | |
| | | config = newConfig; |
| | | |
| | | Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO, |
| | | CLASS_NAME, "applyNewConfiguration", |
| | | env.getConfig().toString()); |
| | | } |
| | | |
| | | /** |
| | | * Get the environment stats of the JE environment used in this root |
| | | * container. |
| | | * |
| | | * @param statsConfig The configuration to use for the EnvironmentStats |
| | | * object. |
| | | * @return The environment status of the JE environment. |
| | | * @throws DatabaseException If an error occurs while retriving the stats |
| | | * object. |
| | | */ |
| | | public EnvironmentStats getEnvironmentStats(StatsConfig statsConfig) |
| | | throws DatabaseException |
| | | { |
| | | return env.getStats(statsConfig); |
| | | } |
| | | |
| | | /** |
| | | * Get the environment config of the JE environment used in this root |
| | | * container. |
| | | * |
| | | * @return The environment config of the JE environment. |
| | | * @throws DatabaseException If an error occurs while retriving the |
| | | * configuration object. |
| | | */ |
| | | public EnvironmentConfig getEnvironmentConfig() throws DatabaseException |
| | | { |
| | | return env.getConfig(); |
| | | } |
| | | } |
| | |
| | | import com.sleepycat.je.CursorConfig; |
| | | import com.sleepycat.je.DatabaseEntry; |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.Environment; |
| | | import com.sleepycat.je.EnvironmentConfig; |
| | | import com.sleepycat.je.EnvironmentStats; |
| | | import com.sleepycat.je.LockMode; |
| | | import com.sleepycat.je.OperationStatus; |
| | | import com.sleepycat.je.StatsConfig; |
| | | import com.sleepycat.je.Transaction; |
| | | |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.api.OrderingMatchingRule; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.loggers.Debug; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.ConditionResult; |
| | | import org.opends.server.types.DebugLogCategory; |
| | | import org.opends.server.types.DebugLogSeverity; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.messages.JebMessages.*; |
| | | |
| | | import java.io.File; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.HashMap; |
| | |
| | | private VerifyConfig verifyConfig; |
| | | |
| | | /** |
| | | * The JE backend to be verified. |
| | | */ |
| | | private Backend backend; |
| | | |
| | | /** |
| | | * The configuration of the JE backend. |
| | | */ |
| | | private Config config; |
| | | |
| | | /** |
| | | * A read-only JE database environment handle for the purpose of verification. |
| | | * The root container used for the verify job. |
| | | */ |
| | | private Environment env; |
| | | RootContainer rootContainer; |
| | | |
| | | /** |
| | | * The number of milliseconds between job progress reports. |
| | |
| | | /** |
| | | * Construct a VerifyJob. |
| | | * |
| | | * @param backend The backend performing the verify process. |
| | | * @param config The backend configuration. |
| | | * @param verifyConfig The verify configuration. |
| | | */ |
| | | public VerifyJob(Backend backend, Config config, VerifyConfig verifyConfig) |
| | | public VerifyJob(Config config, VerifyConfig verifyConfig) |
| | | { |
| | | this.verifyConfig = verifyConfig; |
| | | this.backend = backend; |
| | | this.config = config; |
| | | } |
| | | |
| | | /** |
| | | * Verify the backend. |
| | | * |
| | | * @param rootContainer The root container that holds the entries to verify. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | public void verifyBackend() throws DatabaseException, JebException |
| | | public void verifyBackend(RootContainer rootContainer) throws |
| | | DatabaseException, JebException |
| | | { |
| | | File backendDirectory = config.getBackendDirectory(); |
| | | |
| | | // Open the environment read-only. |
| | | EnvironmentConfig envConfig = config.getEnvironmentConfig(); |
| | | envConfig.setReadOnly(true); |
| | | envConfig.setAllowCreate(false); |
| | | envConfig.setTransactional(false); |
| | | env = new Environment(backendDirectory, envConfig); |
| | | |
| | | Debug.debugMessage(DebugLogCategory.BACKEND, DebugLogSeverity.INFO, |
| | | CLASS_NAME, "verifyBackend", |
| | | env.getConfig().toString()); |
| | | |
| | | // Open a container read-only. |
| | | String containerName = |
| | | BackendImpl.getContainerName(verifyConfig.getBaseDN()); |
| | | Container container = new Container(env, containerName); |
| | | this.rootContainer = rootContainer; |
| | | EntryContainer entryContainer = |
| | | new EntryContainer(backend, config, container); |
| | | entryContainer.openReadOnly(); |
| | | rootContainer.getEntryContainer(verifyConfig.getBaseDN()); |
| | | |
| | | ArrayList<String> completeList = verifyConfig.getCompleteList(); |
| | | ArrayList<String> cleanList = verifyConfig.getCleanList(); |
| | |
| | | |
| | | // We will be updating these files independently of the indexes |
| | | // so we need direct access to them rather than going through |
| | | // the entry container methods. |
| | | // the entry entryContainer methods. |
| | | id2entry = entryContainer.getID2Entry(); |
| | | dn2id = entryContainer.getDN2ID(); |
| | | id2c = entryContainer.getID2Children(); |
| | |
| | | public ProgressTask() throws DatabaseException |
| | | { |
| | | previousTime = System.currentTimeMillis(); |
| | | prevEnvStats = env.getStats(new StatsConfig()); |
| | | prevEnvStats = |
| | | rootContainer.getEnvironmentStats(new StatsConfig()); |
| | | } |
| | | |
| | | /** |
| | |
| | | Runtime runtime = Runtime.getRuntime(); |
| | | long freeMemory = runtime.freeMemory() / bytesPerMegabyte; |
| | | |
| | | EnvironmentStats envStats = env.getStats(new StatsConfig()); |
| | | EnvironmentStats envStats = |
| | | rootContainer.getEnvironmentStats(new StatsConfig()); |
| | | long nCacheMiss = |
| | | envStats.getNCacheMiss() - prevEnvStats.getNCacheMiss(); |
| | | |
| | |
| | | import java.util.ArrayList; |
| | | |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.LDIFImportConfig; |
| | | import org.opends.server.types.FilePermission; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.util.LDIFReader; |
| | | import org.testng.annotations.Test; |
| | | import org.testng.annotations.BeforeClass; |
| | |
| | | @Test() |
| | | public void test1() throws Exception { |
| | | EnvManager.createHomeDir(homeDirName); |
| | | EnvironmentConfig envConfig = new EnvironmentConfig(); |
| | | envConfig.setTransactional(true); |
| | | envConfig.setAllowCreate(true); |
| | | Environment env = new Environment(new File(homeDirName), envConfig); |
| | | EntryContainer entryContainer = new EntryContainer(null, new Config(), |
| | | new Container(env, null)); |
| | | RootContainer rootContainer = new RootContainer(new Config(), null); |
| | | rootContainer.open(new File(homeDirName), |
| | | new FilePermission(true, true, true), |
| | | false, true, true, false, true, true); |
| | | |
| | | entryContainer.open(); |
| | | EntryContainer entryContainer = |
| | | rootContainer.openEntryContainer(DirectoryServer.getSchemaDN()); |
| | | |
| | | EntryID actualHighestID = entryContainer.getHighestEntryID(); |
| | | assertTrue(actualHighestID.equals(new EntryID(0))); |
| | | |
| | |
| | | actualHighestID = entryContainer.getHighestEntryID(); |
| | | assertTrue(actualHighestID.equals(new EntryID(calculatedHighestID))); |
| | | |
| | | entryContainer.close(); |
| | | |
| | | env.close(); |
| | | rootContainer.close(); |
| | | EnvManager.removeFiles(homeDirName); |
| | | } |
| | | } |