From 4b00c9c4997b772b184f6f5bc3b3554e57561ccb Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 17 Oct 2013 12:49:49 +0000
Subject: [PATCH] Preliminary work for OPENDJ-701: Implement paged results support
---
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java | 57 +++++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 47 insertions(+), 10 deletions(-)
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java
index 812fdfc..cbc1c89 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/MemoryBackend.java
@@ -43,6 +43,7 @@
import org.forgerock.opendj.ldap.controls.PostReadResponseControl;
import org.forgerock.opendj.ldap.controls.PreReadRequestControl;
import org.forgerock.opendj.ldap.controls.PreReadResponseControl;
+import org.forgerock.opendj.ldap.controls.SimplePagedResultsControl;
import org.forgerock.opendj.ldap.controls.SubtreeDeleteRequestControl;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.BindRequest;
@@ -396,15 +397,18 @@
if (matcher.matches(baseEntry).toBoolean()) {
sendEntry(attributeFilter, resultHandler, baseEntry);
}
+ resultHandler.handleResult(newResult(ResultCode.SUCCESS));
} else if (scope.equals(SearchScope.SINGLE_LEVEL) || scope.equals(SearchScope.WHOLE_SUBTREE)) {
- searchWithSubordinates(
- requestContext, resultHandler, dn, matcher,
- attributeFilter, request.getSizeLimit(), scope);
+ searchWithSubordinates(requestContext, resultHandler, dn, matcher, attributeFilter,
+ request.getSizeLimit(), scope, request.getControl(
+ SimplePagedResultsControl.DECODER, new DecodeOptions()));
} else {
throw newErrorResult(ResultCode.PROTOCOL_ERROR,
"Search request contains an unsupported search scope");
}
- resultHandler.handleResult(newResult(ResultCode.SUCCESS));
+ } catch (DecodeException e) {
+ resultHandler.handleErrorResult(ErrorResultException.newErrorResult(
+ ResultCode.PROTOCOL_ERROR, e.getMessage(), e));
} catch (final ErrorResultException e) {
resultHandler.handleErrorResult(e);
}
@@ -478,33 +482,66 @@
* @param attributeFilter to select attributes to return in search results
* @param sizeLimit maximum number of entries to return. A value of zero indicates no restriction
* on number of entries.
+ * @param pagedResults The simple paged results control, if present.
* @throws CancelledResultException
* If a cancellation request has been received and processing of
* the request should be aborted if possible.
* @throws ErrorResultException
* If the request is unsuccessful.
*/
- private void searchWithSubordinates(final RequestContext requestContext, final SearchResultHandler resultHandler,
- final DN dn, final Matcher matcher, final AttributeFilter attributeFilter, final int sizeLimit,
- SearchScope scope) throws CancelledResultException, ErrorResultException {
-
+ private void searchWithSubordinates(final RequestContext requestContext,
+ final SearchResultHandler resultHandler, final DN dn, final Matcher matcher,
+ final AttributeFilter attributeFilter, final int sizeLimit, SearchScope scope,
+ SimplePagedResultsControl pagedResults) throws CancelledResultException,
+ ErrorResultException {
+ final int pageSize = pagedResults != null ? pagedResults.getSize() : 0;
+ final int offset = (pagedResults != null && !pagedResults.getCookie().isEmpty())
+ ? Integer.valueOf(pagedResults.getCookie().toString()) : 0;
final Map<DN, Entry> subtree = entries.subMap(dn, dn.child(RDN.maxValue()));
int numberOfResults = 0;
+ int position = 0;
for (final Entry entry : subtree.values()) {
requestContext.checkIfCancelled(false);
if (scope.equals(SearchScope.WHOLE_SUBTREE) || entry.getName().isChildOf(dn)) {
if (matcher.matches(entry).toBoolean()) {
+ /*
+ * This entry is going to be returned to the client so it
+ * counts towards the size limit and any paging criteria.
+ */
+
+ // Check size limit.
if (sizeLimit > 0 && numberOfResults >= sizeLimit) {
throw newErrorResult(newResult(ResultCode.SIZE_LIMIT_EXCEEDED));
}
+
+ // Ignore this entry if we haven't reached the first page yet.
+ if (pageSize > 0 && position++ < offset) {
+ continue;
+ }
+
+ // Send the entry back to the client.
+ if (!sendEntry(attributeFilter, resultHandler, entry)) {
+ // Client has disconnected or cancelled.
+ break;
+ }
+
numberOfResults++;
- boolean acceptMoreResults = sendEntry(attributeFilter, resultHandler, entry);
- if (!acceptMoreResults) {
+
+ // Stop if we've reached the end of the page.
+ if (pageSize > 0 && numberOfResults == pageSize) {
break;
}
}
}
}
+ final Result result = newResult(ResultCode.SUCCESS);
+ if (pageSize > 0) {
+ final ByteString cookie =
+ numberOfResults == pageSize ? ByteString.valueOf(String.valueOf(position))
+ : ByteString.empty();
+ result.addControl(SimplePagedResultsControl.newControl(true, 0, cookie));
+ }
+ resultHandler.handleResult(result);
}
private <R extends Result> R addResultControls(final Request request, final Entry before,
--
Gitblit v1.10.0