opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistorical.java
@@ -28,17 +28,15 @@ package org.opends.server.replication.plugin; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.forgerock.opendj.ldap.ByteString; import org.opends.server.replication.common.CSN; import org.opends.server.types.AttributeType; import org.forgerock.opendj.ldap.ByteString; import org.opends.server.types.Entry; import org.opends.server.types.Modification; /** * This class store historical information for a provided attribute. */ /** This class store historical information for a provided attribute. */ public abstract class AttrHistorical { /** @@ -55,23 +53,21 @@ * @return a boolean indicating if a conflict was detected. */ public abstract boolean replayOperation( Iterator<Modification> modsIterator, CSN csn, Entry modifiedEntry, Modification mod); Iterator<Modification> modsIterator, CSN csn, Entry modifiedEntry, Modification mod); /** * This method calculate the historical information and update the hist * This method calculates the historical information and update the hist * attribute to store the historical information for modify operation that * does not conflict with previous operation. * This is the usual path and should therefore be optimized. * * <p> * It does not check if the operation to process is conflicting or not with * previous operations. The caller is responsible for this. * * @param csn The CSN of the operation to process * @param mod The modify operation to process. */ public abstract void processLocalOrNonConflictModification( CSN csn, Modification mod); public abstract void processLocalOrNonConflictModification(CSN csn, Modification mod); /** * Create a new object from a provided attribute type. Historical is empty. @@ -79,19 +75,17 @@ * @param type the provided attribute type. * @return a new AttributeInfo object. */ public static AttrHistorical createAttributeHistorical( AttributeType type) public static AttrHistorical createAttributeHistorical(AttributeType type) { return type.isSingleValue() ? new AttrHistoricalSingle() : new AttrHistoricalMultiple(); } /** * Get the List of ValueInfo for this attribute Info. * Get the historical informations for this attribute Info. * * @return the List of ValueInfo * @return the historical informations */ public abstract Map<AttrValueHistorical, AttrValueHistorical> getValuesHistorical(); public abstract Set<AttrValueHistorical> getValuesHistorical(); /** * Returns the last time when this attribute was deleted. @@ -107,8 +101,5 @@ * @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, ByteString value, CSN csn); } opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalMultiple.java
@@ -29,11 +29,16 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.ModificationType; import org.opends.server.replication.common.CSN; import org.opends.server.types.*; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeBuilder; import org.opends.server.types.AttributeType; import org.opends.server.types.Entry; import org.opends.server.types.Modification; /** * This class is used to store historical information for multiple valued @@ -98,10 +103,6 @@ return lastUpdateTime; } /** * Returns the last time when the attribute was deleted. * @return the last time when the attribute was deleted */ @Override public CSN getDeleteTime() { @@ -117,8 +118,7 @@ */ AttrHistoricalMultiple duplicate() { return new AttrHistoricalMultiple(this.deleteTime, this.lastUpdateTime, this.valuesHist); return new AttrHistoricalMultiple(this.deleteTime, this.lastUpdateTime, this.valuesHist); } /** @@ -127,7 +127,7 @@ * Add the delete attribute state information * @param csn time when the delete was done */ protected void delete(CSN csn) void delete(CSN csn) { // iterate through the values in the valuesInfo and suppress all the values // that have not been added after the date of this delete. @@ -153,43 +153,46 @@ } } /** * Update the historical of this attribute after deleting a set of values. * * @param attr * the attribute containing the set of values that were deleted * @param csn * time when the delete was done */ void delete(Attribute attr, CSN csn) { for (ByteString val : attr) { delete(val, 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 */ protected void delete(ByteString val, CSN csn) void delete(ByteString val, CSN csn) { AttrValueHistorical info = new AttrValueHistorical(val, null, csn); valuesHist.remove(info); valuesHist.put(info, info); if (csn.isNewerThan(lastUpdateTime)) { lastUpdateTime = csn; } update(csn, new AttrValueHistorical(val, null, csn)); } /** * Update the historical of this attribute after deleting a set of values. * * @param attr * the attribute containing the set of values that were * deleted * @param csn * time when the delete was done */ protected void delete(Attribute attr, CSN csn) /** * Update the historical information when values are added. * * @param attr * the attribute containing the set of added values * @param csn * time when the add is done */ private void add(Attribute attr, CSN csn) { for (ByteString val : attr) { AttrValueHistorical info = new AttrValueHistorical(val, null, csn); valuesHist.remove(info); valuesHist.put(info, info); if (csn.isNewerThan(lastUpdateTime)) { lastUpdateTime = csn; } add(val, csn); } } @@ -201,51 +204,27 @@ * @param csn * time when the value was added */ protected void add(ByteString addedValue, CSN csn) void add(ByteString addedValue, CSN csn) { AttrValueHistorical info = new AttrValueHistorical(addedValue, csn, null); valuesHist.remove(info); valuesHist.put(info, info); if (csn.isNewerThan(lastUpdateTime)) { lastUpdateTime = csn; } update(csn, new AttrValueHistorical(addedValue, csn, null)); } /** * Update the historical information when values are added. * * @param attr * the attribute containing the set of added values * @param csn * time when the add is done */ private void add(Attribute attr, CSN csn) private void update(CSN csn, AttrValueHistorical info) { for (ByteString val : attr) valuesHist.remove(info); valuesHist.put(info, info); if (csn.isNewerThan(lastUpdateTime)) { AttrValueHistorical info = new AttrValueHistorical(val, csn, null); valuesHist.remove(info); valuesHist.put(info, info); if (csn.isNewerThan(lastUpdateTime)) { lastUpdateTime = csn; } lastUpdateTime = csn; } } /** * Get the list of historical information for the values. * * @return the list of historical information for the values. */ @Override public Map<AttrValueHistorical,AttrValueHistorical> getValuesHistorical() public Set<AttrValueHistorical> getValuesHistorical() { return valuesHist; return valuesHist.keySet(); } /** {@inheritDoc} */ @Override public boolean replayOperation(Iterator<Modification> modsIterator, CSN csn, Entry modifiedEntry, Modification m) @@ -326,25 +305,12 @@ } } /** * This method calculates the historical information and update the hist * attribute to store the historical information for a modify operation that * does not conflict with previous operation. * This is the usual path and should therefore be optimized. * * It does not check if the operation to process is conflicting or not with * previous operations. The caller is responsible for this. * * @param csn The CSN of the operation to process * @param mod The modify operation to process. */ @Override public void processLocalOrNonConflictModification(CSN csn, Modification mod) { /* * The operation is either a non-conflicting operation or a local * operation so there is no need to check the historical information * for conflicts. * The operation is either a non-conflicting operation or a local operation * so there is no need to check the historical information for conflicts. * If this is a local operation, then this code is run after * the pre-operation phase. * If this is a non-conflicting replicated operation, this code is run @@ -472,8 +438,7 @@ boolean addedInCurrentOp = false; /* update historical information */ AttrValueHistorical valInfo = new AttrValueHistorical(val, null, csn); AttrValueHistorical valInfo = new AttrValueHistorical(val, null, csn); AttrValueHistorical oldValInfo = valuesHist.get(valInfo); if (oldValInfo != null) { @@ -539,10 +504,8 @@ * @param csn the historical info associated to the entry * @param m the modification that is being processed * @param modsIterator iterator on the list of modification * @return false if operation becomes empty and must not be processed */ private boolean conflictAdd(CSN csn, Modification m, Iterator<Modification> modsIterator) private void conflictAdd(CSN csn, Modification m, Iterator<Modification> modsIterator) { /* * if historicalattributedelete is newer forget this mod else find @@ -560,7 +523,7 @@ * forget this MOD ADD */ modsIterator.remove(); return false; return; } AttributeBuilder builder = new AttributeBuilder(m.getAttribute()); @@ -633,11 +596,8 @@ { lastUpdateTime = csn; } return true; } /** {@inheritDoc} */ @Override public void assign(HistAttrModificationKey histKey, ByteString value, CSN csn) { @@ -671,5 +631,3 @@ } } } opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/AttrHistoricalSingle.java
@@ -26,9 +26,11 @@ */ package org.opends.server.replication.plugin; import static org.opends.server.replication.plugin.HistAttrModificationKey.*; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.ModificationType; @@ -54,45 +56,33 @@ private CSN addTime; /** Last added value. */ private ByteString value; /** * Last operation applied. This is only used for multiple mods on the same * single valued attribute in the same modification. */ private HistAttrModificationKey lastMod; /** {@inheritDoc} */ @Override public CSN getDeleteTime() { return this.deleteTime; } /** {@inheritDoc} */ @Override public Map<AttrValueHistorical,AttrValueHistorical> getValuesHistorical() public Set<AttrValueHistorical> getValuesHistorical() { if (addTime == null) if (addTime != null) { return Collections.emptyMap(); return Collections.singleton(new AttrValueHistorical(value, addTime, null)); } else { AttrValueHistorical val = new AttrValueHistorical(value, addTime, null); return Collections.singletonMap(val, val); } return Collections.emptySet(); } /** {@inheritDoc} */ @Override public void processLocalOrNonConflictModification(CSN csn, Modification mod) { ByteString newValue = null; Attribute modAttr = mod.getAttribute(); if (modAttr != null && !modAttr.isEmpty()) { newValue = modAttr.iterator().next(); } ByteString newValue = getSingleValue(modAttr); switch (mod.getModificationType().asEnum()) { @@ -100,13 +90,13 @@ this.addTime = null; this.deleteTime = csn; this.value = newValue; lastMod = HistAttrModificationKey.DEL; lastMod = DEL; break; case ADD: this.addTime = csn; this.value = newValue; lastMod = HistAttrModificationKey.ADD; lastMod = ADD; break; case REPLACE: @@ -116,12 +106,12 @@ this.addTime = null; this.deleteTime = csn; this.value = null; lastMod = HistAttrModificationKey.DEL; lastMod = DEL; } else { this.deleteTime = addTime = csn; lastMod = HistAttrModificationKey.REPL; lastMod = REPL; } this.value = newValue; break; @@ -132,20 +122,14 @@ } } /** {@inheritDoc} */ @Override public boolean replayOperation(Iterator<Modification> modsIterator, CSN csn, Entry modifiedEntry, Modification mod) { boolean conflict = false; ByteString newValue = null; Attribute modAttr = mod.getAttribute(); if (modAttr != null && !modAttr.isEmpty()) { newValue = modAttr.iterator().next(); } ByteString newValue = getSingleValue(modAttr); boolean conflict = false; switch (mod.getModificationType().asEnum()) { case DELETE: @@ -172,7 +156,7 @@ else { addTime = null; lastMod = HistAttrModificationKey.DEL; lastMod = DEL; value = null; } } @@ -184,15 +168,14 @@ } else if (csn.equals(addTime)) { if (lastMod == HistAttrModificationKey.ADD || lastMod == HistAttrModificationKey.REPL) if (lastMod == ADD || lastMod == REPL) { if (csn.isNewerThan(deleteTime)) { deleteTime = csn; } addTime = null; lastMod = HistAttrModificationKey.DEL; lastMod = DEL; value = null; } else @@ -215,7 +198,7 @@ mod.setModificationType(ModificationType.REPLACE); addTime = csn; value = newValue; lastMod = HistAttrModificationKey.REPL; lastMod = REPL; } else { @@ -225,17 +208,17 @@ // no conflict : don't do anything beside setting the addTime addTime = csn; value = newValue; lastMod = HistAttrModificationKey.ADD; lastMod = ADD; } else { // Case where CSN = addTime = deleteTime if (csn.equals(deleteTime) && csn.equals(addTime) && lastMod == HistAttrModificationKey.DEL) && lastMod == DEL) { // No conflict, record the new value. value = newValue; lastMod = HistAttrModificationKey.ADD; lastMod = ADD; } else { @@ -260,14 +243,14 @@ addTime = null; value = newValue; deleteTime = csn; lastMod = HistAttrModificationKey.DEL; lastMod = DEL; } else { addTime = csn; value = newValue; deleteTime = csn; lastMod = HistAttrModificationKey.REPL; lastMod = REPL; } } break; @@ -279,7 +262,15 @@ return conflict; } /** {@inheritDoc} */ private ByteString getSingleValue(Attribute modAttr) { if (modAttr != null && !modAttr.isEmpty()) { return modAttr.iterator().next(); } return null; } @Override public void assign(HistAttrModificationKey histKey, ByteString value, CSN csn) { @@ -312,4 +303,3 @@ } } } opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/EntryHistorical.java
@@ -433,30 +433,17 @@ Map<Set<String>, AttrHistorical> attrWithOptions = entryWithOptions.getValue().getAttributesInfo(); for (Map.Entry<Set<String>, AttrHistorical> entry : attrWithOptions .entrySet()) for (Map.Entry<Set<String>, AttrHistorical> entry : attrWithOptions.entrySet()) { // Encode an (attribute type/option) Set<String> options = entry.getKey(); String optionsString = toOptionsString(entry.getKey()); AttrHistorical attrHist = entry.getValue(); String optionsString = ""; if (options != null) { StringBuilder optionsBuilder = new StringBuilder(); for (String s : options) { optionsBuilder.append(';').append(s); } optionsString = optionsBuilder.toString(); } CSN deleteTime = attrHist.getDeleteTime(); /* generate the historical information for deleted attributes */ boolean delAttr = deleteTime != null; for (AttrValueHistorical attrValHist : attrHist.getValuesHistorical() .keySet()) for (AttrValueHistorical attrValHist : attrHist.getValuesHistorical()) { final ByteString value = attrValHist.getAttributeValue(); @@ -534,6 +521,20 @@ return builder.toAttribute(); } private String toOptionsString(Set<String> options) { if (options != null) { StringBuilder optionsBuilder = new StringBuilder(); for (String s : options) { optionsBuilder.append(';').append(s); } return optionsBuilder.toString(); } return ""; } private boolean needsPurge(CSN csn, long purgeDate) { boolean needsPurge = purgeDelayInMillisec > 0 && csn.getTime() <= purgeDate; @@ -623,17 +624,11 @@ */ public static EntryHistorical newInstanceFromEntry(Entry entry) { AttributeType lastAttrType = null; Set<String> lastOptions = new HashSet<>(); AttrHistorical attrInfo = null; AttrHistoricalWithOptions attrInfoWithOptions = null; // Read the DB historical attribute from the entry List<Attribute> histAttrWithOptionsFromEntry = getHistoricalAttr(entry); // Now we'll build the Historical object we want to construct EntryHistorical newHistorical = new EntryHistorical(); final EntryHistorical newHistorical = new EntryHistorical(); if (histAttrWithOptionsFromEntry == null) { // No historical attribute in the entry, return empty object @@ -642,6 +637,11 @@ try { AttributeType lastAttrType = null; Set<String> lastOptions = new HashSet<>(); AttrHistorical attrInfo = null; AttrHistoricalWithOptions attrInfoWithOptions = null; // For each value of the historical attr read (mod. on a user attribute) // build an AttrInfo sub-object @@ -653,14 +653,8 @@ for (ByteString histAttrValueFromEntry : histAttrFromEntry) { // From each value of the hist attr, create an object HistoricalAttributeValue histVal = new HistoricalAttributeValue( histAttrValueFromEntry.toString()); AttributeType attrType = histVal.getAttrType(); Set<String> options = histVal.getOptions(); CSN csn = histVal.getCSN(); ByteString value = histVal.getAttributeValue(); HistAttrModificationKey histKey = histVal.getHistKey(); final HistoricalAttributeValue histVal = new HistoricalAttributeValue(histAttrValueFromEntry.toString()); final CSN csn = histVal.getCSN(); // update the oldest CSN stored in the new entry historical newHistorical.updateOldestCSN(csn); @@ -675,6 +669,7 @@ } else { AttributeType attrType = histVal.getAttrType(); if (attrType == null) { /* @@ -694,6 +689,7 @@ * AttrInfo that we add to AttrInfoWithOptions * if both match we keep everything */ Set<String> options = histVal.getOptions(); if (attrType != lastAttrType) { attrInfo = AttrHistorical.createAttributeHistorical(attrType); @@ -703,8 +699,7 @@ attrInfoWithOptions.put(options, attrInfo); // Store this attrInfoWithOptions in the newHistorical object newHistorical.attributesHistorical. put(attrType, attrInfoWithOptions); newHistorical.attributesHistorical.put(attrType, attrInfoWithOptions); lastAttrType = attrType; lastOptions = options; @@ -716,7 +711,7 @@ lastOptions = options; } attrInfo.assign(histKey, value, csn); attrInfo.assign(histVal.getHistKey(), histVal.getAttributeValue(), csn); } } } opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/AttrInfoTest.java
@@ -27,13 +27,13 @@ package org.opends.server.replication.plugin; import java.util.Collections; import java.util.Map; import java.util.Set; import org.forgerock.opendj.ldap.ByteString; import org.opends.server.core.DirectoryServer; import org.opends.server.replication.ReplicationTestCase; import org.opends.server.replication.common.CSN; import org.opends.server.types.AttributeType; import org.forgerock.opendj.ldap.ByteString; import org.opends.server.types.Attributes; import org.opends.server.util.TimeThread; import org.testng.annotations.DataProvider; @@ -41,16 +41,11 @@ import static org.testng.Assert.*; /** * Test AttrInfo and AttrInfoWithOptions. */ /** Test AttrInfo and AttrInfoWithOptions. */ @SuppressWarnings("javadoc") public class AttrInfoTest extends ReplicationTestCase { /** * Build some data for the AttrInfo test below. */ /** Build some data for the AttrInfo test below. */ @DataProvider(name = "attrInfo") public Object[][] createData() { @@ -74,9 +69,7 @@ { att3, upd3, upd3 } }; } /** * Create a AttrInfo and check the methods. */ /** Create a AttrInfo and check the methods. */ @Test(dataProvider = "attrInfo") public void attrInfo(ByteString att, CSN deleteTime, CSN updateTime) throws Exception { @@ -85,10 +78,10 @@ // Check attrInfo1.add(att, updateTime); Map<AttrValueHistorical,AttrValueHistorical> values1 = attrInfo1.getValuesHistorical(); Set<AttrValueHistorical> values1 = attrInfo1.getValuesHistorical(); assertEquals(values1.size(), 1); AttrValueHistorical valueInfo1 = new AttrValueHistorical(att, updateTime, null); assertTrue(values1.containsKey(valueInfo1)); assertTrue(values1.contains(valueInfo1)); // Check constructor with parameter AttrValueHistorical valueInfo2 = new AttrValueHistorical(att, updateTime, deleteTime); @@ -101,14 +94,14 @@ // Check constructor with time parameter and not Value AttrHistoricalMultiple attrInfo3 = new AttrHistoricalMultiple(deleteTime, updateTime, null); attrInfo3.add(att, updateTime); Map<AttrValueHistorical,AttrValueHistorical> values3 = attrInfo3.getValuesHistorical(); Set<AttrValueHistorical> values3 = attrInfo3.getValuesHistorical(); assertEquals(values3.size(), 1); valueInfo1 = new AttrValueHistorical(att, updateTime, null); assertTrue(values3.containsKey(valueInfo1)); assertTrue(values3.contains(valueInfo1)); // Check duplicate AttrHistoricalMultiple attrInfo4 = attrInfo3.duplicate(); Map<AttrValueHistorical,AttrValueHistorical> values4 = attrInfo4.getValuesHistorical(); Set<AttrValueHistorical> values4 = attrInfo4.getValuesHistorical(); assertEquals(attrInfo4.getDeleteTime().compareTo(attrInfo3.getDeleteTime()), 0); assertEquals(values4.size(), values3.size()); @@ -122,7 +115,7 @@ assertEquals(attrInfo3.getValuesHistorical().size(), 1); // Check attrInfo2.delete(updateTime) ; attrInfo2.delete(updateTime); assertEquals(attrInfo2.getValuesHistorical().size(), 0); } }