| | |
| | | import java.util.Set; |
| | | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | |
| | |
| | | /** |
| | | * Create a new object from a provided attribute type. Historical is empty. |
| | | * |
| | | * @param type the provided attribute type. |
| | | * @param attrType the provided attribute type. |
| | | * @return a new AttributeInfo object. |
| | | */ |
| | | public static AttrHistorical createAttributeHistorical(AttributeType type) |
| | | public static AttrHistorical createAttributeHistorical(AttributeType attrType) |
| | | { |
| | | return type.isSingleValue() ? new AttrHistoricalSingle() : new AttrHistoricalMultiple(); |
| | | return attrType.isSingleValue() ? new AttrHistoricalSingle(attrType) : new AttrHistoricalMultiple(); |
| | | } |
| | | |
| | | /** |
| | |
| | | * Assign the provided information to this object. |
| | | * |
| | | * @param histKey the key to assign. |
| | | * @param attrType the associated attribute type. |
| | | * @param value the associated value or null if there is no value; |
| | | * @param csn the associated CSN. |
| | | */ |
| | | public abstract void assign(HistAttrModificationKey histKey, ByteString value, CSN csn); |
| | | public abstract void assign(HistAttrModificationKey histKey, AttributeType attrType, ByteString value, CSN csn); |
| | | } |
| | |
| | | */ |
| | | void delete(Attribute attr, CSN csn) |
| | | { |
| | | AttributeType attrType = attr.getAttributeDescription().getAttributeType(); |
| | | for (ByteString val : attr) |
| | | { |
| | | delete(val, csn); |
| | | delete(val, attrType, csn); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Update the historical of this attribute after a delete value. |
| | | * |
| | | * @param val value that was deleted |
| | | * @param csn time when the delete was done |
| | | */ |
| | | void delete(ByteString val, CSN csn) |
| | | * Update the historical of this attribute after a delete value. |
| | | * |
| | | * @param val |
| | | * value that was deleted |
| | | * @param attrType |
| | | * @param csn |
| | | * time when the delete was done |
| | | */ |
| | | void delete(ByteString val, AttributeType attrType, CSN csn) |
| | | { |
| | | update(csn, new AttrValueHistorical(val, null, csn)); |
| | | update(csn, new AttrValueHistorical(val, attrType, null, csn)); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | private void add(Attribute attr, CSN csn) |
| | | { |
| | | AttributeType attrType = attr.getAttributeDescription().getAttributeType(); |
| | | for (ByteString val : attr) |
| | | { |
| | | add(val, csn); |
| | | add(val, attrType, csn); |
| | | } |
| | | } |
| | | |
| | |
| | | * |
| | | * @param addedValue |
| | | * values that was added |
| | | * @param attrType |
| | | * @param csn |
| | | * time when the value was added |
| | | */ |
| | | void add(ByteString addedValue, CSN csn) |
| | | void add(ByteString addedValue, AttributeType attrType, CSN csn) |
| | | { |
| | | update(csn, new AttrValueHistorical(addedValue, csn, null)); |
| | | update(csn, new AttrValueHistorical(addedValue, attrType, csn, null)); |
| | | } |
| | | |
| | | private void update(CSN csn, AttrValueHistorical valInfo) |
| | |
| | | // we are processing DELETE of some attribute values |
| | | AttributeBuilder builder = new AttributeBuilder(modAttr); |
| | | |
| | | AttributeType attrType = modAttr.getAttributeDescription().getAttributeType(); |
| | | for (ByteString val : modAttr) |
| | | { |
| | | boolean deleteIt = true; // true if the delete must be done |
| | | boolean addedInCurrentOp = false; |
| | | |
| | | // update historical information |
| | | AttrValueHistorical valInfo = new AttrValueHistorical(val, null, csn); |
| | | AttrValueHistorical valInfo = new AttrValueHistorical(val, attrType, null, csn); |
| | | AttrValueHistorical oldValInfo = valuesHist.get(valInfo); |
| | | if (oldValInfo != null) |
| | | { |
| | |
| | | return false; |
| | | } |
| | | |
| | | AttributeBuilder builder = new AttributeBuilder(m.getAttribute()); |
| | | for (ByteString addVal : m.getAttribute()) |
| | | Attribute attribute = m.getAttribute(); |
| | | AttributeBuilder builder = new AttributeBuilder(attribute); |
| | | AttributeType attrType = attribute.getAttributeDescription().getAttributeType(); |
| | | for (ByteString addVal : attribute) |
| | | { |
| | | AttrValueHistorical valInfo = new AttrValueHistorical(addVal, csn, null); |
| | | AttrValueHistorical valInfo = new AttrValueHistorical(addVal, attrType, csn, null); |
| | | AttrValueHistorical oldValInfo = valuesHist.get(valInfo); |
| | | if (oldValInfo == null) |
| | | { |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void assign(HistAttrModificationKey histKey, ByteString value, CSN csn) |
| | | public void assign(HistAttrModificationKey histKey, AttributeType attrType, ByteString value, CSN csn) |
| | | { |
| | | switch (histKey) |
| | | { |
| | | case ADD: |
| | | if (value != null) |
| | | { |
| | | add(value, csn); |
| | | add(value, attrType, csn); |
| | | } |
| | | break; |
| | | |
| | | case DEL: |
| | | if (value != null) |
| | | { |
| | | delete(value, csn); |
| | | delete(value, attrType, csn); |
| | | } |
| | | break; |
| | | |
| | |
| | | delete(csn); |
| | | if (value != null) |
| | | { |
| | | add(value, csn); |
| | | add(value, attrType, csn); |
| | | } |
| | | break; |
| | | |
| | |
| | | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.Attribute; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | |
| | |
| | | */ |
| | | public class AttrHistoricalSingle extends AttrHistorical |
| | | { |
| | | /** Last time when the attribute was deleted. */ |
| | | private CSN deleteTime; |
| | | /** Last time when a value was added. */ |
| | | private CSN addTime; |
| | | /** Last added value. */ |
| | | private ByteString value; |
| | | /** Attribute type for this historical value */ |
| | | private AttributeType attributeType; |
| | | /** Last time when a value was added. */ |
| | | private CSN addTime; |
| | | /** Last time when the attribute was deleted. */ |
| | | private CSN deleteTime; |
| | | /** |
| | | * Last operation applied. This is only used for multiple mods on the same |
| | | * single valued attribute in the same modification. |
| | | */ |
| | | private HistAttrModificationKey lastMod; |
| | | |
| | | /** |
| | | * Builds an {@link AttrHistoricalSingle} object. |
| | | * |
| | | * @param attributeType |
| | | * the attribute type for this historical value |
| | | */ |
| | | public AttrHistoricalSingle(AttributeType attributeType) |
| | | { |
| | | this.attributeType = attributeType; |
| | | } |
| | | |
| | | @Override |
| | | public CSN getDeleteTime() |
| | | { |
| | |
| | | { |
| | | if (addTime != null) |
| | | { |
| | | return Collections.singleton(new AttrValueHistorical(value, addTime, null)); |
| | | return Collections.singleton(new AttrValueHistorical(value, attributeType, addTime, null)); |
| | | } |
| | | return Collections.emptySet(); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void assign(HistAttrModificationKey histKey, ByteString value, CSN csn) |
| | | public void assign(HistAttrModificationKey histKey, AttributeType attrType, ByteString value, CSN csn) |
| | | { |
| | | switch (histKey) |
| | | { |
| | |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013-2015 ForgeRock AS. |
| | | * Portions Copyright 2013-2016 ForgeRock AS. |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import static org.forgerock.util.Reject.*; |
| | | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.opends.server.replication.common.CSN; |
| | | |
| | | /** AttrValueHistorical is the historical information of the modification of one attribute value. */ |
| | | public class AttrValueHistorical |
| | | { |
| | | private AttributeType attributeType; |
| | | private ByteString value; |
| | | private ByteString normalizedValue; |
| | | private CSN valueDeleteTime; |
| | | private CSN valueUpdateTime; |
| | | |
| | | /** |
| | | * Build an AttrValueHistorical for a provided attribute value, providing |
| | | * the last time the provided value is either updated or deleted. |
| | | * @param value the provided attributeValue |
| | | * @param value the provided attribute value |
| | | * @param attributeType the provided attribute type |
| | | * @param csnUpdate last time when this value was updated |
| | | * @param csnDelete last time when this value for deleted |
| | | */ |
| | | public AttrValueHistorical(ByteString value, CSN csnUpdate, CSN csnDelete) |
| | | public AttrValueHistorical(ByteString value, AttributeType attributeType, CSN csnUpdate, CSN csnDelete) |
| | | { |
| | | this.value = value; |
| | | this.attributeType = checkNotNull(attributeType); |
| | | this.valueUpdateTime = csnUpdate; |
| | | this.valueDeleteTime = csnDelete; |
| | | } |
| | |
| | | if (obj instanceof AttrValueHistorical) |
| | | { |
| | | AttrValueHistorical objVal = (AttrValueHistorical) obj; |
| | | return value.equals(objVal.getAttributeValue()); |
| | | try |
| | | { |
| | | return getNormalizedValue().equals(objVal.getNormalizedValue()); |
| | | } |
| | | catch (DecodeException e) |
| | | { |
| | | return value.equals(objVal.getAttributeValue()); |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | |
| | | @Override |
| | | public int hashCode() |
| | | { |
| | | return value.hashCode(); |
| | | try |
| | | { |
| | | return getNormalizedValue().hashCode(); |
| | | } |
| | | catch (DecodeException e) |
| | | { |
| | | return value.hashCode(); |
| | | } |
| | | } |
| | | |
| | | private ByteString getNormalizedValue() throws DecodeException |
| | | { |
| | | if (normalizedValue == null) |
| | | { |
| | | normalizedValue = attributeType.getEqualityMatchingRule().normalizeAttributeValue(value); |
| | | } |
| | | return normalizedValue; |
| | | } |
| | | |
| | | /** |
| | |
| | | attrInfo = AttrHistorical.createAttributeHistorical(attrDesc.getAttributeType()); |
| | | newHistorical.attributesHistorical.put(attrDesc, attrInfo); |
| | | } |
| | | attrInfo.assign(histVal.getHistKey(), histVal.getAttributeValue(), csn); |
| | | attrInfo.assign(histVal.getHistKey(), attrDesc.getAttributeType(), histVal.getAttributeValue(), csn); |
| | | } |
| | | } |
| | | } |
| | |
| | | import java.util.Collections; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessageBuilder; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.opendj.ldap.schema.CoreSchema; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | |
| | | |
| | | /** Create a AttrInfo and check the methods. */ |
| | | @Test(dataProvider = "attrInfo") |
| | | public void attrInfo(ByteString att, CSN deleteTime, CSN updateTime) throws Exception |
| | | public void attrInfo(ByteString attrValue, CSN deleteTime, CSN updateTime) throws Exception |
| | | { |
| | | AttributeType attrType = CoreSchema.getDescriptionAttributeType(); |
| | | // Create an empty AttrInfo |
| | | AttrHistoricalMultiple attrInfo1 = new AttrHistoricalMultiple(); |
| | | |
| | | // Check |
| | | attrInfo1.add(att, updateTime); |
| | | Set<AttrValueHistorical> values1 = attrInfo1.getValuesHistorical(); |
| | | assertEquals(values1.size(), 1); |
| | | AttrValueHistorical valueInfo1 = new AttrValueHistorical(att, updateTime, null); |
| | | assertTrue(values1.contains(valueInfo1)); |
| | | attrInfo1.add(attrValue, attrType, updateTime); |
| | | assertThat(attrInfo1.getValuesHistorical()) |
| | | .containsOnly(new AttrValueHistorical(attrValue, attrType, updateTime, null)); |
| | | |
| | | // Check constructor with parameter |
| | | AttrValueHistorical valueInfo2 = new AttrValueHistorical(att, updateTime, deleteTime); |
| | | AttrValueHistorical valueInfo2 = new AttrValueHistorical(attrValue, attrType, updateTime, deleteTime); |
| | | AttrHistoricalMultiple attrInfo2 = new AttrHistoricalMultiple( |
| | | deleteTime, updateTime, Collections.singletonMap(valueInfo2, valueInfo2)); |
| | | deleteTime, updateTime, Collections.singleton(valueInfo2)); |
| | | |
| | | // Check equality |
| | | //assertTrue(attrInfo1.getDeleteTime().compareTo(attrInfo2.getDeleteTime())==0); |
| | | |
| | | // Check constructor with time parameter and not Value |
| | | AttrHistoricalMultiple attrInfo3 = new AttrHistoricalMultiple(deleteTime, updateTime, null); |
| | | attrInfo3.add(att, updateTime); |
| | | Set<AttrValueHistorical> values3 = attrInfo3.getValuesHistorical(); |
| | | assertEquals(values3.size(), 1); |
| | | valueInfo1 = new AttrValueHistorical(att, updateTime, null); |
| | | assertTrue(values3.contains(valueInfo1)); |
| | | attrInfo3.add(attrValue, attrType, updateTime); |
| | | assertThat(attrInfo3.getValuesHistorical()) |
| | | .containsOnly(new AttrValueHistorical(attrValue, attrType, updateTime, null)); |
| | | |
| | | // Check duplicate |
| | | AttrHistoricalMultiple attrInfo4 = attrInfo3.duplicate(); |
| | | Set<AttrValueHistorical> values4 = attrInfo4.getValuesHistorical(); |
| | | AttrHistoricalMultiple attrInfo4 = duplicate(attrInfo3); |
| | | assertEquals(attrInfo4.getDeleteTime().compareTo(attrInfo3.getDeleteTime()), 0); |
| | | assertEquals(values4.size(), values3.size()); |
| | | assertThat(attrInfo4.getValuesHistorical()).isEqualTo(attrInfo3.getValuesHistorical()); |
| | | |
| | | // Check |
| | | attrInfo4.delete(att, updateTime); |
| | | attrInfo4.delete(attrValue, attrType, updateTime); |
| | | assertEquals(attrInfo4.getValuesHistorical().size(), 1); |
| | | |
| | | // Check |
| | | AttributeType type = DirectoryServer.getSchema().getAttributeType(ATTRIBUTE_NAME); |
| | | attrInfo3.delete(Attributes.create(type, att), updateTime) ; |
| | | attrInfo3.delete(Attributes.create(type, attrValue), updateTime); |
| | | assertEquals(attrInfo3.getValuesHistorical().size(), 1); |
| | | |
| | | // Check |
| | | attrInfo2.delete(updateTime); |
| | | assertEquals(attrInfo2.getValuesHistorical().size(), 0); |
| | | assertThat(attrInfo2.getValuesHistorical()).isEmpty(); |
| | | } |
| | | |
| | | private AttrHistoricalMultiple duplicate(AttrHistoricalMultiple attrMul) |
| | | { |
| | | return new AttrHistoricalMultiple( |
| | | attrMul.getDeleteTime(), attrMul.getLastUpdateTime(), attrMul.getValuesHistorical()); |
| | | } |
| | | |
| | | @Test |
| | |
| | | assertAttributeValues(entry, "X"); |
| | | } |
| | | |
| | | /** Use case: user changes the case of a family name for example. */ |
| | | @Test |
| | | public void replay_addThenDeleteThenAdd_differentCaseWithCaseIgnoreAttributeType() throws Exception |
| | | { |
| | | CSN[] t = newCSNs(3); |
| | | |
| | | mod = newModification(ADD, "X"); |
| | | mod = newModification(ADD, "x"); |
| | | replayOperation(t[0], entry, mod, SUCCESS); |
| | | assertAttributeValues(entry, "X"); |
| | | assertAttributeValues(entry, "x"); |
| | | |
| | | mod = newModification(DELETE, "x"); |
| | | mod = newModification(DELETE, "X"); |
| | | replayOperation(t[1], entry, mod, SUCCESS); |
| | | assertNoAttributeValue(entry); |
| | | |
| | | mod = newModification(ADD, "X"); |
| | | replayOperationSuppressMod(t[2], entry, mod, CONFLICT); |
| | | assertAttributeValues(entry); |
| | | replayOperation(t[2], entry, mod, SUCCESS); |
| | | assertAttributeValues(entry, "X"); |
| | | } |
| | | |
| | | @Test |
| | |
| | | import org.forgerock.i18n.LocalizableMessageBuilder; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.CSN; |
| | |
| | | @BeforeMethod |
| | | public void localSetUp() throws Exception |
| | | { |
| | | attrHist = new AttrHistoricalSingle(); |
| | | AttributeType attrType = Schema.getDefaultSchema().getAttributeType(ATTRIBUTE_NAME); |
| | | attrHist = new AttrHistoricalSingle(attrType); |
| | | csn = csnGen.newCSN(); |
| | | entry = TestCaseUtils.makeEntry( |
| | | "dn: uid=test.user", |
| | |
| | | @Test |
| | | public void replay_deleteDubious() throws Exception |
| | | { |
| | | HistoricalAttributeValue histAttrVal = new HistoricalAttributeValue("display:" + csn + ":add:X"); |
| | | attrHist.assign(histAttrVal.getHistKey(), histAttrVal.getAttributeValue(), csn); |
| | | AttributeType attrType = Schema.getDefaultSchema().getAttributeType(ATTRIBUTE_NAME); |
| | | HistoricalAttributeValue histAttrVal = new HistoricalAttributeValue(ATTRIBUTE_NAME + ":" + csn + ":add:X"); |
| | | attrHist.assign(histAttrVal.getHistKey(), attrType, histAttrVal.getAttributeValue(), csn); |
| | | mod = newModification(ADD, "X"); |
| | | entry.applyModification(mod); |
| | | assertAttributeValue(entry, "X"); |
| File was renamed from opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/ValueInfoTest.java |
| | |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013-2015 ForgeRock AS. |
| | | * Portions Copyright 2013-2016 ForgeRock AS. |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import static org.assertj.core.api.Assertions.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.opendj.ldap.schema.CoreSchema; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.opends.server.util.TimeThread; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test of ValueInfo. |
| | | */ |
| | | /** Test of AttrValueHistorical. */ |
| | | @SuppressWarnings("javadoc") |
| | | public class ValueInfoTest extends ReplicationTestCase |
| | | public class AttrValueHistoricalTest extends ReplicationTestCase |
| | | { |
| | | |
| | | /** |
| | | * Build some data for the ValueInfo test below. |
| | | */ |
| | | /** Build some data for the ValueInfo test below. */ |
| | | @DataProvider(name = "valueInfo") |
| | | public Object[][] createData() { |
| | | ByteString att1 = ByteString.valueOfUtf8("string"); |
| | |
| | | {att3,upd3,del3}}; |
| | | } |
| | | |
| | | /** |
| | | * Create a ValueInfo and check the methods. |
| | | */ |
| | | /** Create a ValueInfo and check the methods. */ |
| | | @Test(dataProvider = "valueInfo") |
| | | public void valueInfo(ByteString value, CSN csnUpdate, CSN csnDelete) throws Exception |
| | | { |
| | | AttrValueHistorical valInfo1 = new AttrValueHistorical(value, csnUpdate, csnDelete); |
| | | AttrValueHistorical valInfo2 = new AttrValueHistorical(value, csnUpdate, csnUpdate); |
| | | AttributeType attrType = CoreSchema.getDescriptionAttributeType(); |
| | | AttrValueHistorical valInfo1 = new AttrValueHistorical(value, attrType, csnUpdate, csnDelete); |
| | | AttrValueHistorical valInfo2 = new AttrValueHistorical(value, attrType, csnUpdate, csnUpdate); |
| | | AttrValueHistorical valInfo3 = new AttrValueHistorical(ByteString.valueOfUtf8("Test"), |
| | | csnUpdate, csnUpdate); |
| | | attrType, csnUpdate, csnUpdate); |
| | | |
| | | // Check equals |
| | | assertFalse(valInfo1.equals(new Object())); |
| | |
| | | assertEquals(valInfo1.getAttributeValue(), value); |
| | | assertEquals(valInfo1.isUpdate(), csnUpdate != null); |
| | | } |
| | | |
| | | @Test |
| | | public void testEqualsHashCode() |
| | | { |
| | | final AttrValueHistorical histVal1 = newAttrValueHistorical("description"); |
| | | final AttrValueHistorical histVal2 = newAttrValueHistorical("Description"); |
| | | final AttrValueHistorical histVal3 = newAttrValueHistorical("another description"); |
| | | |
| | | assertThat(histVal1).isEqualTo(histVal1); |
| | | assertThat(histVal1).isEqualTo(histVal2); |
| | | assertThat(histVal2).isEqualTo(histVal1); |
| | | |
| | | assertThat(histVal1).isNotEqualTo(histVal3); |
| | | |
| | | assertThat(histVal1.hashCode()).isEqualTo(histVal2.hashCode()); |
| | | assertThat(histVal1.hashCode()).isNotEqualTo(histVal3.hashCode()); |
| | | } |
| | | |
| | | private AttrValueHistorical newAttrValueHistorical(String pp) |
| | | { |
| | | AttributeType attrType = CoreSchema.getDescriptionAttributeType(); |
| | | return new AttrValueHistorical(ByteString.valueOfUtf8(pp), attrType, null, null); |
| | | } |
| | | } |