/*
* 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 2011-2012 ForgeRock AS
*/
/**
* This class is used to run an index verification process on the backend.
*/
public class VerifyJob
{
/**
* The tracer object for the debug logger.
*/
/**
* The verify configuration.
*/
/**
* The root container used for the verify job.
*/
/**
* The number of milliseconds between job progress reports.
*/
/**
* The number of index keys processed.
*/
/**
* The number of errors found.
*/
/**
* The number of records that have exceeded the entry limit.
*/
/**
* The number of records that reference more than one entry.
*/
/**
* The total number of entry references.
*/
/**
* The maximum number of references per record.
*/
/**
* This map is used to gather some statistics about values that have
* exceeded the entry limit.
*/
/**
* Indicates whether the DN database is to be verified.
*/
private boolean verifyDN2ID = false;
/**
* Indicates whether the children database is to be verified.
*/
private boolean verifyID2Children = false;
/**
* Indicates whether the subtree database is to be verified.
*/
private boolean verifyID2Subtree = false;
/**
* The entry database.
*/
/**
* The DN database.
*/
/**
* The children database.
*/
/**
* The subtree database.
*/
/**
* A list of the attribute indexes to be verified.
*/
/**
* A list of the VLV indexes to be verified.
*/
/**
* The types of indexes that are verifiable.
*/
enum IndexType
{
}
/**
* Construct a VerifyJob.
*
* @param verifyConfig The verify configuration.
*/
{
this.verifyConfig = verifyConfig;
}
/**
* Verify the backend.
*
* @param rootContainer The root container that holds the entries to verify.
* @param statEntry Optional statistics entry.
* @return The error count.
* @throws DatabaseException If an error occurs in the JE database.
* @throws JebException If an error occurs in the JE backend.
* @throws DirectoryException If an error occurs while verifying the backend.
*/
{
this.rootContainer = rootContainer;
try
{
boolean cleanMode = false;
{
verifyDN2ID = true;
{
verifyID2Children = true;
verifyID2Subtree = true;
}
}
else
{
if (!completeList.isEmpty())
{
list = completeList;
}
else
{
cleanMode = true;
}
{
{
verifyDN2ID = true;
}
{
{
verifyID2Children = true;
}
else
{
throw new JebException(msg);
}
}
{
{
verifyID2Subtree = true;
}
else
{
throw new JebException(msg);
}
}
{
{
throw new JebException(msg);
}
{
throw new JebException(msg);
}
}
else
{
{
throw new JebException(msg);
}
{
throw new JebException(msg);
}
}
}
}
attrIndexList.size());
// We will be updating these files independently of the indexes
// so we need direct access to them rather than going through
// the entry entryContainer methods.
// Make a note of the time we started.
// Start a timer for the progress report.
if (cleanMode)
{
// Create a new progressTask based on the index count.
progressTask = new ProgressTask(true);
}
// Iterate through the index keys.
try
{
if (cleanMode)
{
iterateIndex();
}
else
{
// Make sure the vlv indexes are in correct order.
{
iterateVLVIndex(vlvIndex, false);
}
}
}
finally
{
}
float rate = 0;
if (totalTime > 0)
{
}
if (cleanMode)
{
if (multiReferenceCount > 0)
{
float averageEntryReferences = 0;
if (keyCount > 0)
{
}
message =
message =
}
}
else
{
//TODO add entry-limit-stats to the statEntry
{
{
// Calculate the median value for entry limit exceeded.
long medianValue;
{
}
else
{
medianValue = values[x];
}
}
}
}
}
finally
{
}
return errorCount;
}
/**
* Iterate through the entries in id2entry to perform a check for
* index completeness. We check that the ID for the entry is indeed
* present in the indexes for the appropriate values.
*
* @throws DatabaseException If an error occurs in the JE database.
*/
{
try
{
{
try
{
}
catch (Exception e)
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
keyCount++;
try
{
}
catch (Exception e)
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
}
if (keyCount != storedEntryCount)
{
errorCount++;
if (debugEnabled())
{
"not agree with the actual number of entry " +
}
}
}
finally
{
}
}
/**
* Iterate through the entries in an index to perform a check for
* index cleanliness. For each ID in the index we check that the
* entry it refers to does indeed contain the expected value.
*
* @throws JebException If an error occurs in the JE backend.
* @throws DatabaseException If an error occurs in the JE database.
* @throws DirectoryException If an error occurs reading values in the index.
*/
private void iterateIndex()
{
if (verifyDN2ID)
{
iterateDN2ID();
}
else if (verifyID2Children)
{
}
else if (verifyID2Subtree)
{
}
else
{
{
// TODO: Need to iterate through ExtendedMatchingRules indexes.
{
}
}
}
/**
* Iterate through the entries in DN2ID to perform a check for
* index cleanliness.
*
* @throws DatabaseException If an error occurs in the JE database.
*/
{
try
{
{
keyCount++;
try
{
}
catch (Exception e)
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
try
{
}
catch (Exception e)
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
{
errorCount++;
if (debugEnabled())
{
}
}
else
{
{
errorCount++;
if (debugEnabled())
{
}
}
}
}
}
finally
{
}
}
/**
* Iterate through the entries in ID2Children to perform a check for
* index cleanliness.
*
* @throws JebException If an error occurs in the JE backend.
* @throws DatabaseException If an error occurs in the JE database.
*/
{
try
{
{
keyCount++;
try
{
}
catch (Exception e)
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
try
{
}
catch (Exception e)
{
errorCount++;
if (debugEnabled())
{
"for ID %s:%n%s%n", entryID,
}
continue;
}
if (entryIDList.isDefined())
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
errorCount++;
continue;
}
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
errorCount++;
continue;
}
if (childEntry == null)
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
{
errorCount++;
if (debugEnabled())
{
"referencing ID %d with non-child DN <%s>%n",
}
}
}
}
}
}
finally
{
}
}
/**
* Iterate through the entries in ID2Subtree to perform a check for
* index cleanliness.
*
* @throws JebException If an error occurs in the JE backend.
* @throws DatabaseException If an error occurs in the JE database.
*/
{
try
{
{
keyCount++;
try
{
}
catch (Exception e)
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
try
{
}
catch (Exception e)
{
errorCount++;
if (debugEnabled())
{
"for ID %s:%n%s%n", entryID,
}
continue;
}
if (entryIDList.isDefined())
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
errorCount++;
continue;
}
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
errorCount++;
continue;
}
if (subordEntry == null)
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
{
errorCount++;
if (debugEnabled())
{
"referencing ID %d with non-subordinate " +
"DN <%s>%n",
}
}
}
}
}
}
finally
{
}
}
/**
* Increment the counter for a key that has exceeded the
* entry limit. The counter gives the number of entries that have
* referenced the key.
*
* @param index The index containing the key.
* @param key A key that has exceeded the entry limit.
*/
{
{
}
{
counter = 1L;
}
else
{
counter++;
}
}
/**
* Update the statistical information for an index record.
*
* @param entryIDSet The set of entry IDs for the index record.
*/
{
if (!entryIDSet.isDefined())
{
}
else
{
{
}
}
}
/**
* Iterate through the entries in a VLV index to perform a check for index
* cleanliness.
*
* @param vlvIndex The VLV index to perform the check against.
* @param verifyID True to verify the IDs against id2entry.
* @throws JebException If an error occurs in the JE backend.
* @throws DatabaseException If an error occurs in the JE database.
* @throws DirectoryException If an error occurs reading values in the index.
*/
{
{
return;
}
try
{
{
{
keyCount++;
{
// Make sure the values is larger then the previous one.
if(debugEnabled())
{
}
errorCount++;
}
{
// If this is the last one in a bounded set, make sure it is the
// same as the database key.
{
if(debugEnabled())
{
"index %s. Last values bytes %s, Key bytes %s",
}
errorCount++;
}
}
lastValues = values;
if(verifyID)
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
errorCount++;
continue;
}
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
{
errorCount++;
if(debugEnabled())
{
"which does not match the values%n%s",
}
}
}
}
}
}
finally
{
}
}
/**
* Iterate through the entries in an attribute index to perform a check for
* index cleanliness.
* @param attrType The attribute type of the index to be checked.
* @param index The index database to be checked.
* @param indexType Type of the index (ie, SUBSTRING, ORDERING)
* @throws JebException If an error occurs in the JE backend.
* @throws DatabaseException If an error occurs in the JE database.
*/
throws JebException, DatabaseException
{
{
return;
}
try
{
{
keyCount++;
try
{
}
catch (Exception e)
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
if (entryIDList.isDefined())
{
{
{
if (debugEnabled())
{
}
}
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
errorCount++;
continue;
}
{
errorCount++;
if (debugEnabled())
{
}
continue;
}
// As an optimization avoid passing in a real set and wasting time
// hashing and comparing a potentially large set of values, as well
// as using up memory. Instead just intercept the add() method and
// detect when an equivalent value has been added.
// We need to use an AtomicBoolean here since anonymous classes
// require referenced external variables to be final.
{
{
// The set is always empty.
}
public int size()
{
// The set is always empty.
return 0;
}
public boolean add(byte[] e)
{
// We could terminate processing at this point by throwing an
// UnsupportedOperationException, but this optimization is
// already ugly enough.
foundMatchingKey.set(true);
}
return true;
}
};
if (!foundMatchingKey.get())
{
errorCount++;
if (debugEnabled())
{
}
}
}
}
}
}
finally
{
}
}
/**
* Check that an index is complete for a given entry.
*
* @param entryID The entry ID.
* @param entry The entry to be checked.
*/
{
if (verifyDN2ID)
{
}
if (verifyID2Children)
{
}
if (verifyID2Subtree)
{
}
}
/**
* Check that the DN2ID index is complete for a given entry.
*
* @param entryID The entry ID.
* @param entry The entry to be checked.
*/
{
// Check the ID is in dn2id with the correct DN.
try
{
{
if (debugEnabled())
{
dn.toNormalizedString());
}
errorCount++;
}
{
if (debugEnabled())
{
dn.toNormalizedString());
}
errorCount++;
}
}
catch (Exception e)
{
if (debugEnabled())
{
e.getMessage());
}
errorCount++;
}
// Check the parent DN is in dn2id.
{
try
{
{
if (debugEnabled())
{
}
errorCount++;
}
}
catch (Exception e)
{
if (debugEnabled())
{
e.getMessage());
}
errorCount++;
}
}
}
/**
* Check that the ID2Children index is complete for a given entry.
*
* @param entryID The entry ID.
* @param entry The entry to be checked.
*/
{
{
try
{
{
if (debugEnabled())
{
}
errorCount++;
}
}
catch (Exception e)
{
if (debugEnabled())
{
e.getMessage());
}
errorCount++;
}
{
try
{
{
if (debugEnabled())
{
"for key %d.%n",
}
errorCount++;
}
{
}
}
catch (DatabaseException e)
{
if (debugEnabled())
{
}
errorCount++;
}
}
}
}
/**
* Check that the ID2Subtree index is complete for a given entry.
*
* @param entryID The entry ID.
* @param entry The entry to be checked.
*/
{
{
try
{
{
if (debugEnabled())
{
dn.toNormalizedString());
}
errorCount++;
}
}
catch (Exception e)
{
if (debugEnabled())
{
e.getMessage());
}
errorCount++;
}
{
try
{
{
if (debugEnabled())
{
"for key %d.%n",
}
errorCount++;
}
{
}
}
catch (DatabaseException e)
{
if (debugEnabled())
{
}
errorCount++;
}
}
}
}
/**
* Construct a printable string from a raw key value.
*
* @param index The index database containing the key value.
* @param keyBytes The bytes of the key.
* @return A string that may be logged or printed.
*/
{
/*
String str;
try
{
str = new String(keyBytes, "UTF-8");
}
catch (UnsupportedEncodingException e)
{
str = StaticUtils.bytesToHex(keyBytes);
}
return str;
*/
}
/**
* Construct a printable string from a raw key value.
*
* @param vlvIndex The vlvIndex database containing the key value.
* @param keySortValues THe sort values that is being used as the key.
* @return A string that may be logged or printed.
*/
{
/*
String str;
try
{
str = new String(keyBytes, "UTF-8");
}
catch (UnsupportedEncodingException e)
{
str = StaticUtils.bytesToHex(keyBytes);
}
return str;
*/
if(keySortValues == null)
{
}
else
{
}
}
/**
* Check that an attribute index is complete for a given entry.
*
* @param entryID The entry ID.
* @param entry The entry to be checked.
*/
{
{
try
{
{
}
}
catch (DirectoryException e)
{
if (debugEnabled())
{
"entry <%s>: %s.%n",
}
}
}
{
try
{
{
{
if(debugEnabled())
{
}
errorCount++;
}
}
}
catch (DirectoryException e)
{
if (debugEnabled())
{
"base DN for VLV index %s: %s",
}
errorCount++;
}
catch (DatabaseException e)
{
if (debugEnabled())
{
StaticUtils.getBacktrace(e));
}
errorCount++;
}
catch (JebException e)
{
if (debugEnabled())
{
StaticUtils.getBacktrace(e));
}
errorCount++;
}
}
}
/**
* Check that an attribute index is complete for a given attribute.
*
* @param attrIndex The attribute index to be checked.
* @param entryID The entry ID.
* @param attrList The attribute to be checked.
* @throws DirectoryException If a Directory Server error occurs.
*/
throws DirectoryException
{
// TODO: Add support for Extended Matching Rules indexes.
// Presence index.
{
try
{
{
if (debugEnabled())
{
}
errorCount++;
}
{
}
}
catch (DatabaseException e)
{
if (debugEnabled())
{
e.getMessage(),
}
errorCount++;
}
}
{
{
{
// Equality index.
if (equalityIndex != null)
{
try
{
{
if (debugEnabled())
{
}
errorCount++;
}
{
}
}
catch (DatabaseException e)
{
if (debugEnabled())
{
e.getMessage(),
}
errorCount++;
}
}
// Substring index.
if (substringIndex != null)
{
{
try
{
{
if (debugEnabled())
{
}
errorCount++;
}
{
}
}
catch (DatabaseException e)
{
if (debugEnabled())
{
e.getMessage(),
}
errorCount++;
}
}
}
// Ordering index.
if (orderingIndex != null)
{
// Use the ordering matching rule to normalize the value.
try
{
{
if (debugEnabled())
{
}
errorCount++;
}
{
}
}
catch (DatabaseException e)
{
if (debugEnabled())
{
e.getMessage(),
}
errorCount++;
}
}
// Approximate index.
if (approximateIndex != null)
{
// Use the approximate matching rule to normalize the value.
try
{
{
if (debugEnabled())
{
}
errorCount++;
}
{
}
}
catch (DatabaseException e)
{
if (debugEnabled())
{
e.getMessage(),
}
errorCount++;
}
}
}
}
}
}
/**
* Get the parent DN of a given DN.
*
* @param dn The DN.
* @return The parent DN or null if the given DN is a base DN.
*/
{
{
return null;
}
return dn.getParentDNInSuffix();
}
/**
* This class reports progress of the verify job at fixed intervals.
*/
{
/**
* The total number of records to process.
*/
private long totalCount;
/**
* The number of records that had been processed at the time of the
* previous progress report.
*/
/**
* The time in milliseconds of the previous progress report.
*/
private long previousTime;
/**
* The environment statistics at the time of the previous report.
*/
/**
* The number of bytes in a megabyte.
* Note that 1024*1024 bytes may eventually become known as a mebibyte(MiB).
*/
/**
* Create a new verify progress task.
* @throws DatabaseException An error occurred while accessing the JE
* database.
*/
{
}
/**
* Create a new verify progress task.
* @param indexIterator boolean, indicates if the task is iterating
* through indexes or the entries.
* @throws DatabaseException An error occurred while accessing the JE
* database.
*/
{
if (indexIterator)
{
if (verifyDN2ID)
{
}
else if (verifyID2Children)
{
}
else if (verifyID2Subtree)
{
}
else
{
{
totalCount = 0;
{
}
{
}
{
}
{
}
{
}
// TODO: Add support for Extended Matching Rules indexes.
{
}
}
}
else
{
}
}
/**
* The action to be performed by this timer task.
*/
public void run()
{
long latestCount = keyCount;
if (deltaTime == 0)
{
return;
}
try
{
long nCacheMiss =
float cacheMissRate = 0;
if (deltaCount > 0)
{
}
}
catch (DatabaseException e)
{
if (debugEnabled())
{
}
}
}
}
/**
* Adds an attribute of type t and value v to the statEntry, only if the
* statEntry is not null.
* @param statEntry passed in from backentryImpl.verifyBackend.
* @param t String to be used as the attribute type.
* @param v String to be used as the attribute value.
*/
{
{
}
}
}