mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Jean-Noel Rouvignac
18.56.2015 1f051607c1cd390c0fecd5e89c9f479b8a53435c
opendj-sdk/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-sdk/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-sdk/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-sdk/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-sdk/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);
  }
}