if (targetOffset < 0)
{
// The client specified a negative target offset. This should never
// be allowed.
searchOperation.addResponseControl(
new VLVResponseControl(targetOffset, currentCount,
LDAPResultCode.OFFSET_RANGE_ERROR));
Message message = ERR_ENTRYIDSORTER_NEGATIVE_START_POS.get();
throw new DirectoryException(ResultCode.VIRTUAL_LIST_VIEW_ERROR,
message);
}
else if (targetOffset == 0)
{
// This is an easy mistake to make, since VLV offsets start at 1
// instead of 0. We'll assume the client meant to use 1.
targetOffset = 1;
}
int listOffset = targetOffset - 1; // VLV offsets start at 1, not 0.
int startPos = listOffset - beforeCount;
if (startPos < 0)
{
// 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.
startPos = 0;
beforeCount = listOffset;
}
else if(startPos >= currentCount)
{
// 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.
// 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.
targetOffset = currentCount + 1;
listOffset = currentCount;
startPos = listOffset - beforeCount;
afterCount = 0;
}
int count = 1 + beforeCount + afterCount;
selectedIDs = new long[count];
DatabaseEntry key = new DatabaseEntry();
OperationStatus status;
LockMode lockMode = LockMode.DEFAULT;
DatabaseEntry data = new DatabaseEntry();
Cursor cursor = openCursor(txn, CursorConfig.READ_COMMITTED);
try
{
//Locate the set that contains the target entry.
int cursorCount = 0;
int selectedPos = 0;
status = cursor.getFirst(key, data,lockMode);
while(status == OperationStatus.SUCCESS)
{
if(debugEnabled())
{
StringBuilder searchKeyHex = new StringBuilder();
StaticUtils.byteArrayToHexPlusAscii(searchKeyHex, key.getData(),
4);
StringBuilder foundKeyHex = new StringBuilder();
StaticUtils.byteArrayToHexPlusAscii(foundKeyHex, key.getData(),
4);
TRACER.debugVerbose("Retrieved a sort values set in VLV " +
"vlvIndex %s\nSearch Key:%s\nFound Key:%s\n",
config.getName(),
searchKeyHex,
foundKeyHex);
}
long[] IDs = SortValuesSet.getEncodedIDs(data.getData(), 0);
for(int i = startPos + selectedPos - cursorCount;
i < IDs.length && selectedPos < count;
i++, selectedPos++)
{
selectedIDs[selectedPos] = IDs[i];
}
cursorCount += IDs.length;
status = cursor.getNext(key, data,lockMode);
}
if (selectedPos < count)
{
// We don't have enough entries in the set to meet the requested
// page size, so we'll need to shorten the array.
long[] newIDArray = new long[selectedPos];
System.arraycopy(selectedIDs, 0, newIDArray, 0, selectedPos);
selectedIDs = newIDArray;
}
searchOperation.addResponseControl(
new VLVResponseControl(targetOffset, currentCount,
LDAPResultCode.SUCCESS));
if(debugBuilder != null)
{
debugBuilder.append("[COUNT:");
debugBuilder.append(cursorCount);
debugBuilder.append("]");
}
}
finally
{
cursor.close();
}
}
else
{
int targetOffset = 0;
int includedBeforeCount = 0;
int includedAfterCount = 0;
LinkedList<EntryID> idList = new LinkedList<EntryID>();
DatabaseEntry key = new DatabaseEntry();
OperationStatus status;
LockMode lockMode = LockMode.DEFAULT;
DatabaseEntry data = new DatabaseEntry();
Cursor cursor = openCursor(txn, CursorConfig.READ_COMMITTED);
try
{
ByteSequence vBytes = vlvRequest.getGreaterThanOrEqualAssertion();
ByteStringBuilder keyBytes =
new ByteStringBuilder(vBytes.length() + 4);
keyBytes.appendBERLength(vBytes.length());
vBytes.copyTo(keyBytes);
key.setData(keyBytes.getBackingArray(), 0, keyBytes.length());
status = cursor.getSearchKeyRange(key, data, lockMode);
if(status == OperationStatus.SUCCESS)
{
if(debugEnabled())
{
StringBuilder searchKeyHex = new StringBuilder();
StaticUtils.byteArrayToHexPlusAscii(searchKeyHex, key.getData(),
4);
StringBuilder foundKeyHex = new StringBuilder();
StaticUtils.byteArrayToHexPlusAscii(foundKeyHex, key.getData(),
4);
TRACER.debugVerbose("Retrieved a sort values set in VLV " +
"vlvIndex %s\nSearch Key:%s\nFound Key:%s\n",
config.getName(),
searchKeyHex,
foundKeyHex);
}
SortValuesSet sortValuesSet =
new SortValuesSet(key.getData(), data.getData(), this);
AttributeValue[] assertionValue = new AttributeValue[1];
assertionValue[0] =
AttributeValues.create(
sortOrder.getSortKeys()[0].getAttributeType(),
vlvRequest.getGreaterThanOrEqualAssertion());
int adjustedTargetOffset =
sortValuesSet.binarySearch(-1, assertionValue);
if(adjustedTargetOffset < 0)
{
// For a negative return value r, the vlvIndex -(r+1) gives the
// array index of the ID that is greater then the assertion value.
adjustedTargetOffset = -(adjustedTargetOffset+1);
}
targetOffset = adjustedTargetOffset;
// Iterate through all the sort values sets before this one to find
// the target offset in the index.
int lastOffset = adjustedTargetOffset - 1;
long[] lastIDs = sortValuesSet.getEntryIDs();
while(true)
{
for(int i = lastOffset;
i >= 0 && includedBeforeCount < beforeCount; i--)
{
idList.addFirst(new EntryID(lastIDs[i]));
includedBeforeCount++;
}
status = cursor.getPrev(key, data, lockMode);
if(status != OperationStatus.SUCCESS)
{
break;
}
if(includedBeforeCount < beforeCount)
{
lastIDs =
SortValuesSet.getEncodedIDs(data.getData(), 0);
lastOffset = lastIDs.length - 1;
targetOffset += lastIDs.length;
}
else
{
targetOffset += SortValuesSet.getEncodedSize(data.getData(), 0);
}
}
// Set the cursor back to the position of the target entry set
key.setData(sortValuesSet.getKeyBytes());
cursor.getSearchKey(key, data, lockMode);
// Add the target and after count entries if the target was found.
lastOffset = adjustedTargetOffset;
lastIDs = sortValuesSet.getEntryIDs();
int afterIDCount = 0;
while(true)
{
for(int i = lastOffset;
i < lastIDs.length && includedAfterCount < afterCount + 1;
i++)
{
idList.addLast(new EntryID(lastIDs[i]));
includedAfterCount++;
}
if(includedAfterCount >= afterCount + 1)
{
break;
}
status = cursor.getNext(key, data, lockMode);
if(status != OperationStatus.SUCCESS)
{
break;
}
lastIDs =
SortValuesSet.getEncodedIDs(data.getData(), 0);
lastOffset = 0;
afterIDCount += lastIDs.length;
}
selectedIDs = new long[idList.size()];
Iterator<EntryID> idIterator = idList.iterator();
for (int i=0; i < selectedIDs.length; i++)
{
selectedIDs[i] = idIterator.next().longValue();
}
searchOperation.addResponseControl(
new VLVResponseControl(targetOffset + 1, currentCount,
LDAPResultCode.SUCCESS));
if(debugBuilder != null)
{
debugBuilder.append("[COUNT:");