LDAP-234: Rollback intermittently causes data loss
Moved stuff related to LDAP-176 from sandbox to core. Minor formatting tweaks.
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2005-2010 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.support.ad;
|
||||
|
||||
/**
|
||||
* @since 1.3.2
|
||||
* @author Marius Scurtescu
|
||||
*/
|
||||
public interface AttributeValueProcessor {
|
||||
public void process(Object value);
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2005-2010 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.support.ad;
|
||||
|
||||
import org.springframework.ldap.core.AttributesMapper;
|
||||
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.NamingEnumeration;
|
||||
|
||||
/**
|
||||
* Utility class that helps with reading all attribute values from Active Directory using <em>Incremental Retrieval of
|
||||
* Multi-valued Properties</em>.
|
||||
* <p/>
|
||||
* <p>Example usage of this attribute mapper:
|
||||
* <pre>
|
||||
* public void retrieveAttributeIncrementally(LdapTemplate ldap, LdapName entrDn, String attributeName, AttributeValueProcessor valueProcessor)
|
||||
* {
|
||||
* IncrementalAttributeMapper incrementalAttributeMapper = new IncrementalAttributeMapper(attributeName, valueProcessor);
|
||||
*
|
||||
* while (incrementalAttributeMapper.hasMore())
|
||||
* {
|
||||
* ldap.lookup(entrDn, incrementalAttributeMapper.getAttributesArray(), incrementalAttributeMapper);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Marius Scurtescu
|
||||
* @see <a href="http://www.watersprings.org/pub/id/draft-kashi-incremental-00.txt">Incremental Retrieval of Multi-valued Properties</a>
|
||||
* @since 1.3.2
|
||||
*/
|
||||
public class IncrementalAttributeMapper implements AttributesMapper {
|
||||
private final String attributeName;
|
||||
private final AttributeValueProcessor valueProcessor;
|
||||
|
||||
private boolean more = true;
|
||||
private int pageSize;
|
||||
private RangeOption requestRange = new RangeOption(0, pageSize);
|
||||
private boolean omitFullRange = true;
|
||||
|
||||
public IncrementalAttributeMapper(String attributeName, AttributeValueProcessor valueProcessor) {
|
||||
this(attributeName, valueProcessor, RangeOption.TERMINAL_END_OF_RANGE);
|
||||
}
|
||||
|
||||
public IncrementalAttributeMapper(String attributeName, AttributeValueProcessor valueProcessor, int pageSize) {
|
||||
this.attributeName = attributeName;
|
||||
this.pageSize = pageSize;
|
||||
this.requestRange = new RangeOption(0, pageSize);
|
||||
this.valueProcessor = valueProcessor;
|
||||
}
|
||||
|
||||
public boolean isOmitFullRange() {
|
||||
return omitFullRange;
|
||||
}
|
||||
|
||||
public void setOmitFullRange(boolean omitFullRange) {
|
||||
this.omitFullRange = omitFullRange;
|
||||
}
|
||||
|
||||
public Object mapFromAttributes(Attributes attributes) throws NamingException {
|
||||
if (!more) {
|
||||
throw new IllegalStateException("No more attributes!");
|
||||
}
|
||||
|
||||
more = false;
|
||||
|
||||
NamingEnumeration attributeNameEnum = attributes.getIDs();
|
||||
|
||||
while (attributeNameEnum.hasMore()) {
|
||||
String attributeName = (String) attributeNameEnum.next();
|
||||
|
||||
if (attributeName.equals(this.attributeName)) {
|
||||
processValues(attributes, this.attributeName);
|
||||
} else if (attributeName.startsWith(this.attributeName + ";")) {
|
||||
String[] attributeNameSplit = attributeName.split(";");
|
||||
for (int i = 0; i < attributeNameSplit.length; i++) {
|
||||
String option = attributeNameSplit[i];
|
||||
|
||||
RangeOption responseRange = RangeOption.parse(option);
|
||||
|
||||
if (responseRange != null) {
|
||||
more = requestRange.compareTo(responseRange) > 0;
|
||||
|
||||
if (more) {
|
||||
requestRange = responseRange.nextRange(pageSize);
|
||||
}
|
||||
|
||||
processValues(attributes, attributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private void processValues(Attributes attributes, String attributeName) throws NamingException {
|
||||
Attribute attribute = attributes.get(attributeName);
|
||||
NamingEnumeration valueEnum = attribute.getAll();
|
||||
|
||||
while (valueEnum.hasMore()) {
|
||||
valueProcessor.process(valueEnum.next());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasMore() {
|
||||
return more;
|
||||
}
|
||||
|
||||
public String[] getAttributesArray() {
|
||||
StringBuilder attributeBuilder = new StringBuilder(attributeName);
|
||||
|
||||
if (!(omitFullRange && requestRange.isFullRange())) {
|
||||
attributeBuilder.append(';');
|
||||
|
||||
requestRange.toString(attributeBuilder);
|
||||
}
|
||||
|
||||
return new String[]{attributeBuilder.toString()};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2005-2010 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.support.ad;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @since 1.3.2
|
||||
* @author Marius Scurtescu
|
||||
*/
|
||||
public class ListAttributeValueProcessor implements AttributeValueProcessor {
|
||||
private List values = new LinkedList();
|
||||
|
||||
public void process(Object value) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
public List getValues() {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,19 @@
|
||||
package org.springframework.ldap.util;
|
||||
/*
|
||||
* Copyright 2005-2010 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.support.ad;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -7,66 +22,62 @@ import java.util.regex.Matcher;
|
||||
* Attribute name Range Option used for <em>Incremental Retrieval of
|
||||
* Multi-valued Properties</em>.
|
||||
*
|
||||
* @see org.springframework.ldap.util.IncrementalAttributeMapper
|
||||
* @author Marius Scurtescu
|
||||
*/
|
||||
public class RangeOption implements Comparable<RangeOption>
|
||||
{
|
||||
*
|
||||
* @see IncrementalAttributeMapper
|
||||
* @since 1.3.2
|
||||
*/
|
||||
public class RangeOption implements Comparable {
|
||||
public static final int TERMINAL_END_OF_RANGE = -1;
|
||||
public static final int TERMINAL_MISSING = -2;
|
||||
|
||||
private int _initial = 0;
|
||||
private int _terminal = TERMINAL_END_OF_RANGE;
|
||||
private int initial = 0;
|
||||
private int terminal = TERMINAL_END_OF_RANGE;
|
||||
|
||||
private static Pattern RANGE_PATTERN = Pattern.compile("^Range=([0-9]+)(-([0-9]+|\\*))?$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public RangeOption(int initial)
|
||||
{
|
||||
public RangeOption(int initial) {
|
||||
this(initial, TERMINAL_END_OF_RANGE);
|
||||
}
|
||||
|
||||
public RangeOption(int initial, int terminal)
|
||||
{
|
||||
if (terminal < 0 && (terminal != TERMINAL_END_OF_RANGE && terminal != TERMINAL_MISSING))
|
||||
public RangeOption(int initial, int terminal) {
|
||||
if (terminal < 0 && (terminal != TERMINAL_END_OF_RANGE && terminal != TERMINAL_MISSING)) {
|
||||
throw new IllegalArgumentException("Illegal range-terminal: " + terminal);
|
||||
}
|
||||
|
||||
if (initial < 0)
|
||||
if (initial < 0) {
|
||||
throw new IllegalArgumentException("Illegal range-initial: " + initial);
|
||||
}
|
||||
|
||||
if (terminal >= 0 && terminal < initial)
|
||||
if (terminal >= 0 && terminal < initial) {
|
||||
throw new IllegalArgumentException("range-terminal cannot be smaller than range-initial: " + initial + "-" + terminal);
|
||||
}
|
||||
|
||||
_initial = initial;
|
||||
_terminal = terminal;
|
||||
this.initial = initial;
|
||||
this.terminal = terminal;
|
||||
}
|
||||
|
||||
public boolean isTerminalEndOfRange()
|
||||
{
|
||||
return _terminal == TERMINAL_END_OF_RANGE;
|
||||
public boolean isTerminalEndOfRange() {
|
||||
return terminal == TERMINAL_END_OF_RANGE;
|
||||
}
|
||||
|
||||
public boolean isTerminalMissing()
|
||||
{
|
||||
return _terminal == TERMINAL_MISSING;
|
||||
public boolean isTerminalMissing() {
|
||||
return terminal == TERMINAL_MISSING;
|
||||
}
|
||||
|
||||
public int getInitial()
|
||||
{
|
||||
return _initial;
|
||||
public int getInitial() {
|
||||
return initial;
|
||||
}
|
||||
|
||||
public int getTerminal()
|
||||
{
|
||||
return _terminal;
|
||||
public int getTerminal() {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
public boolean isFullRange()
|
||||
{
|
||||
public boolean isFullRange() {
|
||||
return getInitial() == 0 && getTerminal() == TERMINAL_END_OF_RANGE;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
public String toString() {
|
||||
StringBuilder rangeBuilder = new StringBuilder();
|
||||
|
||||
toString(rangeBuilder);
|
||||
@@ -74,37 +85,34 @@ public class RangeOption implements Comparable<RangeOption>
|
||||
return rangeBuilder.toString();
|
||||
}
|
||||
|
||||
public void toString(StringBuilder rangeBuilder)
|
||||
{
|
||||
rangeBuilder.append("Range=").append(_initial);
|
||||
public void toString(StringBuilder rangeBuilder) {
|
||||
rangeBuilder.append("Range=").append(initial);
|
||||
|
||||
if (!isTerminalMissing())
|
||||
{
|
||||
if (!isTerminalMissing()) {
|
||||
rangeBuilder.append('-');
|
||||
|
||||
if (isTerminalEndOfRange())
|
||||
rangeBuilder.append('*');
|
||||
else
|
||||
rangeBuilder.append(_terminal);
|
||||
rangeBuilder.append(terminal);
|
||||
}
|
||||
}
|
||||
|
||||
public static RangeOption parse(String option)
|
||||
{
|
||||
public static RangeOption parse(String option) {
|
||||
Matcher rangeMatcher = RANGE_PATTERN.matcher(option);
|
||||
|
||||
rangeMatcher.find();
|
||||
|
||||
if (!rangeMatcher.matches())
|
||||
if (!rangeMatcher.matches()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String initialStr = rangeMatcher.group(1);
|
||||
|
||||
int initial = Integer.parseInt(initialStr);
|
||||
int initial = Integer.parseInt(initialStr);
|
||||
int terminal = TERMINAL_MISSING;
|
||||
|
||||
if (rangeMatcher.group(2) != null)
|
||||
{
|
||||
if (rangeMatcher.group(2) != null) {
|
||||
String terminalStr = rangeMatcher.group(3);
|
||||
|
||||
if ("*".equals(terminalStr))
|
||||
@@ -116,38 +124,50 @@ public class RangeOption implements Comparable<RangeOption>
|
||||
return new RangeOption(initial, terminal);
|
||||
}
|
||||
|
||||
public int compareTo(RangeOption that)
|
||||
{
|
||||
public int compareTo(Object o) {
|
||||
RangeOption that;
|
||||
if (o instanceof RangeOption) {
|
||||
that = (RangeOption) o;
|
||||
} else {
|
||||
throw new IllegalArgumentException("A RangeOption instance cannot be compared to " + o.getClass());
|
||||
}
|
||||
|
||||
if (this.getInitial() != that.getInitial())
|
||||
throw new IllegalStateException("Ranges cannot be compared, range-initial not the same: " + this.toString() + " vs " + that.toString());
|
||||
|
||||
if (this.getTerminal() == that.getTerminal())
|
||||
if (this.getTerminal() == that.getTerminal()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (that.getTerminal() == TERMINAL_MISSING)
|
||||
if (that.getTerminal() == TERMINAL_MISSING) {
|
||||
throw new IllegalStateException("Don't know how to deal with missing range-terminal: " + that.toString());
|
||||
}
|
||||
|
||||
if (this.getTerminal() == TERMINAL_MISSING)
|
||||
if (this.getTerminal() == TERMINAL_MISSING) {
|
||||
throw new IllegalStateException("Don't know how to deal with missing range-terminal: " + this.toString());
|
||||
}
|
||||
|
||||
if (this.getTerminal() == TERMINAL_END_OF_RANGE)
|
||||
if (this.getTerminal() == TERMINAL_END_OF_RANGE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (that.getTerminal() == TERMINAL_END_OF_RANGE)
|
||||
if (that.getTerminal() == TERMINAL_END_OF_RANGE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.getTerminal() > that.getTerminal() ? 1 : -1;
|
||||
}
|
||||
|
||||
public RangeOption nextRange(int pageSize)
|
||||
{
|
||||
if (getTerminal() < 0)
|
||||
public RangeOption nextRange(int pageSize) {
|
||||
if (getTerminal() < 0) {
|
||||
throw new IllegalStateException("Cannot generate next range, range-terminal: " + getTerminal());
|
||||
}
|
||||
|
||||
if (pageSize < 0 && pageSize != TERMINAL_END_OF_RANGE)
|
||||
if (pageSize < 0 && pageSize != TERMINAL_END_OF_RANGE) {
|
||||
throw new IllegalArgumentException("Invalid page size: " + pageSize);
|
||||
}
|
||||
|
||||
int initial = getTerminal() + 1;
|
||||
int initial = getTerminal() + 1;
|
||||
int terminal = pageSize == TERMINAL_END_OF_RANGE ? TERMINAL_END_OF_RANGE : getTerminal() + pageSize;
|
||||
|
||||
return new RangeOption(initial, terminal);
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for Active Directory-specific functionality.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2005-2010 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.support.ad;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.BasicAttribute;
|
||||
import javax.naming.directory.BasicAttributes;
|
||||
|
||||
/**
|
||||
* IncrementalAttributeMapper Tester.
|
||||
*
|
||||
* @author Marius Scurtescu
|
||||
*/
|
||||
public class IncrementalAttributeMapperTest extends TestCase {
|
||||
private IncrementalAttributeMapper incrementalAttributeMapper;
|
||||
private ListAttributeValueProcessor valueProcessor;
|
||||
|
||||
public IncrementalAttributeMapperTest(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public void setUp() throws Exception {
|
||||
valueProcessor = new ListAttributeValueProcessor();
|
||||
incrementalAttributeMapper = new IncrementalAttributeMapper("member", valueProcessor);
|
||||
}
|
||||
|
||||
public void tearDown() throws Exception {
|
||||
incrementalAttributeMapper = null;
|
||||
valueProcessor = null;
|
||||
}
|
||||
|
||||
public void testGetAttributesArray() throws Exception {
|
||||
String[] attributes = incrementalAttributeMapper.getAttributesArray();
|
||||
|
||||
assertEquals(1, attributes.length);
|
||||
assertEquals("member", attributes[0]);
|
||||
|
||||
incrementalAttributeMapper = new IncrementalAttributeMapper("member", valueProcessor, 10);
|
||||
|
||||
attributes = incrementalAttributeMapper.getAttributesArray();
|
||||
|
||||
assertEquals(1, attributes.length);
|
||||
assertEquals("member;Range=0-10", attributes[0]);
|
||||
}
|
||||
|
||||
public void testLoopEmpty() throws Exception {
|
||||
assertTrue(incrementalAttributeMapper.hasMore());
|
||||
|
||||
Attributes attributes = new BasicAttributes();
|
||||
|
||||
incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertFalse(incrementalAttributeMapper.hasMore());
|
||||
assertEquals(0, valueProcessor.getValues().size());
|
||||
}
|
||||
|
||||
public void testLoop() throws Exception {
|
||||
Attributes attributes = createAttributes(new RangeOption(0, 10), true);
|
||||
|
||||
incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertTrue(incrementalAttributeMapper.hasMore());
|
||||
assertEquals(11, valueProcessor.getValues().size());
|
||||
|
||||
attributes = createAttributes(new RangeOption(11), 5, false);
|
||||
|
||||
incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertFalse(incrementalAttributeMapper.hasMore());
|
||||
assertEquals(16, valueProcessor.getValues().size());
|
||||
}
|
||||
|
||||
public void test1LoopWithPageSizeExact() throws Exception {
|
||||
incrementalAttributeMapper = new IncrementalAttributeMapper("member", valueProcessor, 10);
|
||||
|
||||
Attributes attributes = createAttributes(new RangeOption(0, 10), true);
|
||||
|
||||
incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertFalse(incrementalAttributeMapper.hasMore());
|
||||
assertEquals(11, valueProcessor.getValues().size());
|
||||
}
|
||||
|
||||
public void test2LoopsWithPageSizeExact() throws Exception {
|
||||
incrementalAttributeMapper = new IncrementalAttributeMapper("member", valueProcessor, 20);
|
||||
|
||||
Attributes attributes = createAttributes(new RangeOption(0, 10), true);
|
||||
|
||||
incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertTrue(incrementalAttributeMapper.hasMore());
|
||||
assertEquals(11, valueProcessor.getValues().size());
|
||||
|
||||
attributes = createAttributes(new RangeOption(11, 30), false);
|
||||
|
||||
incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertFalse(incrementalAttributeMapper.hasMore());
|
||||
assertEquals(31, valueProcessor.getValues().size());
|
||||
}
|
||||
|
||||
public void test2LoopsWithPageSize() throws Exception {
|
||||
incrementalAttributeMapper = new IncrementalAttributeMapper("member", valueProcessor, 20);
|
||||
|
||||
Attributes attributes = createAttributes(new RangeOption(0, 10), true);
|
||||
|
||||
incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertTrue(incrementalAttributeMapper.hasMore());
|
||||
assertEquals(11, valueProcessor.getValues().size());
|
||||
|
||||
attributes = createAttributes(new RangeOption(11), 5, false);
|
||||
|
||||
incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertFalse(incrementalAttributeMapper.hasMore());
|
||||
assertEquals(16, valueProcessor.getValues().size());
|
||||
}
|
||||
|
||||
private Attributes createAttributes(RangeOption range, boolean emptyPlain) {
|
||||
return createAttributes(range, range.getTerminal() - range.getInitial() + 1, emptyPlain);
|
||||
}
|
||||
|
||||
private Attributes createAttributes(RangeOption range, int valueCnt, boolean emptyPlain) {
|
||||
Attributes attributes = new BasicAttributes();
|
||||
|
||||
if (emptyPlain) {
|
||||
attributes.put(new BasicAttribute("member"));
|
||||
}
|
||||
|
||||
Attribute attribute = new BasicAttribute("member;" + range.toString());
|
||||
for (int i = 0; i < valueCnt; i++) {
|
||||
attribute.add("value" + (range.getInitial() + i - 1));
|
||||
}
|
||||
attributes.put(attribute);
|
||||
|
||||
return attributes;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,20 @@
|
||||
package org.springframework.ldap.util;
|
||||
/*
|
||||
* Copyright 2005-2010 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.support.ad;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
@@ -9,115 +22,79 @@ import junit.framework.TestCase;
|
||||
*
|
||||
* @author Marius Scurtescu
|
||||
*/
|
||||
public class RangeOptionTest extends TestCase
|
||||
{
|
||||
public RangeOptionTest(String name)
|
||||
{
|
||||
public class RangeOptionTest extends TestCase {
|
||||
public RangeOptionTest(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testConstructorInvalid()
|
||||
{
|
||||
try
|
||||
{
|
||||
public void testConstructorInvalid() {
|
||||
try {
|
||||
new RangeOption(101, 100);
|
||||
|
||||
fail();
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
// ignore
|
||||
fail("IllegalArgumentException expected");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
new RangeOption(-1, 100);
|
||||
|
||||
fail();
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
// ignore
|
||||
fail("IllegalArgumentException expected");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
new RangeOption(-10, 100);
|
||||
|
||||
fail();
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
// ignore
|
||||
fail("IllegalArgumentException expected");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
new RangeOption(0, -3);
|
||||
|
||||
fail();
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
// ignore
|
||||
fail("IllegalArgumentException expected");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testToString() throws Exception
|
||||
{
|
||||
public void testToString() throws Exception {
|
||||
RangeOption range = new RangeOption(0, 100);
|
||||
|
||||
assertEquals("Range=0-100", range.toString());
|
||||
|
||||
range = new RangeOption(0, RangeOption.TERMINAL_END_OF_RANGE);
|
||||
|
||||
assertEquals("Range=0-*", range.toString());
|
||||
|
||||
range = new RangeOption(0, RangeOption.TERMINAL_MISSING);
|
||||
|
||||
assertEquals("Range=0", range.toString());
|
||||
}
|
||||
|
||||
public void testParse() throws Exception
|
||||
{
|
||||
public void testParse() throws Exception {
|
||||
RangeOption range = RangeOption.parse("Range=0-100");
|
||||
|
||||
assertEquals(0, range.getInitial());
|
||||
assertEquals(100, range.getTerminal());
|
||||
|
||||
range = RangeOption.parse("range=0-100");
|
||||
|
||||
assertEquals(0, range.getInitial());
|
||||
assertEquals(100, range.getTerminal());
|
||||
|
||||
range = RangeOption.parse("RANGE=0-100");
|
||||
|
||||
assertEquals(0, range.getInitial());
|
||||
assertEquals(100, range.getTerminal());
|
||||
|
||||
range = RangeOption.parse("Range=0-*");
|
||||
|
||||
assertEquals(0, range.getInitial());
|
||||
assertEquals(RangeOption.TERMINAL_END_OF_RANGE, range.getTerminal());
|
||||
|
||||
range = RangeOption.parse("Range=10");
|
||||
|
||||
assertEquals(10, range.getInitial());
|
||||
assertEquals(RangeOption.TERMINAL_MISSING, range.getTerminal());
|
||||
}
|
||||
|
||||
public void testParseInvalid()
|
||||
{
|
||||
public void testParseInvalid() {
|
||||
assertNull(RangeOption.parse("Range=10-"));
|
||||
assertNull(RangeOption.parse("Range=10-a"));
|
||||
assertNull(RangeOption.parse("lang-en"));
|
||||
@@ -127,106 +104,81 @@ public class RangeOptionTest extends TestCase
|
||||
assertNull(RangeOption.parse("Range=10-100;lang-de"));
|
||||
}
|
||||
|
||||
public void testCompare()
|
||||
{
|
||||
public void testCompare() {
|
||||
RangeOption range1 = RangeOption.parse("Range=10-500");
|
||||
RangeOption range2 = RangeOption.parse("Range=10-500");
|
||||
|
||||
assertTrue(range1.compareTo(range2) == 0);
|
||||
assertTrue(range2.compareTo(range1) == 0);
|
||||
|
||||
range1 = RangeOption.parse("Range=0-*");
|
||||
range2 = RangeOption.parse("Range=0-*");
|
||||
|
||||
assertTrue(range1.compareTo(range2) == 0);
|
||||
assertTrue(range2.compareTo(range1) == 0);
|
||||
|
||||
range1 = RangeOption.parse("Range=0");
|
||||
range2 = RangeOption.parse("Range=0");
|
||||
|
||||
assertTrue(range1.compareTo(range2) == 0);
|
||||
assertTrue(range2.compareTo(range1) == 0);
|
||||
|
||||
range1 = RangeOption.parse("Range=0-101");
|
||||
range2 = RangeOption.parse("Range=0-100");
|
||||
|
||||
assertTrue(range1.compareTo(range2) > 0);
|
||||
assertTrue(range2.compareTo(range1) < 0);
|
||||
|
||||
range1 = RangeOption.parse("Range=0-*");
|
||||
range2 = RangeOption.parse("Range=0-100");
|
||||
|
||||
assertTrue(range1.compareTo(range2) > 0);
|
||||
assertTrue(range2.compareTo(range1) < 0);
|
||||
}
|
||||
|
||||
public void testCompareInvalid()
|
||||
{
|
||||
public void testCompareInvalid() {
|
||||
RangeOption range1 = RangeOption.parse("Range=10-500");
|
||||
RangeOption range2 = RangeOption.parse("Range=11-500");
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
assertTrue(range1.compareTo(range2) == 0);
|
||||
|
||||
fail();
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
// ignore
|
||||
fail("IllegalStateException expected");
|
||||
} catch (IllegalStateException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
range1 = RangeOption.parse("Range=10");
|
||||
range2 = RangeOption.parse("Range=10-500");
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
assertTrue(range1.compareTo(range2) == 0);
|
||||
|
||||
fail();
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
// ignore
|
||||
fail("IllegalStateException expected");
|
||||
} catch (IllegalStateException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
range1 = RangeOption.parse("Range=10-500");
|
||||
range2 = RangeOption.parse("Range=10");
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
assertTrue(range1.compareTo(range2) == 0);
|
||||
|
||||
fail();
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
// ignore
|
||||
fail("IllegalStateException expected");
|
||||
} catch (IllegalStateException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testNext()
|
||||
{
|
||||
public void testNext() {
|
||||
RangeOption range = RangeOption.parse("Range=0-100");
|
||||
|
||||
range = range.nextRange(100);
|
||||
|
||||
assertEquals(101, range.getInitial());
|
||||
assertEquals(200, range.getTerminal());
|
||||
|
||||
range = range.nextRange(10);
|
||||
|
||||
assertEquals(201, range.getInitial());
|
||||
assertEquals(210, range.getTerminal());
|
||||
|
||||
range = range.nextRange(RangeOption.TERMINAL_END_OF_RANGE);
|
||||
|
||||
assertEquals(211, range.getInitial());
|
||||
assertEquals(RangeOption.TERMINAL_END_OF_RANGE, range.getTerminal());
|
||||
}
|
||||
|
||||
public static Test suite()
|
||||
{
|
||||
return new TestSuite(RangeOptionTest.class);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package org.springframework.ldap.util;
|
||||
|
||||
/**
|
||||
* @author Marius Scurtescu
|
||||
*/
|
||||
public interface AttributeValueProcessor
|
||||
{
|
||||
public void process(Object value);
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package org.springframework.ldap.util;
|
||||
|
||||
import org.springframework.ldap.core.AttributesMapper;
|
||||
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.NamingEnumeration;
|
||||
|
||||
/**
|
||||
* Utility class that helps with reading all attribute values from Active Directory using <em>Incremental Retrieval of
|
||||
* Multi-valued Properties</em>.
|
||||
*
|
||||
* <p>Example usage of this attribute mapper:
|
||||
* <pre>
|
||||
* public void retrieveAttributeIncrementally(LdapTemplate ldap, LdapName entrDn, String attributeName, AttributeValueProcessor valueProcessor)
|
||||
* {
|
||||
* IncrementalAttributeMapper incrementalAttributeMapper = new IncrementalAttributeMapper(attributeName, valueProcessor);
|
||||
*
|
||||
* while (incrementalAttributeMapper.hasMore())
|
||||
* {
|
||||
* ldap.lookup(entrDn, incrementalAttributeMapper.getAttributesArray(), incrementalAttributeMapper);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Marius Scurtescu
|
||||
* @see <a href="http://www.watersprings.org/pub/id/draft-kashi-incremental-00.txt">Incremental Retrieval of Multi-valued Properties</a>
|
||||
*/
|
||||
public class IncrementalAttributeMapper implements AttributesMapper
|
||||
{
|
||||
private String _attributeName;
|
||||
private boolean _more = true;
|
||||
private int _pageSize = RangeOption.TERMINAL_END_OF_RANGE;
|
||||
private RangeOption _requestRange = new RangeOption(0, _pageSize);
|
||||
private AttributeValueProcessor _valueProcessor;
|
||||
private boolean _omitFullRange = true;
|
||||
|
||||
public IncrementalAttributeMapper(String attributeName, AttributeValueProcessor valueProcessor)
|
||||
{
|
||||
_attributeName = attributeName;
|
||||
_valueProcessor = valueProcessor;
|
||||
}
|
||||
|
||||
public IncrementalAttributeMapper(String attributeName, AttributeValueProcessor valueProcessor, int pageSize)
|
||||
{
|
||||
_attributeName = attributeName;
|
||||
_pageSize = pageSize;
|
||||
_requestRange = new RangeOption(0, pageSize);
|
||||
_valueProcessor = valueProcessor;
|
||||
}
|
||||
|
||||
public boolean isOmitFullRange()
|
||||
{
|
||||
return _omitFullRange;
|
||||
}
|
||||
|
||||
public void setOmitFullRange(boolean omitFullRange)
|
||||
{
|
||||
_omitFullRange = omitFullRange;
|
||||
}
|
||||
|
||||
public Object mapFromAttributes(Attributes attributes) throws NamingException
|
||||
{
|
||||
if (!_more)
|
||||
throw new IllegalStateException("No more attributes!");
|
||||
|
||||
_more = false;
|
||||
|
||||
NamingEnumeration<String> attributeNameEnum = attributes.getIDs();
|
||||
|
||||
while (attributeNameEnum.hasMore())
|
||||
{
|
||||
String attributeName = attributeNameEnum.next();
|
||||
|
||||
if (attributeName.equals(_attributeName))
|
||||
{
|
||||
processValues(attributes, _attributeName);
|
||||
}
|
||||
else if (attributeName.startsWith(_attributeName + ";"))
|
||||
{
|
||||
for (String option : attributeName.split(";"))
|
||||
{
|
||||
RangeOption responseRange = RangeOption.parse(option);
|
||||
|
||||
if (responseRange != null)
|
||||
{
|
||||
_more = _requestRange.compareTo(responseRange) > 0;
|
||||
|
||||
if (_more)
|
||||
{
|
||||
_requestRange = responseRange.nextRange(_pageSize);
|
||||
}
|
||||
|
||||
processValues(attributes, attributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private void processValues(Attributes attributes, String attributeName) throws NamingException
|
||||
{
|
||||
Attribute attribute = attributes.get(attributeName);
|
||||
NamingEnumeration valueEnum = attribute.getAll();
|
||||
|
||||
while (valueEnum.hasMore())
|
||||
{
|
||||
_valueProcessor.process(valueEnum.next());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasMore()
|
||||
{
|
||||
return _more;
|
||||
}
|
||||
|
||||
public String [] getAttributesArray()
|
||||
{
|
||||
StringBuilder attributeBuilder = new StringBuilder(_attributeName);
|
||||
|
||||
if (!(_omitFullRange && _requestRange.isFullRange()))
|
||||
{
|
||||
attributeBuilder.append(';');
|
||||
|
||||
_requestRange.toString(attributeBuilder);
|
||||
}
|
||||
|
||||
return new String[]{attributeBuilder.toString()};
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.springframework.ldap.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author Marius Scurtescu
|
||||
*/
|
||||
public class ListAttributeValueProcessor implements AttributeValueProcessor
|
||||
{
|
||||
private List<String> _values = new ArrayList<String>();
|
||||
|
||||
public void process(Object value)
|
||||
{
|
||||
_values.add((String) value);
|
||||
}
|
||||
|
||||
public List<String> getValues()
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
package org.springframework.ldap.util;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.BasicAttributes;
|
||||
import javax.naming.directory.BasicAttribute;
|
||||
import javax.naming.directory.Attribute;
|
||||
|
||||
/**
|
||||
* IncrementalAttributeMapper Tester.
|
||||
*
|
||||
* @author Marius Scurtescu
|
||||
*/
|
||||
public class IncrementalAttributeMapperTest extends TestCase
|
||||
{
|
||||
private IncrementalAttributeMapper _incrementalAttributeMapper;
|
||||
private ListAttributeValueProcessor _valueProcessor;
|
||||
|
||||
public IncrementalAttributeMapperTest(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
super.setUp();
|
||||
|
||||
_valueProcessor = new ListAttributeValueProcessor();
|
||||
_incrementalAttributeMapper = new IncrementalAttributeMapper("member", _valueProcessor);
|
||||
}
|
||||
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
_incrementalAttributeMapper = null;
|
||||
_valueProcessor = null;
|
||||
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testGetAttributesArray() throws Exception
|
||||
{
|
||||
String[] attributes = _incrementalAttributeMapper.getAttributesArray();
|
||||
|
||||
assertEquals(1, attributes.length);
|
||||
assertEquals("member", attributes[0]);
|
||||
|
||||
_incrementalAttributeMapper = new IncrementalAttributeMapper("member", _valueProcessor, 10);
|
||||
|
||||
attributes = _incrementalAttributeMapper.getAttributesArray();
|
||||
|
||||
assertEquals(1, attributes.length);
|
||||
assertEquals("member;Range=0-10", attributes[0]);
|
||||
}
|
||||
|
||||
public void testLoopEmpty() throws Exception
|
||||
{
|
||||
assertTrue(_incrementalAttributeMapper.hasMore());
|
||||
|
||||
Attributes attributes = new BasicAttributes();
|
||||
|
||||
_incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertFalse(_incrementalAttributeMapper.hasMore());
|
||||
assertEquals(0, _valueProcessor.getValues().size());
|
||||
}
|
||||
|
||||
public void testLoop() throws Exception
|
||||
{
|
||||
Attributes attributes = createAttributes(new RangeOption(0, 10), true);
|
||||
|
||||
_incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertTrue(_incrementalAttributeMapper.hasMore());
|
||||
assertEquals(11, _valueProcessor.getValues().size());
|
||||
|
||||
attributes = createAttributes(new RangeOption(11), 5, false);
|
||||
|
||||
_incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertFalse(_incrementalAttributeMapper.hasMore());
|
||||
assertEquals(16, _valueProcessor.getValues().size());
|
||||
}
|
||||
|
||||
public void test1LoopWithPageSizeExact() throws Exception
|
||||
{
|
||||
_incrementalAttributeMapper = new IncrementalAttributeMapper("member", _valueProcessor, 10);
|
||||
|
||||
Attributes attributes = createAttributes(new RangeOption(0, 10), true);
|
||||
|
||||
_incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertFalse(_incrementalAttributeMapper.hasMore());
|
||||
assertEquals(11, _valueProcessor.getValues().size());
|
||||
}
|
||||
|
||||
public void test2LoopsWithPageSizeExact() throws Exception
|
||||
{
|
||||
_incrementalAttributeMapper = new IncrementalAttributeMapper("member", _valueProcessor, 20);
|
||||
|
||||
Attributes attributes = createAttributes(new RangeOption(0, 10), true);
|
||||
|
||||
_incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertTrue(_incrementalAttributeMapper.hasMore());
|
||||
assertEquals(11, _valueProcessor.getValues().size());
|
||||
|
||||
attributes = createAttributes(new RangeOption(11, 30), false);
|
||||
|
||||
_incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertFalse(_incrementalAttributeMapper.hasMore());
|
||||
assertEquals(31, _valueProcessor.getValues().size());
|
||||
}
|
||||
|
||||
public void test2LoopsWithPageSize() throws Exception
|
||||
{
|
||||
_incrementalAttributeMapper = new IncrementalAttributeMapper("member", _valueProcessor, 20);
|
||||
|
||||
Attributes attributes = createAttributes(new RangeOption(0, 10), true);
|
||||
|
||||
_incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertTrue(_incrementalAttributeMapper.hasMore());
|
||||
assertEquals(11, _valueProcessor.getValues().size());
|
||||
|
||||
attributes = createAttributes(new RangeOption(11), 5, false);
|
||||
|
||||
_incrementalAttributeMapper.mapFromAttributes(attributes);
|
||||
|
||||
assertFalse(_incrementalAttributeMapper.hasMore());
|
||||
assertEquals(16, _valueProcessor.getValues().size());
|
||||
}
|
||||
|
||||
private Attributes createAttributes(RangeOption range, boolean emptyPlain)
|
||||
{
|
||||
return createAttributes(range, range.getTerminal() - range.getInitial() + 1, emptyPlain);
|
||||
}
|
||||
|
||||
private Attributes createAttributes(RangeOption range, int valueCnt, boolean emptyPlain)
|
||||
{
|
||||
Attributes attributes = new BasicAttributes();
|
||||
|
||||
if (emptyPlain)
|
||||
attributes.put(new BasicAttribute("member"));
|
||||
|
||||
Attribute attribute = new BasicAttribute("member;" + range.toString());
|
||||
for (int i = 0; i < valueCnt; i++)
|
||||
attribute.add("value" + (range.getInitial() + i - 1));
|
||||
attributes.put(attribute);
|
||||
|
||||
return attributes;
|
||||
|
||||
}
|
||||
|
||||
public static Test suite()
|
||||
{
|
||||
return new TestSuite(IncrementalAttributeMapperTest.class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user