EntryContainer.java revision 6f1a7f89a2bc9812c61f71d282ead3299556f876
3853N/A * The contents of this file are subject to the terms of the 3853N/A * Common Development and Distribution License, Version 1.0 only 3853N/A * (the "License"). You may not use this file except in compliance 3853N/A * See the License for the specific language governing permissions 3853N/A * and limitations under the License. 3853N/A * When distributing Covered Code, include this CDDL HEADER in each 6983N/A * If applicable, add the following below this CDDL HEADER, with the 6983N/A * fields enclosed by brackets "[]" replaced with your own identifying 3853N/A * Portions Copyright [yyyy] [name of copyright owner] 5085N/A * Copyright 2006-2010 Sun Microsystems, Inc. 6159N/A * Portions Copyright 2011-2015 ForgeRock AS 3853N/A * Portions copyright 2013 Manuel Gaupp 3853N/A * Storage container for LDAP entries. Each base DN of a backend is given 3853N/A * its own entry container. The entry container is the object that implements 3853N/A * the guts of the backend API methods for LDAP operations. 3853N/A /** Number of EntryID to considers when building EntryIDSet from DN2ID. */ 3853N/A /** The name of the entry tree. */ 3853N/A /** The name of the DN tree. */ 3853N/A /** The name of the children index tree. */ 3853N/A /** The name of the referral tree. */ 3853N/A /** The name of the state tree. */ 3853N/A /** The attribute index configuration manager. */ 3853N/A /** The vlv index configuration manager. */ 3853N/A /** ID of the backend to which this entry container belongs. */ 3853N/A /** The root container in which this entryContainer belongs. */ 3853N/A /** The baseDN this entry container is responsible for. */ 3853N/A /** The backend configuration. */ 3853N/A /** The DN tree maps a normalized DN string to an entry ID (8 bytes). */ 3853N/A /** The entry tree maps an entry ID (8 bytes) to a complete encoded entry. */ 3853N/A /** Store the number of children for each entry. */ 3853N/A /** The referral tree maps a normalized DN string to labeled URIs. */ 3853N/A /** The state tree maps a config DN to config entries. */ 3853N/A /** The set of attribute indexes. */ 3853N/A /** The set of VLV (Virtual List View) indexes. */ 3853N/A * Prevents name clashes for common indexes (like id2entry) across multiple suffixes. 3853N/A * For example when a root container contains multiple suffixes. 4500N/A * This class is responsible for managing the configuration for attribute 3853N/A * indexes used within this entry container. 3853N/A // TODO: validate more before returning true? 3853N/A * This class is responsible for managing the configuration for VLV indexes 3853N/A * used within this entry container. 5138N/A // TODO: validate more before returning true? 3884N/A /** A read write lock to handle schema changes and bulk changes. */ 3884N/A * Create a new entry container object. 3884N/A * @param baseDN The baseDN this entry container will be responsible for 3884N/A * @param backendID ID of the backend that is creating this entry container. 3853N/A * It is needed by the Directory Server entry cache methods. 3853N/A * @param config The configuration of the backend. 3853N/A * @param storage The storage for this entryContainer. 3853N/A * @param rootContainer The root container this entry container is in. 3853N/A * @throws ConfigException if a configuration related error occurs. 3884N/A * Opens the entryContainer for reading and writing. 3884N/A * @param txn a non null transaction 3884N/A * @param accessMode specifies how the container has to be opened (read-write or read-only) 3884N/A * @throws StorageRuntimeException If an error occurs in the storage. 3884N/A * @throws ConfigException if a configuration related error occurs. 3884N/A * Closes the entry container. 3853N/A * @throws StorageRuntimeException If an error occurs in the storage. 3853N/A // Deregister any listeners. 3853N/A * Retrieves a reference to the root container in which this entry container 4935N/A * @return A reference to the root container in which this entry container * Get the DN tree used by this entry container. * The entryContainer must have been opened. * Get the entry tree used by this entry container. * The entryContainer must have been opened. * @return The entry tree. * Get the referral tree used by this entry container. * The entryContainer must have been opened. * @return The referral tree. * Get the children tree used by this entry container. * The entryContainer must have been opened. * @return The children tree. * 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. * Look for a 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 transaction * @return The highest entry ID. * @throws StorageRuntimeException If an error occurs in the storage. // Position a cursor on the last data item, and the key should give the highest ID. * Determine the number of children entries for a given entry. * @param entryDN The distinguished name of the entry. * @return The number of children entries for the given entry or -1 if * the entry does not exist. * @throws StorageRuntimeException If an error occurs in the storage. * 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 storage. * @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. // 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 tree * <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 tree * <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 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 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 tree. This method must ensure that the * entry is appropriate for the tree 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 tree. * @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 storage. * @throws CanceledOperationException if this operation should be cancelled. // Insert into the indexes, in index configuration order. // Check whether the entry already exists. // One last check before committing // Check that the parent entry exists. // Check for referral entries above the target. * Removes the specified entry from this tree. This method must ensure * that the entry exists and that it does not have any subordinate entries * (unless the storage 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 tree. * @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 storage. * @throws CanceledOperationException if this operation should be cancelled. * We will iterate forwards through a range of the dn2id keys to find subordinates of the target entry from the top // Check for referral entries above the target entry. // Since everything under targetDN will be deleted, we only have to decrement the counter of targetDN's // parent. Other counters will be removed in deleteEntry() // 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 tree. // Remove from the indexes, in index config order. // Remove the children counter for this entry. // 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 tree. * Retrieves the requested entry, trying the entry cache first, * @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 storage 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 // The entryDN does not exist. Check for referral entries above the target entry. * 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. // 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 storage. * @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 tree. // 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 storage. // 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 // 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 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. // 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. // Since entry has moved, oldSuperiorDN has lost a child // 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. // 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 storage. * @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 storage. * @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 storage. * @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 including the baseDN * @return The number of entries stored in this entry container including the baseDN. * @throws StorageRuntimeException * If an error occurs in the storage. * 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 transaction * @throws StorageRuntimeException If an error occurs while removing the entry * Remove a tree from disk. * @param txn a non null transaction * @param tree The tree container to remove. * @throws StorageRuntimeException If an error occurs while attempting to delete the tree. // The state tree can not be removed individually. * 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. * 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. * Clear the contents of this entry container. * @throws StorageRuntimeException If an error occurs while removing the entry * 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. * 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. * 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. */