Introduced TempEntryRenamingStrategy.

This commit is contained in:
Mattias Arthursson
2007-01-14 16:19:09 +00:00
parent c4ad4c54d6
commit d7c5ac6ccd
12 changed files with 392 additions and 79 deletions

View File

@@ -1,4 +1,28 @@
package org.springframework.ldap.support.transaction;
public interface DummyDao {
void createWithException(String country, String company, String fullname,
String lastname, String description);
void create(String country, String company, String fullname,
String lastname, String description);
void update(String dn, String lastname, String description);
void updateWithException(String dn, String lastname, String description);
void updateAndRename(String dn, String newDn, String description);
void updateAndRenameWithException(String dn, String newDn,
String description);
void modifyAttributes(String dn, String lastName, String description);
void modifyAttributesWithException(String dn, String lastName,
String description);
void unbind(String dn);
void unbindWithException(String dn);
}

View File

@@ -5,7 +5,7 @@ import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
public class DummyDaoImpl implements DummyDao {
public class DummyDaoImpl implements DummyDao{
private LdapTemplate ldapTemplate;
private JdbcTemplate jdbcTemplate;
@@ -25,6 +25,9 @@ public class DummyDaoImpl implements DummyDao {
* java.lang.String, java.lang.String, java.lang.String,
* java.lang.String)
*/
/* (non-Javadoc)
* @see org.springframework.ldap.support.transaction.TempDummyDao#createWithException(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
public void createWithException(String country, String company,
String fullname, String lastname, String description) {
create(country, company, fullname, lastname, description);
@@ -38,6 +41,9 @@ public class DummyDaoImpl implements DummyDao {
* java.lang.String, java.lang.String, java.lang.String,
* java.lang.String)
*/
/* (non-Javadoc)
* @see org.springframework.ldap.support.transaction.TempDummyDao#create(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
public void create(String country, String company, String fullname,
String lastname, String description) {
DistinguishedName dn = new DistinguishedName();
@@ -60,6 +66,9 @@ public class DummyDaoImpl implements DummyDao {
* @see org.springframework.ldap.support.transaction.DummyDao#update(java.lang.String,
* java.lang.String, java.lang.String)
*/
/* (non-Javadoc)
* @see org.springframework.ldap.support.transaction.TempDummyDao#update(java.lang.String, java.lang.String, java.lang.String)
*/
public void update(String dn, String lastname, String description) {
DirContextAdapter ctx = (DirContextAdapter) ldapTemplate.lookup(dn);
ctx.setAttributeValue("sn", lastname);
@@ -75,6 +84,9 @@ public class DummyDaoImpl implements DummyDao {
* @see org.springframework.ldap.support.transaction.DummyDao#updateWithException(java.lang.String,
* java.lang.String, java.lang.String)
*/
/* (non-Javadoc)
* @see org.springframework.ldap.support.transaction.TempDummyDao#updateWithException(java.lang.String, java.lang.String, java.lang.String)
*/
public void updateWithException(String dn, String lastname,
String description) {
update(dn, lastname, description);
@@ -87,6 +99,9 @@ public class DummyDaoImpl implements DummyDao {
* @see org.springframework.ldap.support.transaction.DummyDao#updateAndRename(java.lang.String,
* java.lang.String, java.lang.String)
*/
/* (non-Javadoc)
* @see org.springframework.ldap.support.transaction.TempDummyDao#updateAndRename(java.lang.String, java.lang.String, java.lang.String)
*/
public void updateAndRename(String dn, String newDn, String description) {
DirContextAdapter ctx = (DirContextAdapter) ldapTemplate.lookup(dn);
ctx.setAttributeValue("description", description);
@@ -103,6 +118,9 @@ public class DummyDaoImpl implements DummyDao {
* @see org.springframework.ldap.support.transaction.DummyDao#updateAndRenameWithException(java.lang.String,
* java.lang.String, java.lang.String)
*/
/* (non-Javadoc)
* @see org.springframework.ldap.support.transaction.TempDummyDao#updateAndRenameWithException(java.lang.String, java.lang.String, java.lang.String)
*/
public void updateAndRenameWithException(String dn, String newDn,
String description) {
updateAndRename(dn, newDn, description);
@@ -115,6 +133,9 @@ public class DummyDaoImpl implements DummyDao {
* @see org.springframework.ldap.support.transaction.DummyDao#modifyAttributes(java.lang.String,
* java.lang.String, java.lang.String)
*/
/* (non-Javadoc)
* @see org.springframework.ldap.support.transaction.TempDummyDao#modifyAttributes(java.lang.String, java.lang.String, java.lang.String)
*/
public void modifyAttributes(String dn, String lastName, String description) {
DirContextAdapter ctx = (DirContextAdapter) ldapTemplate.lookup(dn);
ctx.setAttributeValue("sn", lastName);
@@ -129,6 +150,9 @@ public class DummyDaoImpl implements DummyDao {
* @see org.springframework.ldap.support.transaction.DummyDao#modifyAttributesWithException(java.lang.String,
* java.lang.String, java.lang.String)
*/
/* (non-Javadoc)
* @see org.springframework.ldap.support.transaction.TempDummyDao#modifyAttributesWithException(java.lang.String, java.lang.String, java.lang.String)
*/
public void modifyAttributesWithException(String dn, String lastName,
String description) {
modifyAttributes(dn, lastName, description);
@@ -140,6 +164,9 @@ public class DummyDaoImpl implements DummyDao {
*
* @see org.springframework.ldap.support.transaction.DummyDao#unbind(java.lang.String)
*/
/* (non-Javadoc)
* @see org.springframework.ldap.support.transaction.TempDummyDao#unbind(java.lang.String)
*/
public void unbind(String dn) {
ldapTemplate.unbind(dn);
}
@@ -149,6 +176,9 @@ public class DummyDaoImpl implements DummyDao {
*
* @see org.springframework.ldap.support.transaction.DummyDao#unbindWithException(java.lang.String)
*/
/* (non-Javadoc)
* @see org.springframework.ldap.support.transaction.TempDummyDao#unbindWithException(java.lang.String)
*/
public void unbindWithException(String dn) {
unbind(dn);
throw new DummyException("This operation failed.");

View File

@@ -79,8 +79,12 @@ public class DefaultCompensatingTransactionDataManager implements
this.rollbackOperations = rollbackOperations;
}
/* (non-Javadoc)
* @see org.springframework.ldap.support.transaction.CompensatingTransactionDataManager#commit()
*/
public void commit() {
log.debug("Performing rollback");
// TODO: Should this really be done in reverse order?
while (!rollbackOperations.isEmpty()) {
CompensatingTransactionRollbackOperation rollbackOperation = (CompensatingTransactionRollbackOperation) rollbackOperations
.pop();

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2002-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.support.transaction;
import java.util.List;
import javax.naming.Name;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapRdn;
import org.springframework.ldap.core.LdapRdnComponent;
/**
* Default implementation of {@link TempEntryRenamingStrategy}. This
* implementation simply adds "_temp" to the leftmost (least siginificant part)
* of the name, e.g.:<br/>
*
* <code>
* cn=john doe, ou=company1, c=SE
* </code>
*
* becomes
*
* <code>
* cn=john doe_temp, ou=company1, c=SE
* </code>.
* <p>
* Note that using this strategy means that the entry remains in virtually the
* same location as where it originally resided. This means that searches later
* in the same transaction might return references to the temporary entry even
* though it should have been removed or rebound.
*
* @author Mattias Arthursson
*/
public class DefaultTempEntryRenamingStrategy implements
TempEntryRenamingStrategy {
/**
* The default temp entry suffix, &quot;_temp&quot;.
*/
public static final String DEFAULT_TEMP_SUFFIX = "_temp";
private String tempSuffix = DEFAULT_TEMP_SUFFIX;
/*
* (non-Javadoc)
*
* @see org.springframework.ldap.support.transaction.TempEntryRenamingStrategy#getTemporaryName(javax.naming.Name)
*/
public Name getTemporaryName(Name originalName) {
DistinguishedName temporaryName = new DistinguishedName(originalName);
List names = temporaryName.getNames();
LdapRdn rdn = (LdapRdn) names.get(names.size() - 1);
LdapRdnComponent rdnComponent = rdn.getComponent();
String value = rdnComponent.getValue();
rdnComponent.setValue(value + DEFAULT_TEMP_SUFFIX);
return temporaryName;
}
/**
* Get the suffix that will be used for renaming temporary entries.
*
* @return the suffix.
*/
public String getTempSuffix() {
return tempSuffix;
}
/**
* Set the suffix to use for renaming temporary entries. Default value is
* {@link #DEFAULT_TEMP_SUFFIX}.
*
* @param tempSuffix
* the suffix.
*/
public void setTempSuffix(String tempSuffix) {
this.tempSuffix = tempSuffix;
}
}

View File

@@ -0,0 +1,75 @@
package org.springframework.ldap.support.transaction;
import java.util.List;
import javax.naming.Name;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapRdn;
import org.springframework.ldap.core.LdapRdnComponent;
/**
* A {@link TempEntryRenamingStrategy} that moves the entry to a different
* subtree than the original entry. The specified subtree needs to be present in
* the LDAP tree; it will not be created and operations using this strategy will
* fail if it is not in place. However, this strategy is preferrable to
* {@link DefaultTempEntryRenamingStrategy}, as it makes it possible to have
* searches have the expected result even though the temporary entry still exits
* until the end of the transaction.
* <p>
* Example: If the specified <code>subtreeNode</code> is
* <code>ou=tempEntries</code> and the <code>originalName</code> is
* <code>cn=john doe, ou=company1, c=SE</code>, the result of
* {@link #getTemporaryName(Name)} will be
* <code>cn=john doe1, ou=tempEntries</code>. The &quot;1&quot; suffix is a
* sequence number needed to prevent potential collisions in the temporary
* storage.
*
* @author Mattias Arthursson
*
*/
public class DifferentSubtreeTempEntryRenamingStrategy implements
TempEntryRenamingStrategy {
private Name subtreeNode;
private static int nextSequenceNo = 1;
public DifferentSubtreeTempEntryRenamingStrategy(Name subtreeNode) {
this.subtreeNode = subtreeNode;
}
public Name getSubtreeNode() {
return subtreeNode;
}
public void setSubtreeNode(Name subtreeNode) {
this.subtreeNode = subtreeNode;
}
int getNextSequenceNo() {
return nextSequenceNo;
}
/*
* (non-Javadoc)
*
* @see org.springframework.ldap.support.transaction.TempEntryRenamingStrategy#getTemporaryName(javax.naming.Name)
*/
public Name getTemporaryName(Name originalName) {
DistinguishedName tempName = new DistinguishedName(originalName);
List names = tempName.getNames();
LdapRdn rdn = (LdapRdn) names.get(names.size() - 1);
LdapRdnComponent component = rdn.getComponent();
LdapRdn newRdn;
synchronized (this) {
newRdn = new LdapRdn(component.getKey(), component.getValue()
+ nextSequenceNo++);
}
DistinguishedName newName = new DistinguishedName(subtreeNode);
newName.add(newRdn);
return newName;
}
}

View File

@@ -15,17 +15,10 @@
*/
package org.springframework.ldap.support.transaction;
import java.util.List;
import javax.naming.Name;
import javax.naming.directory.Attributes;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.core.LdapRdn;
import org.springframework.ldap.core.LdapRdnComponent;
/**
* A {@link CompensatingTransactionRecordingOperation} keeping track of a rebind
@@ -37,10 +30,10 @@ import org.springframework.ldap.core.LdapRdnComponent;
public class RebindRecordingOperation implements
CompensatingTransactionRecordingOperation {
private static Log log = LogFactory.getLog(RebindRecordingOperation.class);
private LdapOperations ldapOperations;
private TempEntryRenamingStrategy renamingStrategy;
/**
* Constructor.
*
@@ -73,24 +66,13 @@ public class RebindRecordingOperation implements
attributes = (Attributes) args[2];
}
Name temporaryName = getTemporaryName(dn);
Name temporaryName = renamingStrategy.getTemporaryName(dn);
ldapOperations.rename(dn, temporaryName);
return new RebindRollbackOperation(ldapOperations, dn, temporaryName,
object, attributes);
}
Name getTemporaryName(Name originalName) {
DistinguishedName temporaryName = new DistinguishedName(originalName);
List names = temporaryName.getNames();
LdapRdn rdn = (LdapRdn) names.get(names.size() - 1);
LdapRdnComponent rdnComponent = rdn.getComponent();
String value = rdnComponent.getValue();
rdnComponent.setValue(value + "_temp");
return temporaryName;
}
/**
* Get the LdapOperations. For testing purposes.
*
@@ -99,4 +81,12 @@ public class RebindRecordingOperation implements
LdapOperations getLdapOperations() {
return ldapOperations;
}
public TempEntryRenamingStrategy getRenamingStrategy() {
return renamingStrategy;
}
public void setRenamingStrategy(TempEntryRenamingStrategy renamingStrategy) {
this.renamingStrategy = renamingStrategy;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2002-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.support.transaction;
import javax.naming.Name;
/**
* Interface for different strategies to rename temporary entries for unbind and
* rebind operations.
*
* @author Mattias Arthursson
*/
public interface TempEntryRenamingStrategy {
/**
* Get a temporary name for the current entry to be renamed to.
*
* @param originalName
* The original name of the entry.
* @return The name to which the entry should be temporarily renamed
* according to this strategy.
*/
public Name getTemporaryName(Name originalName);
}

View File

@@ -15,14 +15,9 @@
*/
package org.springframework.ldap.support.transaction;
import java.util.List;
import javax.naming.Name;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.core.LdapRdn;
import org.springframework.ldap.core.LdapRdnComponent;
/**
* {@link CompensatingTransactionRecordingOperation} to keep track of unbind
@@ -36,6 +31,8 @@ public class UnbindRecordingOperation implements
private LdapOperations ldapOperations;
private TempEntryRenamingStrategy renamingStrategy = new DefaultTempEntryRenamingStrategy();
/**
* Constructor.
*
@@ -56,25 +53,23 @@ public class UnbindRecordingOperation implements
public CompensatingTransactionRollbackOperation recordOperation(
Object[] args) {
Name dn = LdapUtils.getFirstArgumentAsName(args);
Name temporaryDn = getTemporaryName(dn);
Name temporaryDn = renamingStrategy.getTemporaryName(dn);
ldapOperations.rename(dn, temporaryDn);
return new BindRollbackOperation(ldapOperations, dn, temporaryDn);
}
Name getTemporaryName(Name originalName) {
DistinguishedName temporaryName = new DistinguishedName(originalName);
List names = temporaryName.getNames();
LdapRdn rdn = (LdapRdn) names.get(names.size() - 1);
LdapRdnComponent rdnComponent = rdn.getComponent();
String value = rdnComponent.getValue();
rdnComponent.setValue(value + "_temp");
return temporaryName;
}
LdapOperations getLdapOperations() {
return ldapOperations;
}
public TempEntryRenamingStrategy getRenamingStrategy() {
return renamingStrategy;
}
public void setRenamingStrategy(TempEntryRenamingStrategy renamingStrategy) {
this.renamingStrategy = renamingStrategy;
}
}

View File

@@ -0,0 +1,33 @@
package org.springframework.ldap.support.transaction;
import javax.naming.Name;
import org.springframework.ldap.core.DistinguishedName;
import junit.framework.TestCase;
public class DefaultTempEntryRenamingStrategyTest extends TestCase {
public void testGetTemporaryName() {
DistinguishedName expectedOriginalName = new DistinguishedName(
"cn=john doe, ou=somecompany, c=SE");
DefaultTempEntryRenamingStrategy tested = new DefaultTempEntryRenamingStrategy();
Name result = tested.getTemporaryName(expectedOriginalName);
assertEquals("cn=john doe_temp, ou=somecompany, c=SE", result
.toString());
assertNotSame(expectedOriginalName, result);
}
public void testGetTemporaryDN_MultivalueDN() {
DistinguishedName expectedOriginalName = new DistinguishedName(
"cn=john doe+sn=doe, ou=somecompany, c=SE");
DefaultTempEntryRenamingStrategy tested = new DefaultTempEntryRenamingStrategy();
Name result = tested.getTemporaryName(expectedOriginalName);
assertEquals("cn=john doe_temp+sn=doe, ou=somecompany, c=SE", result
.toString());
}
}

View File

@@ -0,0 +1,27 @@
package org.springframework.ldap.support.transaction;
import javax.naming.Name;
import org.springframework.ldap.core.DistinguishedName;
import junit.framework.TestCase;
public class DifferentSubtreeTempEntryRenamingStrategyTest extends TestCase {
public void testGetTemporaryName() {
DistinguishedName originalName = new DistinguishedName(
"cn=john doe, ou=somecompany, c=SE");
DifferentSubtreeTempEntryRenamingStrategy tested = new DifferentSubtreeTempEntryRenamingStrategy(
new DistinguishedName("ou=tempEntries"));
int nextSequenceNo = tested.getNextSequenceNo();
// Perform test
Name result = tested.getTemporaryName(originalName);
// Verify result
assertEquals("cn=john doe" + nextSequenceNo + ", ou=tempEntries",
result.toString());
}
}

View File

@@ -1,6 +1,5 @@
package org.springframework.ldap.support.transaction;
import javax.naming.Name;
import javax.naming.directory.BasicAttributes;
import junit.framework.TestCase;
@@ -14,22 +13,38 @@ public class RebindRecordingOperationTest extends TestCase {
private LdapOperations ldapOperationsMock;
private MockControl renamingStrategyControl;
private TempEntryRenamingStrategy renamingStrategyMock;
protected void setUp() throws Exception {
ldapOperationsControl = MockControl.createControl(LdapOperations.class);
ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock();
renamingStrategyControl = MockControl
.createControl(TempEntryRenamingStrategy.class);
renamingStrategyMock = (TempEntryRenamingStrategy) renamingStrategyControl
.getMock();
}
protected void tearDown() throws Exception {
ldapOperationsControl = null;
ldapOperationsMock = null;
renamingStrategyControl = null;
renamingStrategyMock = null;
}
protected void replay() {
ldapOperationsControl.replay();
renamingStrategyControl.replay();
}
protected void verify() {
ldapOperationsControl.verify();
renamingStrategyControl.verify();
}
public void testRecordOperation() {
@@ -38,18 +53,18 @@ public class RebindRecordingOperationTest extends TestCase {
final DistinguishedName expectedTempDn = new DistinguishedName(
"cn=john doe");
RebindRecordingOperation tested = new RebindRecordingOperation(
ldapOperationsMock) {
Name getTemporaryName(Name originalName) {
assertSame(expectedDn, originalName);
return expectedTempDn;
}
};
ldapOperationsMock);
tested.setRenamingStrategy(renamingStrategyMock);
renamingStrategyControl.expectAndReturn(renamingStrategyMock
.getTemporaryName(expectedDn), expectedTempDn);
ldapOperationsMock.rename(expectedDn, expectedTempDn);
replay();
Object expectedObject = new Object();
BasicAttributes expectedAttributes = new BasicAttributes();
// perform test
CompensatingTransactionRollbackOperation result = tested
.recordOperation(new Object[] { expectedDn, expectedObject,

View File

@@ -1,58 +1,48 @@
package org.springframework.ldap.support.transaction;
import javax.naming.Name;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapOperations;
import junit.framework.TestCase;
public class UnbindRecordingOperationTest extends TestCase {
private MockControl ldapOperationsControl;
private LdapOperations ldapOperationsMock;
private MockControl renamingStrategyControl;
private TempEntryRenamingStrategy renamingStrategyMock;
protected void setUp() throws Exception {
ldapOperationsControl = MockControl.createControl(LdapOperations.class);
ldapOperationsMock = (LdapOperations) ldapOperationsControl.getMock();
renamingStrategyControl = MockControl
.createControl(TempEntryRenamingStrategy.class);
renamingStrategyMock = (TempEntryRenamingStrategy) renamingStrategyControl
.getMock();
}
protected void tearDown() throws Exception {
ldapOperationsControl = null;
ldapOperationsMock = null;
renamingStrategyControl = null;
renamingStrategyMock = null;
}
protected void replay() {
ldapOperationsControl.replay();
renamingStrategyControl.replay();
}
protected void verify() {
ldapOperationsControl.verify();
}
public void testGetTemporaryDN() {
DistinguishedName expectedOriginalName = new DistinguishedName(
"cn=john doe, ou=somecompany, c=SE");
UnbindRecordingOperation tested = new UnbindRecordingOperation(
ldapOperationsMock);
Name result = tested.getTemporaryName(expectedOriginalName);
assertEquals("cn=john doe_temp, ou=somecompany, c=SE", result
.toString());
assertNotSame(expectedOriginalName, result);
}
public void testGetTemporaryDN_MultivalueDN() {
DistinguishedName expectedOriginalName = new DistinguishedName(
"cn=john doe+sn=doe, ou=somecompany, c=SE");
UnbindRecordingOperation tested = new UnbindRecordingOperation(
ldapOperationsMock);
Name result = tested.getTemporaryName(expectedOriginalName);
assertEquals("cn=john doe_temp+sn=doe, ou=somecompany, c=SE", result
.toString());
renamingStrategyControl.verify();
}
public void testRecordOperation() {
@@ -61,12 +51,11 @@ public class UnbindRecordingOperationTest extends TestCase {
final DistinguishedName expectedDn = new DistinguishedName(
"cn=john doe");
UnbindRecordingOperation tested = new UnbindRecordingOperation(
ldapOperationsMock) {
Name getTemporaryName(Name originalName) {
assertSame(expectedDn, originalName);
return expectedTempName;
}
};
ldapOperationsMock);
tested.setRenamingStrategy(renamingStrategyMock);
renamingStrategyControl.expectAndReturn(renamingStrategyMock
.getTemporaryName(expectedDn), expectedTempName);
ldapOperationsMock.rename(expectedDn, expectedTempName);