Index.java revision f0a048d41a13eca4cba405da9403c2469ca3d1ea
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2012-2015 ForgeRock AS
*/
/**
* Represents an index implemented by a JE database in which each key maps to
* a set of entry IDs. The key is a byte array, and is constructed from some
* normalized form of an attribute value (or fragment of a value) appearing
* in the entry.
*/
public class Index extends DatabaseContainer
{
/** The indexer object to construct index keys from LDAP attribute values. */
/** The limit on the number of entry IDs that may be indexed by one key. */
private int indexEntryLimit;
/**
* Limit on the number of entry IDs that may be retrieved by cursoring
* through an index.
*/
private final int cursorEntryLimit;
/**
* Number of keys that have exceeded the entry limit since this
* object was created.
*/
private int entryLimitExceededCount;
/** The max number of tries to rewrite phantom records. */
private final int phantomWriteRetries = 3;
/**
* Whether to maintain a count of IDs for a key once the entry limit
* has exceeded.
*/
private final boolean maintainCount;
/**
* A flag to indicate if this index should be trusted to be consistent
* with the entries database. If not trusted, we assume that existing
* entryIDSets for a key is still accurate. However, keys that do not
* exist are undefined instead of an empty entryIDSet. The following
* rules will be observed when the index is not trusted:
*
* - no entryIDs will be added to a non-existing key.
* - undefined entryIdSet will be returned whenever a key is not found.
*/
private boolean trusted;
private final ImportIDSet newImportIDSet;
/**
* Create a new index object.
* @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 state The state database to persist index state info.
* @param indexEntryLimit The configured limit on the number of entry IDs
* that may be indexed by one key.
* @param cursorEntryLimit The configured limit on the number of entry IDs
* @param maintainCount Whether to maintain a count of IDs for a key once
* the entry limit has exceeded.
* @param env The JE Environment
* @param entryContainer The database entryContainer holding this index.
* @throws DatabaseException If an error occurs in the JE database.
*/
@SuppressWarnings("unchecked")
throws DatabaseException
{
this.indexEntryLimit = indexEntryLimit;
this.cursorEntryLimit = cursorEntryLimit;
this.maintainCount = maintainCount;
this.dbConfig.setOverrideBtreeComparator(true);
{
// If there are no entries in the entry container then there
// is no reason why this index can't be upgraded to trusted.
setTrusted(null, true);
}
}
{
}
/**
* Add an add entry ID operation into a index buffer.
*
* @param buffer The index buffer to insert the ID into.
* @param keyBytes The index key bytes.
* @param entryID The entry ID.
*/
{
}
/**
* Delete the specified import ID set from the import ID set associated with the key.
*
* @param key The key to delete the set from.
* @param importIdSet The import ID set to delete.
* @param data A database entry to use for data.
* @throws DatabaseException If a database error occurs.
*/
public void delete(DatabaseEntry key, ImportIDSet importIdSet, DatabaseEntry data) throws DatabaseException {
{
}
else
{
}
} else {
// Should never happen -- the keys should always be there.
throw new RuntimeException();
}
}
/**
* Insert the specified import ID set into this index. Creates a DB cursor if needed.
*
* @param key The key to add the set to.
* @param importIdSet The set of import IDs.
* @param data Database entry to reuse for read.
* @throws DatabaseException If a database error occurs.
*/
public void insert(DatabaseEntry key, ImportIDSet importIdSet, DatabaseEntry data) throws DatabaseException {
{
}
if(!importIdSet.isDefined()) {
}
} else {
// Should never happen during import.
throw new RuntimeException();
}
}
/**
* Update the set of entry IDs for a given key.
*
* @param txn A database transaction, or null if none is required.
* @param key The database key.
* @param deletedIDs The IDs to remove for the key.
* @param addedIDs the IDs to add for the key.
* @throws DatabaseException If a database error occurs.
*/
throws DatabaseException
{
{
{
}
return;
}
// Handle cases where nothing is changed early to avoid DB access.
{
return;
}
if(maintainCount)
{
for (int i = 0; i < phantomWriteRetries; i++)
{
{
return;
}
}
}
else
{
{
if (entryIDList.isDefined())
{
for (int i = 0; i < phantomWriteRetries; i++)
{
{
return;
}
}
}
}
else if (trusted)
{
if (deletedIDs != null)
{
}
if (isNotNullOrEmpty(addedIDs))
{
{
for (int i = 1; i < phantomWriteRetries; i++)
{
{
return;
}
}
}
}
}
}
}
{
}
{
}
throws DatabaseException
{
{
{
}
else
{
// No more IDs, so remove the key. If index is not
// trusted then this will cause all subsequent reads
// for this key to return undefined set.
}
}
else if (trusted)
{
if (deletedIDs != null)
{
}
if (isNotNullOrEmpty(addedIDs))
{
}
}
return OperationStatus.SUCCESS;
}
{
{
{
if(deletedIDs != null)
{
}
{
if(maintainCount)
{
}
else
{
entryIDList = new EntryIDSet();
}
if(logger.isTraceEnabled())
{
"Limit: %d. ID list size: %d.\nKey:%s",
}
}
else
{
if(deletedIDs != null)
{
}
}
}
else
{
if(deletedIDs != null)
{
}
}
}
else if(deletedIDs != null)
{
}
return entryIDList;
}
/**
* Add an remove entry ID operation into a index buffer.
*
* @param buffer The index buffer to insert the ID into.
* @param keyBytes The index key bytes.
* @param entryID The entry ID.
*/
{
}
{
if (logger.isTraceEnabled())
{
}
setTrusted(txn, false);
}
/**
* Buffered delete of a key from the JE database.
* @param buffer The index buffer to use to store the deleted keys
* @param keyBytes The index key bytes.
*/
{
}
{
}
/**
* Check if an entry ID is in the set of IDs indexed by a given key.
*
* @param txn A database transaction, or null if none is required.
* @param key The index key.
* @param entryID The entry ID.
* @return true if the entry ID is indexed by the given key,
* false if it is not indexed by the given key,
* undefined if the key has exceeded the entry limit.
* @throws DatabaseException If an error occurs in the JE database.
*/
throws DatabaseException
{
{
if (!entryIDList.isDefined())
{
return ConditionResult.UNDEFINED;
}
}
else if (trusted)
{
return ConditionResult.FALSE;
}
else
{
return ConditionResult.UNDEFINED;
}
}
/**
* Reads the set of entry IDs for a given key.
*
* @param key The database key.
* @param txn A database transaction, or null if none is required.
* @param lockMode The JE locking mode to be used for the database read.
* @return The entry IDs indexed by this key.
*/
{
try
{
{
if(trusted)
{
}
else
{
return new EntryIDSet();
}
}
}
catch (DatabaseException e)
{
logger.traceException(e);
return new EntryIDSet();
}
}
/**
* Writes the set of entry IDs for a given key.
*
* @param key The database key.
* @param entryIDList The entry IDs indexed by this key.
* @param txn A database transaction, or null if none is required.
* @throws DatabaseException If an error occurs in the JE database.
*/
throws DatabaseException
{
{
if (!entryIDList.isDefined())
{
}
}
else
{
// No more IDs, so remove the key.
}
}
/**
* Reads a range of keys and collects all their entry IDs into a
* single set.
*
* @param lower The lower bound of the range. A 0 length byte array indicates
* no lower bound and the range will start from the
* smallest key.
* @param upper The upper bound of the range. A 0 length byte array indicates
* no upper bound and the range will end at the largest
* key.
* @param lowerIncluded true if a key exactly matching the lower bound
* is included in the range, false if only keys
* strictly greater than the lower bound are included.
* This value is ignored if the lower bound is not
* specified.
* @param upperIncluded true if a key exactly matching the upper bound
* is included in the range, false if only keys
* strictly less than the upper bound are included.
* This value is ignored if the upper bound is not
* specified.
* @return The set of entry IDs.
*/
boolean lowerIncluded, boolean upperIncluded)
{
// If this index is not trusted, then just return an undefined id set.
if (!trusted)
{
return new EntryIDSet();
}
try
{
// Total number of IDs found so far.
int totalIDCount = 0;
try
{
// Set the lower bound if necessary.
{
// Initialize the cursor to the lower bound.
// Advance past the lower bound if necessary.
{
// Do not include the lower value.
}
}
else
{
key = new DatabaseEntry();
}
{
// There are no values.
}
// Step through the keys until we hit the upper bound or the last key.
{
// Check against the upper bound if necessary
{
{
break;
}
}
{
// There is no point continuing.
return list;
}
{
// There are too many. Give up and return an undefined list.
return new EntryIDSet();
}
}
}
finally
{
}
}
catch (DatabaseException e)
{
logger.traceException(e);
return new EntryIDSet();
}
}
/**
* Get the number of keys that have exceeded the entry limit since this
* object was created.
* @return The number of keys that have exceeded the entry limit since this
* object was created.
*/
public int getEntryLimitExceededCount()
{
return entryLimitExceededCount;
}
/**
* Update the index buffer for a deleted entry.
*
* @param buffer The index buffer to use to store the deleted keys
* @param entryID The entry ID.
* @param entry The entry to be indexed.
* @param options The indexing options to use
* @throws DatabaseException If an error occurs in the JE database.
* @throws DirectoryException If a Directory Server error occurs.
*/
{
{
}
}
/**
* Update the index buffer for a deleted entry.
*
* @param buffer The index buffer to use to store the deleted keys
* @param entryID The entry ID
* @param entry The contents of the deleted entry.
* @param options The indexing options to use
* @throws DatabaseException If an error occurs in the JE database.
* @throws DirectoryException If a Directory Server error occurs.
*/
{
{
}
}
/**
* Update the index to reflect a sequence of modifications in a Modify
* operation.
*
* @param buffer The index buffer to use to store the deleted keys
* @param entryID The ID of the entry that was modified.
* @param oldEntry The entry before the modifications were applied.
* @param newEntry The entry after the modifications were applied.
* @param mods The sequence of modifications in the Modify operation.
* @param options The indexing options to use
* @throws DatabaseException If an error occurs in the JE database.
*/
throws DatabaseException
{
{
if(modifiedKey.getValue())
{
}
else
{
}
}
}
/**
* Set the index entry limit.
*
* @param indexEntryLimit The index entry limit to set.
* @return True if a rebuild is required or false otherwise.
*/
public boolean setIndexEntryLimit(int indexEntryLimit)
{
final boolean rebuildRequired =
this.indexEntryLimit = indexEntryLimit;
return rebuildRequired;
}
/**
* Set the indexer.
*
* @param indexer The indexer to set
*/
{
}
/**
* Return entry limit.
*
* @return The entry limit.
*/
public int getIndexEntryLimit() {
return this.indexEntryLimit;
}
/**
* Set the index trust state.
* @param txn A database transaction, or null if none is required.
* @param trusted True if this index should be trusted or false
* otherwise.
* @throws DatabaseException If an error occurs in the JE database.
*/
throws DatabaseException
{
}
/**
* Return true iff this index is trusted.
* @return the trusted state of this index
*/
public synchronized boolean isTrusted()
{
return trusted;
}
/**
* Return <code>true</code> iff this index is being rebuilt.
* @return The rebuild state of this index
*/
public synchronized boolean isRebuildRunning()
{
return false; // FIXME inline?
}
/**
* Whether this index maintains a count of IDs for keys once the
* entry limit has exceeded.
* @return <code>true</code> if this index maintains court of IDs
* or <code>false</code> otherwise
*/
public boolean getMaintainCount()
{
return maintainCount;
}
/**
* Return an indexes comparator.
*
* @return The comparator related to an index.
*/
public Comparator<byte[]> getComparator()
{
return indexer.getComparator();
}
}