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:
Mattias Hellborg Arthursson
2013-07-30 12:46:26 +02:00
parent 895af4a44f
commit 79a922c778
11 changed files with 486 additions and 483 deletions

View File

@@ -1,9 +0,0 @@
package org.springframework.ldap.util;
/**
* @author Marius Scurtescu
*/
public interface AttributeValueProcessor
{
public void process(Object value);
}

View File

@@ -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()};
}
}

View File

@@ -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;
}
}

View File

@@ -1,155 +0,0 @@
package org.springframework.ldap.util;
import java.util.regex.Pattern;
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>
{
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 static Pattern RANGE_PATTERN = Pattern.compile("^Range=([0-9]+)(-([0-9]+|\\*))?$", Pattern.CASE_INSENSITIVE);
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))
throw new IllegalArgumentException("Illegal range-terminal: " + terminal);
if (initial < 0)
throw new IllegalArgumentException("Illegal range-initial: " + initial);
if (terminal >= 0 && terminal < initial)
throw new IllegalArgumentException("range-terminal cannot be smaller than range-initial: " + initial + "-" + terminal);
_initial = initial;
_terminal = terminal;
}
public boolean isTerminalEndOfRange()
{
return _terminal == TERMINAL_END_OF_RANGE;
}
public boolean isTerminalMissing()
{
return _terminal == TERMINAL_MISSING;
}
public int getInitial()
{
return _initial;
}
public int getTerminal()
{
return _terminal;
}
public boolean isFullRange()
{
return getInitial() == 0 && getTerminal() == TERMINAL_END_OF_RANGE;
}
public String toString()
{
StringBuilder rangeBuilder = new StringBuilder();
toString(rangeBuilder);
return rangeBuilder.toString();
}
public void toString(StringBuilder rangeBuilder)
{
rangeBuilder.append("Range=").append(_initial);
if (!isTerminalMissing())
{
rangeBuilder.append('-');
if (isTerminalEndOfRange())
rangeBuilder.append('*');
else
rangeBuilder.append(_terminal);
}
}
public static RangeOption parse(String option)
{
Matcher rangeMatcher = RANGE_PATTERN.matcher(option);
rangeMatcher.find();
if (!rangeMatcher.matches())
return null;
String initialStr = rangeMatcher.group(1);
int initial = Integer.parseInt(initialStr);
int terminal = TERMINAL_MISSING;
if (rangeMatcher.group(2) != null)
{
String terminalStr = rangeMatcher.group(3);
if ("*".equals(terminalStr))
terminal = TERMINAL_END_OF_RANGE;
else
terminal = Integer.parseInt(terminalStr);
}
return new RangeOption(initial, terminal);
}
public int compareTo(RangeOption that)
{
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())
return 0;
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)
throw new IllegalStateException("Don't know how to deal with missing range-terminal: " + this.toString());
if (this.getTerminal() == TERMINAL_END_OF_RANGE)
return 1;
if (that.getTerminal() == TERMINAL_END_OF_RANGE)
return -1;
return this.getTerminal() > that.getTerminal() ? 1 : -1;
}
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)
throw new IllegalArgumentException("Invalid page size: " + pageSize);
int initial = getTerminal() + 1;
int terminal = pageSize == TERMINAL_END_OF_RANGE ? TERMINAL_END_OF_RANGE : getTerminal() + pageSize;
return new RangeOption(initial, terminal);
}
}

View File

@@ -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);
}
}

View File

@@ -1,232 +0,0 @@
package org.springframework.ldap.util;
import junit.framework.Test;
import junit.framework.TestSuite;
import junit.framework.TestCase;
/**
* IncrementalAttributeMapper Tester.
*
* @author Marius Scurtescu
*/
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
{
new RangeOption(101, 100);
fail();
}
catch (IllegalArgumentException e)
{
// ignore
}
try
{
new RangeOption(-1, 100);
fail();
}
catch (IllegalArgumentException e)
{
// ignore
}
try
{
new RangeOption(-10, 100);
fail();
}
catch (IllegalArgumentException e)
{
// ignore
}
try
{
new RangeOption(0, -3);
fail();
}
catch (IllegalArgumentException e)
{
// ignore
}
}
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
{
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()
{
assertNull(RangeOption.parse("Range=10-"));
assertNull(RangeOption.parse("Range=10-a"));
assertNull(RangeOption.parse("lang-en"));
assertNull(RangeOption.parse("member;Range=10-100"));
assertNull(RangeOption.parse(";Range=10-100"));
assertNull(RangeOption.parse("Range=10-100;"));
assertNull(RangeOption.parse("Range=10-100;lang-de"));
}
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()
{
RangeOption range1 = RangeOption.parse("Range=10-500");
RangeOption range2 = RangeOption.parse("Range=11-500");
try
{
assertTrue(range1.compareTo(range2) == 0);
fail();
}
catch (IllegalStateException e)
{
// ignore
}
range1 = RangeOption.parse("Range=10");
range2 = RangeOption.parse("Range=10-500");
try
{
assertTrue(range1.compareTo(range2) == 0);
fail();
}
catch (IllegalStateException e)
{
// ignore
}
range1 = RangeOption.parse("Range=10-500");
range2 = RangeOption.parse("Range=10");
try
{
assertTrue(range1.compareTo(range2) == 0);
fail();
}
catch (IllegalStateException e)
{
// ignore
}
}
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);
}
}