EntryContainer.java revision 24ef7c741bf20dabe8a5be3978187f8f1eb5dd41
* 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 * See the License for the specific language governing permissions * and limitations under the License. * When distributing Covered Code, include this CDDL HEADER in each * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * Portions Copyright [yyyy] [name of copyright owner] * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2015 ForgeRock AS * Portions copyright 2013 Manuel Gaupp * Storage container for LDAP entries. Each base DN of a JE backend is given * its own entry container. The entry container is the object that implements * the guts of the backend API methods for LDAP operations. /** The name of the entry database. */ /** The name of the DN database. */ /** The name of the children index database. */ /** The name of the subtree index database. */ /** The name of the referral database. */ /** The name of the state database. */ /** The attribute index configuration manager. */ /** The vlv index configuration manager. */ /** The backend to which this entry container belongs. */ /** The root container in which this entryContainer belongs. */ /** The baseDN this entry container is responsible for. */ /** The backend configuration. */ /** The database storage. */ /** The DN database maps a normalized DN string to an entry ID (8 bytes). */ /** The entry database maps an entry ID (8 bytes) to a complete encoded entry. */ /** Index maps entry ID to an entry ID list containing its children. */ /** Index maps entry ID to an entry ID list containing its subordinates. */ /** The referral database maps a normalized DN string to labeled URIs. */ /** The state database maps a config DN to config entries. */ /** The set of attribute indexes. */ /** The set of VLV (Virtual List View) indexes. */ * Prevents name clashes for common indexes (like id2entry) across multiple suffixes. * For example when a root container contains multiple suffixes. * This class is responsible for managing the configuration for attribute * indexes used within this entry container. // FIXME this should be a read operation, but I cannot change it // because of AttributeIndex ctor. //Try creating all the indexes before confirming they are valid ones. // TODO: validate more before returning true? * This class is responsible for managing the configuration for VLV indexes * used within this entry container. // TODO: validate more before returning true? /** A read write lock to handle schema changes and bulk changes. */ * Create a new entry container object. * @param baseDN The baseDN this entry container will be responsible for * @param backend A reference to the JE backend that is creating this entry * container. It is needed by the Directory Server entry cache * @param config The configuration of the JE backend. * @param env The JE environment to create this entryContainer in. * @param rootContainer The root container this entry container is in. * @throws ConfigException if a configuration related error occurs. * Opens the entryContainer for reading and writing. * @param txn a non null database transaction * @throws StorageRuntimeException If an error occurs in the database. * @throws ConfigException if a configuration related error occurs. // Use a null index and ensure that future attempts to use the real // subordinate indexes will fail. * Closes the entry container. * @throws StorageRuntimeException If an error occurs in the database. // Deregister any listeners. * Retrieves a reference to the root container in which this entry container * @return A reference to the root container in which this entry container * Get the DN database used by this entry container. * The entryContainer must have been opened. * @return The DN database. * Get the entry database used by this entry container. * The entryContainer must have been opened. * @return The entry database. * Get the referral database used by this entry container. * The entryContainer must have been opened. * @return The referral database. * Get the children database used by this entry container. * The entryContainer must have been opened. * @return The children database. * Get the subtree database used by this entry container. * The entryContainer must have been opened. * @return The subtree database. * Look for an attribute index for the given attribute type. * @param attrType The attribute type for which an attribute index is needed. * @return The attribute index or null if there is none for that type. * Return attribute index map. * @return The attribute index map. * Look for an VLV index for the given index name. * @param vlvIndexName The vlv index name for which an vlv index is needed. * @return The VLV index or null if there is none with that name. * Retrieve all attribute indexes. * @return All attribute indexes defined in this entry container. * Retrieve all VLV indexes. * @return The collection of VLV indexes defined in this entry container. * Determine the highest entryID in the entryContainer. * The entryContainer must already be open. * @param txn a non null database transaction * @return The highest entry ID. * @throws StorageRuntimeException If an error occurs in the database. // Position a cursor on the last data item, and the key should give the highest ID. * Determine the number of subordinate entries for a given entry. * @param entryDN The distinguished name of the entry. * @param subtree <code>true</code> will include all the entries under the * given entries. <code>false</code> will only return the * number of entries immediately under the given entry. * @return The number of subordinate entries for the given entry or -1 if * the entry does not exist. * @throws StorageRuntimeException If an error occurs in the database. * Processes the specified search in this entryContainer. * Matching entries should be provided back to the core server using the * <CODE>SearchOperation.returnEntry</CODE> method. * @param searchOperation The search operation to be processed. * @throws DirectoryException * If a problem occurs while processing the * @throws StorageRuntimeException If an error occurs in the database. * @throws CanceledOperationException if this operation should be cancelled. * If the control's criticality field is true then the server SHOULD * do the following: return unavailableCriticalExtension as a return * code in the searchResultDone message; include the * sortKeyResponseControl in the searchResultDone message, and not * send back any search result entries. // Handle client abandon of paged results. // The RFC says : "If the page size is greater than or equal to the // sizeLimit value, the server should ignore the control as the // request can be satisfied in a single page" // Handle base-object search first. // Indicate no more pages. // Check whether the client requested debug information about the // contribution of the indexes to the search. // Create an index filter to get the search result candidate entries // Evaluate the filter against the attribute indexes. // Evaluate the search scope against the id2children and id2subtree indexes // The id2subtree list does not include the base entry ID. // In this case we know that every candidate is in scope. // If the sort key is not present, the sorting will generate the // default ordering. VLV search request goes through as if // this sort key was not found in the user entry. * There is no sort key associated with the sort control. * Since it came here it means that the criticality is false * so let the server return all search results unsorted and * include the sortKeyResponseControl in the searchResultDone // If requested, construct and return a fictitious entry containing // debug information, and no other entries. // FIXME -- Add support for sorting unindexed searches using indexes // like DSEE currently does. * We were not able to obtain a set of candidate entry IDs for the * search from the indexes. * Here we are relying on the DN key order to ensure children are * returned after their parents. * <li>iterate through a subtree range of the DN database * <li>discard non-children DNs if the search scope is single level * <li>fetch the entry by ID from the entry cache or the entry database * <li>return the entry if it matches the filter * @param searchOperation The search operation. * @param pageRequest A Paged Results control, or null if none. * @throws DirectoryException If an error prevented the search from being // The base entry must already have been processed if this is // a request for the next page in paged results. So we skip // the base entry processing if the cookie is set. * The base entry is only included for whole subtree search. // Indicate no more pages. * We will iterate forwards through a range of the dn2id keys to * find subordinates of the target entry from the top of the tree * downwards. For example, any subordinates of "dc=example,dc=com" appear * in dn2id with a key ending in ",dc=example,dc=com". The entry * "cn=joe,ou=people,dc=example,dc=com" will appear after the entry * "ou=people,dc=example,dc=com". * Set the ending value to a value of equal length but slightly * greater than the suffix. Since keys are compared in * reverse order we must set the first byte (the comma). * No possibility of overflow here. // Set the starting value. // The cookie contains the DN of the next entry to be returned. // Set the starting value to the suffix. // Initialize the cursor very close to the starting value. // Step forward until we pass the ending value. // Lookthrough limit exceeded // We have found a subordinate entry. // Check if this entry is an immediate child. // Process the candidate entry. // The current page is full. // Set the cookie to remember where we were. // We have been told to discontinue processing of the // search. This could be due to size limit exceeded or // Move to the next record. // Indicate no more pages. * Returns the entry corresponding to the provided entryID. * @param txn a non null database transaction * the id of the entry to retrieve * @return the entry corresponding to the provided entryID * @throws DirectoryException * If an error occurs retrieving the entry // Try the entry cache first. // Put the entry in the cache making sure not to overwrite a newer copy // that may have been inserted since the time we read the cache. * We were able to obtain a set of candidate entry IDs for the * search from the indexes. * Here we are relying on ID order to ensure children are returned * <li>Iterate through the candidate IDs * <li>fetch entry by ID from cache or id2entry * <li>put the entry in the cache if not present * <li>discard entries that are not in scope * <li>return entry if it matches the filter * @param entryIDSet The candidate entry IDs. * @param candidatesAreInScope true if it is certain that every candidate * entry is in the search scope. * @param searchOperation The search operation. * @param pageRequest A Paged Results control, or null if none. * @throws DirectoryException If an error prevented the search from being // Set the starting value. // The cookie contains the ID of the next entry to be returned. // Make sure the candidate list is smaller than the lookthrough limit //Lookthrough limit exceeded // Iterate through the index candidates. // Process the candidate entry. // The current page is full. // Set the cookie to remember where we were. // We have been told to discontinue processing of the // search. This could be due to size limit exceeded or // Before we return success from the search we must ensure the base entry // exists. However, if we have returned at least one entry or subordinate // reference it implies the base does exist, so we can omit the check. // Indicate no more pages. // Check if this entry is an immediate child. * Adds the provided entry to this database. This method must ensure that the * entry is appropriate for the database and that no entry already exists with * the same DN. The caller must hold a write lock on the DN of the provided * @param entry The entry to add to this database. * @param addOperation The add operation with which the new entry is * associated. This may be <CODE>null</CODE> for adds * @throws DirectoryException If a problem occurs while trying to add the * @throws StorageRuntimeException If an error occurs in the database. * @throws CanceledOperationException if this operation should be cancelled. // Check whether the entry already exists. // Check that the parent entry exists. // Check for referral entries above the target. // Read the parent ID from dn2id. // Insert into the indexes, in index configuration order. // Insert into id2children and id2subtree. // The database transaction locks on these records will be hotly // contested so we do them last so as to hold the locks for the // Iterate up through the superior entries, starting above the // Read the ID from dn2id. // Insert into id2subtree for this node. // One last check before committing // Update the entry cache. * Removes the specified entry from this database. This method must ensure * that the entry exists and that it does not have any subordinate entries * (unless the database supports a subtree delete operation and the client * included the appropriate information in the request). The caller must hold * a write lock on the provided entry DN. * @param entryDN The DN of the entry to remove from this database. * @param deleteOperation The delete operation with which this action is * associated. This may be <CODE>null</CODE> for * deletes performed internally. * @throws DirectoryException If a problem occurs while trying to remove the * @throws StorageRuntimeException If an error occurs in the database. * @throws CanceledOperationException if this operation should be cancelled. // Check for referral entries above the target entry. // Determine whether this is a subtree delete. * We will iterate forwards through a range of the dn2id keys to * find subordinates of the target entry from the top of the tree * Set the ending value to a value of equal length but slightly * greater than the suffix. // Step forward until we pass the ending value. // We have found a subordinate entry. // The subtree delete control was not specified and // the target entry is not a leaf. * Delete this entry which by now must be a leaf because we have * been deleting from the bottom of the tree upwards. // Invoke any subordinate delete plugins on the entry. // draft-armijo-ldap-treedelete, 4.1 Tree Delete Semantics: // The server MUST NOT chase referrals stored in the tree. If // information about referrals is stored in this section of the // tree, this pointer will be deleted. // One last check before committing // Read the entry ID from dn2id. // FIXME: previously this used a RMW lock - see OPENDJ-1878. // Do not expect to ever come through here. // Check that the entry exists in id2entry and read its contents. // FIXME: previously this used a RMW lock - see OPENDJ-1878. // Update the referral database. // Remove from the indexes, in index config order. // Remove the id2c and id2s records for this entry. // Iterate up through the superior entries from the target entry. // Read the ID from dn2id. // Remove from id2children. // Remove the entry from the entry cache. * Indicates whether an entry with the specified DN exists. * @param entryDN The DN of the entry for which to determine existence. * @return <CODE>true</CODE> if the specified entry exists, * or <CODE>false</CODE> if it does not. * @throws DirectoryException If a problem occurs while trying to make the // Try the entry cache first. * Fetch an entry by DN, trying the entry cache first, then the database. * Retrieves the requested entry, trying the entry cache first, * then the database. Note that the caller must hold a read or write lock * @param entryDN The distinguished name of the entry to retrieve. * @return The requested entry, or <CODE>null</CODE> if the entry does not * @throws DirectoryException If a problem occurs while trying to retrieve * @throws StorageRuntimeException An error occurred during a database operation. // it is not very clean to specify twice the same exception but it saves me some code for now return null;
// it can never happen // Try the entry cache first. // The entryDN does not exist. // Check for referral entries above the target entry. // The entryID does not exist. // Put the entry in the cache making sure not to overwrite // a newer copy that may have been inserted since the time // it is not very clean to specify twice the same exception but it saves me some code for now return null;
// unreachable * The simplest case of replacing an entry in which the entry DN has * @param oldEntry The old contents of the entry * @param newEntry The new contents of the entry * @param modifyOperation The modify operation with which this action is * associated. This may be <CODE>null</CODE> for * modifications performed internally. * @throws StorageRuntimeException If an error occurs in the database. * @throws DirectoryException If a Directory Server error occurs. * @throws CanceledOperationException if this operation should be cancelled. // Check if the entry is a referral entry. // Update the referral database. // In this case we know from the operation what the modifications were. // In this case we know from the operation what the modifications were. // The most optimal would be to figure out what the modifications were. // One last check before committing // Update the entry cache. * Moves and/or renames the provided entry in this backend, altering any * subordinate entries as necessary. This must ensure that an entry already * exists with the provided current DN, and that no entry exists with the * target DN of the provided entry. The caller must hold write locks on both * the current DN and the new DN for the entry. * @param currentDN The current DN of the entry to be replaced. * @param entry The new content to use for the entry. * @param modifyDNOperation The modify DN operation with which this action * is associated. This may be <CODE>null</CODE> * for modify DN operations performed internally. * @throws DirectoryException * If a problem occurs while trying to perform the rename. * @throws CanceledOperationException * If this backend noticed and reacted * to a request to cancel or abandon the * @throws StorageRuntimeException If an error occurs in the database. // FIXME: consistency + isolation cannot be maintained lock free - see OPENDJ-1878. // Check whether the renamed entry already exists. // Check for referral entries above the target entry. * We want to preserve the invariant that the ID of an entry is * greater than its parent, since search results are returned in // This move would break the above invariant so we must // renumber every entry that moves. This is even more // expensive since every entry has to be deleted from // and added back into the attribute indexes. logger.
trace(
"Move of target entry requires renumbering" +
"all entries in the subtree. " +
"Old DN: %s " +
"New DN: %s " +
"Old entry ID: %d " +
"New entry ID: %d " // Move or rename the apex entry. * We will iterate forwards through a range of the dn2id keys to * find subordinates of the target entry from the top of the tree * Set the ending value to a value of equal length but slightly * greater than the suffix. // Step forward until we pass the ending value. // We have found a subordinate entry. // Construct the new DN of the entry. // Assign a new entry ID if we are renumbering. logger.
trace(
"Move of subordinate entry requires renumbering. " +
"Old DN: %s New DN: %s Old entry ID: %d New entry ID: %d",
// Set current to the first moved entry and null out the head. // This will allow processed moved entries to be GCed. // One last check before committing /** Represents an renamed entry that was deleted from JE but yet to be added back. */ // FIXME: the core server should validate that the new subtree location is empty. // Reindex the entry with the new ID. // Add the new ID to id2children and id2subtree of new apex parent entry. // Remove the old DN from dn2id. // Remove old ID from id2entry and put the new entry // (old entry with new DN) in id2entry. // Update any referral records. // Remove the old ID from id2children and id2subtree of // the old apex parent entry. // All the subordinates will be renumbered so we have to rebuild // id2c and id2s with the new ID. // Reindex the entry with the new ID. // Update the indexes if needed. // Remove the entry from the entry cache. // Create a new entry that is a copy of the old entry but with the new DN. // Also invoke any subordinate modify DN plugins on the entry. // FIXME -- At the present time, we don't support subordinate modify DN // plugins that make changes to subordinate entries and therefore // provide an unmodifiable list for the modifications element. // FIXME -- This will need to be updated appropriately if we decided that // these plugins should be invoked for synchronization // Remove the old DN from dn2id. // Remove old ID from id2entry and put the new entry // (old entry with new DN) in id2entry. // Update any referral records. // Remove the old ID from id2subtree of old apex superior entries. // All the subordinates will be renumbered so we have to rebuild // id2c and id2s with the new ID. // Reindex the entry with the new ID. // Remove the entry from the entry cache. * Make a new DN for a subordinate entry of a renamed or moved entry. * @param oldDN The current DN of the subordinate entry. * @param oldSuffixLen The current DN length of the renamed or moved entry. * @param newSuffixDN The new DN of the renamed or moved entry. * @return The new DN of the subordinate entry. * Insert a new entry into the attribute indexes. * @param buffer The index buffer used to buffer up the index changes. * @param entry The entry to be inserted into the indexes. * @param entryID The ID of the entry to be inserted into the indexes. * @throws StorageRuntimeException If an error occurs in the database. * @throws DirectoryException If a Directory Server error occurs. * Remove an entry from the attribute indexes. * @param buffer The index buffer used to buffer up the index changes. * @param entry The entry to be removed from the indexes. * @param entryID The ID of the entry to be removed from the indexes. * @throws StorageRuntimeException If an error occurs in the database. * @throws DirectoryException If a Directory Server error occurs. * Update the attribute indexes to reflect the changes to the * attributes of an entry resulting from a sequence of modifications. * @param buffer The index buffer used to buffer up the index changes. * @param oldEntry The contents of the entry before the change. * @param newEntry The contents of the entry after the change. * @param entryID The ID of the entry that was changed. * @param mods The sequence of modifications made to the entry. * @throws StorageRuntimeException If an error occurs in the database. * @throws DirectoryException If a Directory Server error occurs. // Process in index configuration order. // Check whether any modifications apply to this indexed attribute. * Get a count of the number of entries stored in this entry container. * @param txn a non null database transaction * @return The number of entries stored in this entry container. * @throws StorageRuntimeException If an error occurs in the database. // Add the base entry itself // The count is not maintained. Fall back to the slow method // Base entry doesn't not exist so this entry container // must not have any entries * Get a list of the databases opened by the entryContainer. * @param dbList A list of database containers. * Determine whether the provided operation has the ManageDsaIT request * @param operation The operation for which the determination is to be made. * @return true if the operation has the ManageDsaIT request control, or false * Delete this entry container from disk. The entry container should be * closed before calling this method. * @param txn a non null database transaction * @throws StorageRuntimeException If an error occurs while removing the entry * Remove a database from disk. * @param txn a non null database transaction * @param database The database container to remove. * @throws StorageRuntimeException If an error occurs while attempting to delete the // The state database can not be removed individually. * Removes a attribute index from disk. * @param attributeIndex The attribute index to remove. * @throws StorageRuntimeException If an database error occurs while attempting * This method constructs a container name from a base DN. Only alphanumeric * characters are preserved, all other characters are replaced with an * @return The container name for the base DN. * Sets a new database prefix for this entry container and rename all * existing databases in use by this entry container. * @param newBaseDN The new database prefix to use. * @throws StorageRuntimeException If an error occurs in the database. // Rename in transaction. // Only rename the containers if the txn succeeded. // Open the containers backup. * Get the parent of a DN in the scope of the base DN. * @param dn A DN which is in the scope of the base DN. * @return The parent DN, or null if the given DN is the base DN. // This is always true because only all config attributes used // by the entry container should be validated by the admin framework. // Re-enabling subordinate indexes. // Disabling subordinate indexes. Use a null index and ensure that // future attempts to use the real indexes will fail. * Clear the contents of this entry container. * @throws StorageRuntimeException If an error occurs while removing the entry * Clear the contents for a database from disk. * @param txn a non null database transaction * @param database The database to clear. * @throws StorageRuntimeException if a database error occurs. * Finds an existing entry whose DN is the closest ancestor of a given baseDN. * @param baseDN the DN for which we are searching a matched DN. * @return the DN of the closest ancestor of the baseDN. * @throws DirectoryException If an error prevented the check of an * existing entry from being performed. /** Opens the id2children and id2subtree indexes. */ * Creates a new index for an attribute. * @param txn a non null database transaction * @param indexName the name to give to the new index * @param indexer the indexer to use when inserting data into the index * @param indexEntryLimit the index entry limit * Checks if any modifications apply to this indexed attribute. * @param index the indexed attributes. * @param mods the modifications to check for. * @return true if any apply, false otherwise. * Fetch the base Entry of the EntryContainer. * @param baseDN the DN for the base entry * @param searchScope the scope under which this is fetched. * Scope is used for referral processing. * @return the Entry matching the baseDN. * @throws DirectoryException if the baseDN doesn't exist. // The base entry must exist for a successful result. // Check for referral entries above the base entry. // See if there is a VLV request to further pare down the set of results, and if there is where it should be // processed by offset or assertion value. /** FIXME: Might be moved into a util.Longs class */ // No entry was found to be greater than or equal to the sort key, so the target offset will be one greater // than the content count. // The client specified a negative target offset. This // should never be allowed. // This is an easy mistake to make, since VLV offsets start at 1 instead of 0. We'll assume the client meant // This can happen if beforeCount >= offset, and in this case we'll just adjust the start position to ignore // the range of beforeCount that doesn't exist. // The start position is beyond the end of the list. In this case, we'll assume that the start position was // one greater than the size of the list and will only return the beforeCount entries. // We don't have enough entries in the set to meet the requested page size, so we'll need to shorten the /** Get the exclusive lock. */ /** Unlock the exclusive lock. */