diff --git a/mvn-build/core/src/main/java/org/springframework/ldap/control/VirtualListViewControlAggregateDirContextProcessor.java b/mvn-build/core/src/main/java/org/springframework/ldap/control/VirtualListViewControlAggregateDirContextProcessor.java
new file mode 100644
index 00000000..643a4e5d
--- /dev/null
+++ b/mvn-build/core/src/main/java/org/springframework/ldap/control/VirtualListViewControlAggregateDirContextProcessor.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ldap.control;
+
+import org.springframework.ldap.core.support.AggregateDirContextProcessor;
+
+/**
+ * AggregateDirContextProcessor implementation for managing a virtual list view
+ * by aggregating DirContextProcessor implementations for a VirtualListViewControl
+ * and its required companion SortControl.
+ *
+ * @author Mattias Arthursson
+ * @author Ulrik Sandberg
+ */
+public class VirtualListViewControlAggregateDirContextProcessor extends AggregateDirContextProcessor {
+
+ private VirtualListViewControlDirContextProcessor vlvProcessor;
+
+ private SortControlDirContextProcessor sortControlProcessor;
+
+ public VirtualListViewControlAggregateDirContextProcessor(String sortKey, int pageSize) {
+ }
+}
diff --git a/sandbox/src/main/java/org/springframework/ldap/control/VirtualListViewControlDirContextProcessor.java b/mvn-build/core/src/main/java/org/springframework/ldap/control/VirtualListViewControlDirContextProcessor.java
similarity index 89%
rename from sandbox/src/main/java/org/springframework/ldap/control/VirtualListViewControlDirContextProcessor.java
rename to mvn-build/core/src/main/java/org/springframework/ldap/control/VirtualListViewControlDirContextProcessor.java
index 26070185..34e6cceb 100644
--- a/sandbox/src/main/java/org/springframework/ldap/control/VirtualListViewControlDirContextProcessor.java
+++ b/mvn-build/core/src/main/java/org/springframework/ldap/control/VirtualListViewControlDirContextProcessor.java
@@ -146,6 +146,11 @@ public class VirtualListViewControlDirContextProcessor implements
this.responseControlClass = responseControlClass;
}
+ /**
+ * Set whether the targetOffset should be interpreted as
+ * percentage of the list or an offset into the list.
+ * @param isPercentage true if targetOffset is a percentage
+ */
public void setOffsetPercentage(boolean isPercentage) {
this.offsetPercentage = isPercentage;
}
@@ -189,9 +194,16 @@ public class VirtualListViewControlDirContextProcessor implements
try {
VirtualListViewControl virtualListViewControl;
if (offsetPercentage) {
+ // Request a view of a portion of the list centered around a
+ // given target entry. The position of the target entry is
+ // estimated as a percentage of the list.
virtualListViewControl = new VirtualListViewControl(
targetOffset, pageSize, CRITICAL_CONTROL);
} else {
+ // Request a view of a portion of the list with the specified
+ // number of entries before and after a given target entry. The
+ // target entry is identified by means of an offset into the
+ // list.
virtualListViewControl = new VirtualListViewControl(
targetOffset, listSize, 0, pageSize - 1,
CRITICAL_CONTROL);
@@ -244,11 +256,11 @@ public class VirtualListViewControlDirContextProcessor implements
}
/**
- * Check if the given control matches a paged results response control.
+ * Check if the given control matches a virtual list view response control.
*
* @param responseControl
* the control to check for a match
- * @return whether the control is a paged results response control
+ * @return whether the control is a virtual list view response control
*/
private boolean isVirtualListViewResponseControl(Control responseControl) {
if (responseControl.getClass().isAssignableFrom(responseControlClass)) {
diff --git a/sandbox/src/main/java/org/springframework/ldap/control/VirtualListViewResultsCookie.java b/mvn-build/core/src/main/java/org/springframework/ldap/control/VirtualListViewResultsCookie.java
similarity index 100%
rename from sandbox/src/main/java/org/springframework/ldap/control/VirtualListViewResultsCookie.java
rename to mvn-build/core/src/main/java/org/springframework/ldap/control/VirtualListViewResultsCookie.java
diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/control/ControlArrayMatcher.java b/mvn-build/core/src/test/java/org/springframework/ldap/control/ControlArrayMatcher.java
new file mode 100644
index 00000000..5b89ea16
--- /dev/null
+++ b/mvn-build/core/src/test/java/org/springframework/ldap/control/ControlArrayMatcher.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ldap.control;
+
+import java.util.Arrays;
+
+import javax.naming.ldap.Control;
+
+import org.easymock.AbstractMatcher;
+
+public class ControlArrayMatcher extends AbstractMatcher {
+ protected boolean argumentMatches(Object expected, Object actual) {
+ Control[] expectedControls = (Control[]) expected;
+ Control[] actualControls = (Control[]) actual;
+ if (expectedControls.length != actualControls.length) {
+ return false;
+ }
+ for (int i = 0; i < actualControls.length; i++) {
+ Control actualControl = actualControls[i];
+ Control expectedControl = expectedControls[i];
+ if (actualControl == null && expectedControl != null) {
+ return false;
+ }
+ if (actualControl != null && expectedControl == null) {
+ return false;
+ }
+ if (actualControl == null && expectedControl == null) {
+ continue;
+ }
+ if (!actualControl.getClass().equals(expectedControl.getClass())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected String argumentToString(Object argument) {
+ if (argument instanceof Control[]) {
+ Control[] control = (Control[]) argument;
+ return Arrays.toString(control);
+ }
+ return super.argumentToString(argument);
+ }
+}
diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultTest.java
index e42aa115..bdb9757e 100644
--- a/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultTest.java
+++ b/mvn-build/core/src/test/java/org/springframework/ldap/control/PagedResultTest.java
@@ -18,6 +18,8 @@ package org.springframework.ldap.control;
import java.util.LinkedList;
import java.util.List;
+import javax.naming.ldap.PagedResultsControl;
+
import org.springframework.ldap.control.PagedResult;
import org.springframework.ldap.control.PagedResultsCookie;
@@ -25,6 +27,13 @@ import com.gargoylesoftware.base.testing.EqualsTester;
import junit.framework.TestCase;
+/**
+ * Unit tests for the PagedResult class.
+ * {@link PagedResultsControl}
+ *
+ * @author Mattias Arthursson
+ * @author Ulrik Sandberg
+ */
public class PagedResultTest extends TestCase {
public void testEquals() throws Exception {
List expectedList = new LinkedList();
diff --git a/mvn-build/core/src/test/java/org/springframework/ldap/control/VirtualListViewControlDirContextProcessorTest.java b/mvn-build/core/src/test/java/org/springframework/ldap/control/VirtualListViewControlDirContextProcessorTest.java
new file mode 100644
index 00000000..e8c1cb08
--- /dev/null
+++ b/mvn-build/core/src/test/java/org/springframework/ldap/control/VirtualListViewControlDirContextProcessorTest.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2005-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ldap.control;
+
+import java.io.IOException;
+
+import javax.naming.ldap.Control;
+import javax.naming.ldap.LdapContext;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.easymock.MockControl;
+import org.springframework.ldap.OperationNotSupportedException;
+
+import com.sun.jndi.ldap.Ber;
+import com.sun.jndi.ldap.BerDecoder;
+import com.sun.jndi.ldap.BerEncoder;
+import com.sun.jndi.ldap.ctl.SortControl;
+import com.sun.jndi.ldap.ctl.VirtualListViewControl;
+import com.sun.jndi.ldap.ctl.VirtualListViewResponseControl;
+
+/**
+ * Unit tests for the VirtualListViewControlDirContextProcessor class.
+ *
+ * @author Ulrik Sandberg
+ */
+public class VirtualListViewControlDirContextProcessorTest extends TestCase {
+
+ private static final String OID_REQUEST = "2.16.840.1.113730.3.4.9";
+
+ private static final String OID_RESPONSE = "2.16.840.1.113730.3.4.10";
+
+ private MockControl ldapContextControl;
+
+ private LdapContext ldapContextMock;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // Create ldapContext mock
+ ldapContextControl = MockControl.createControl(LdapContext.class);
+ ldapContextMock = (LdapContext) ldapContextControl.getMock();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ ldapContextControl = null;
+ ldapContextMock = null;
+ }
+
+ protected void replay() {
+ ldapContextControl.replay();
+ }
+
+ protected void verify() {
+ ldapContextControl.verify();
+ }
+
+ public void testPreProcess() throws Exception {
+ final VirtualListViewControl control = new VirtualListViewControl(0, 3,
+ true);
+ int pageSize = 5;
+ VirtualListViewControlDirContextProcessor tested = new VirtualListViewControlDirContextProcessor(
+ pageSize) {
+ public Control createRequestControl() {
+ return control;
+ }
+ };
+
+ ldapContextControl.expectAndReturn(
+ ldapContextMock.getRequestControls(), new Control[0]);
+
+ SortControl sortControl = new SortControl(new String[] { "cn" }, true);
+ VirtualListViewControl vlvControl = new VirtualListViewControl(0, 0, 0,
+ 0, true);
+
+ Control[] controls = new Control[] { sortControl, vlvControl };
+ ldapContextMock.setRequestControls(controls);
+ // just check that class names match
+ ldapContextControl.setMatcher(new ControlArrayMatcher());
+
+ replay();
+
+ tested.preProcess(ldapContextMock);
+
+ verify();
+ }
+
+ public void testCreateRequestControlWithTargetAsOffset() throws Exception {
+ int pageSize = 5;
+ int targetOffset = 25;
+ int listSize = 1000;
+ VirtualListViewControlDirContextProcessor tested = new VirtualListViewControlDirContextProcessor(
+ pageSize, targetOffset, listSize,
+ new VirtualListViewResultsCookie(new byte[0], 0, 0));
+ VirtualListViewControl result = (VirtualListViewControl) tested
+ .createRequestControl();
+ assertNotNull(result);
+ assertEquals(OID_REQUEST, result.getID());
+
+ // verify that the values have been encoded as we expect
+ int expectedBeforeCount = 0;
+ int expectedAfterCount = 4;
+ int expectedOffset = 25;
+ int expectedContentCount = listSize;
+ assertEncodedRequest(result.getEncodedValue(), expectedBeforeCount,
+ expectedAfterCount, expectedOffset, expectedContentCount,
+ new byte[0]);
+ }
+
+ public void testCreateRequestControlWithTargetAsPercentage()
+ throws Exception {
+ int pageSize = 5;
+ int targetPercentage = 25;
+ int listSize = 1000;
+ VirtualListViewControlDirContextProcessor tested = new VirtualListViewControlDirContextProcessor(
+ pageSize, targetPercentage, listSize,
+ new VirtualListViewResultsCookie(new byte[0], 0, 0));
+ tested.setOffsetPercentage(true);
+ VirtualListViewControl result = (VirtualListViewControl) tested
+ .createRequestControl();
+ assertNotNull(result);
+ assertEquals(OID_REQUEST, result.getID());
+
+ int expectedBeforeCount = 2;
+ int expectedAfterCount = 2;
+ // interestingly, it seems rather than calculate what 25% of 1000 is,
+ // the VLVControl requests 25 out of an expected 100
+ int expectedOffset = 25;
+ int expectedContentCount = 100;
+ assertEncodedRequest(result.getEncodedValue(), expectedBeforeCount,
+ expectedAfterCount, expectedOffset, expectedContentCount,
+ new byte[0]);
+ }
+
+ public void testPostProcess() throws Exception {
+ int pageSize = 5;
+ int targetOffset = 25;
+ int listSize = 1000;
+ VirtualListViewControlDirContextProcessor tested = new VirtualListViewControlDirContextProcessor(
+ pageSize, targetOffset, listSize,
+ new VirtualListViewResultsCookie(new byte[0], 0, 0));
+
+ int virtualListViewResult = 53; // unwilling to perform
+ byte[] encoded = encodeResponseValue(10, listSize,
+ virtualListViewResult);
+ VirtualListViewResponseControl control = new VirtualListViewResponseControl(
+ OID_RESPONSE, false, encoded);
+ ldapContextControl.expectAndDefaultReturn(ldapContextMock
+ .getResponseControls(), new Control[] { control });
+
+ replay();
+
+ try {
+ tested.postProcess(ldapContextMock);
+ fail("OperationNotSupportedException expected");
+ }
+ catch (OperationNotSupportedException expected) {
+ Throwable cause = expected.getCause();
+ assertEquals(javax.naming.OperationNotSupportedException.class,
+ cause.getClass());
+ assertEquals("[LDAP: error code 53 - Unwilling To Perform]", cause
+ .getMessage());
+ }
+ verify();
+ assertNotNull(tested.getCookie());
+ assertEquals(0, tested.getCookie().getCookie().length);
+ }
+
+ public void testBerDecoding() throws Exception {
+ int virtualListViewResult = 53; // unwilling to perform
+ byte[] encoded = encodeResponseValue(10, 1000, virtualListViewResult);
+
+ int expectedLength = 14;
+ assertEncodedResponse(encoded, expectedLength, 10, 1000, 53,
+ new byte[0]);
+ }
+
+ private byte[] encodeResponseValue(int targetPosition, int contentCount,
+ int virtualListViewResult) throws IOException {
+
+ // build the ASN.1 encoding
+ BerEncoder ber = new BerEncoder(10);
+
+ ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
+ ber.encodeInt(targetPosition); // list offset for the target entry
+ ber.encodeInt(contentCount); // server's estimate of the current
+ // number of entries in the list
+ ber.encodeInt(virtualListViewResult, Ber.ASN_ENUMERATED);
+ ber.encodeOctetString(new byte[0], Ber.ASN_OCTET_STR);
+ ber.endSeq();
+
+ return ber.getTrimmedBuf();
+ }
+
+ private void assertEncodedRequest(byte[] encodedValue,
+ int expectedBeforeCount, int expectedAfterCount,
+ int expectedOffset, int expectedContentCount,
+ byte[] expectedContextId) throws Exception {
+ dumpEncodedValue("VirtualListViewRequest\n", encodedValue);
+ BerDecoder ber = new BerDecoder(encodedValue, 0, encodedValue.length);
+ ber.parseSeq(null);
+
+ int actualBeforeCount = ber.parseInt();
+ int actualAfterCount = ber.parseInt();
+ byte targetType = (byte) ber.parseByte();
+ targetType <<= 3; // skip highest three bits
+ targetType >>= 3;
+ ber.parseLength(); // ignore
+ switch (targetType) {
+ case 0: // byOffset
+ int actualOffset = ber.parseInt();
+ int actualContentCount = ber.parseInt();
+ assertEquals("beforeCount,", expectedBeforeCount, actualBeforeCount);
+ assertEquals("afterCount,", expectedAfterCount, actualAfterCount);
+ assertEquals("offset,", expectedOffset, actualOffset);
+ assertEquals("contentCount,", expectedContentCount,
+ actualContentCount);
+ break;
+
+ case 1: // greaterThanOrEqual
+ throw new AssertionFailedError(
+ "CHOICE value greaterThanOrEqual not supported");
+
+ default:
+ throw new AssertionFailedError("illegal CHOICE value: "
+ + targetType);
+ }
+ byte[] bs = ber.parseOctetString(Ber.ASN_OCTET_STR, null);
+ assertContextId(expectedContextId, bs);
+ }
+
+ private void assertContextId(byte[] expectedContextId,
+ byte[] actualContextId) {
+ if (expectedContextId == null && actualContextId == null) {
+ return;
+ }
+ if (expectedContextId == null && actualContextId != null) {
+ fail("expected , got <" + actualContextId + ">");
+ }
+ if (expectedContextId != null && actualContextId == null) {
+ fail("expected <" + expectedContextId + ">, got ");
+ }
+ assertEquals(expectedContextId.length, actualContextId.length);
+ }
+
+ private void assertEncodedResponse(byte[] encodedValue,
+ int expectedEncodingLength, int expectedTargetPosition,
+ int expectedContentCount, int expectedVirtualListViewResult,
+ byte[] expectedContextId) throws Exception {
+ dumpEncodedValue("VirtualListViewResponse\n", encodedValue);
+ assertEquals(expectedEncodingLength, encodedValue.length);
+ BerDecoder ber = new BerDecoder(encodedValue, 0, encodedValue.length);
+ ber.parseSeq(null);
+
+ int actualTargetPosition = ber.parseInt();
+ int actualContentCount = ber.parseInt();
+ int actualVirtualListViewResult = ber.parseEnumeration();
+ assertEquals("targetPosition,", expectedTargetPosition,
+ actualTargetPosition);
+ assertEquals("contentCount,", expectedContentCount, actualContentCount);
+ assertEquals("virtualListViewResult,", expectedVirtualListViewResult,
+ actualVirtualListViewResult);
+ byte[] bs = ber.parseOctetString(Ber.ASN_OCTET_STR, null);
+ assertContextId(expectedContextId, bs);
+ }
+
+ private void dumpEncodedValue(String message, byte[] encodedValue) {
+ Ber.dumpBER(System.out, message, encodedValue, 0, encodedValue.length);
+ }
+}
diff --git a/sandbox/.springBeans b/sandbox/.springBeans
index c45e28d1..d5aef395 100644
--- a/sandbox/.springBeans
+++ b/sandbox/.springBeans
@@ -1,35 +1,34 @@
-
-
-
- xml
-
-
- src/iutest/conf/ldapTemplateBaseSuffixTestContext.xml
- src/iutest/conf/ldapTemplateTestContext.xml
-
-
-
- iutest with base
- false
- false
-
- src/iutest/conf/ldapTemplateBaseSuffixTestContext.xml
-
-
-
- iutesttransactions
- false
- false
-
-
-
-
- iutest
- false
- false
-
- src/iutest/conf/ldapTemplateTestContext.xml
-
-
-
-
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+ false
+ false
+
+
+
+
+
+ false
+ false
+
+
+
+
+
+ false
+ false
+
+
+
+
+
diff --git a/sandbox/src/main/java/org/springframework/ldap/control/DummyVLVDirContextProcessor.java b/sandbox/src/main/java/org/springframework/ldap/control/DummyVLVDirContextProcessor.java
deleted file mode 100644
index 64e9cdcd..00000000
--- a/sandbox/src/main/java/org/springframework/ldap/control/DummyVLVDirContextProcessor.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.springframework.ldap.control;
-
-import org.springframework.ldap.core.support.AggregateDirContextProcessor;
-
-public class DummyVLVDirContextProcessor extends AggregateDirContextProcessor {
-
- private VirtualListViewControlDirContextProcessor vlvProcessor;
-
- private SortControlDirContextProcessor sortControlProcessor;
-
- public DummyVLVDirContextProcessor(String sortKey, int pageSize) {
- }
-}