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

Jean-Noel Rouvignac
24.13.2015 ec8ecdbe8ac72dccade39f522e4a702a2f366e9e
EntryHistorical.java:
Use HistAttrModificationKey instead of raw string.
Code cleanup
1 files modified
187 ■■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/EntryHistorical.java 187 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/EntryHistorical.java
@@ -27,7 +27,7 @@
package org.opends.server.replication.plugin;
import static org.opends.messages.ReplicationMessages.*;
import static org.opends.server.util.CollectionUtils.*;
import static org.opends.server.replication.plugin.HistAttrModificationKey.*;
import java.util.*;
@@ -44,39 +44,34 @@
import org.opends.server.util.TimeThread;
/**
 * This class is used to store historical information that is
 * used to resolve modify conflicts
 *
 * It is assumed that the common case is not to have conflict and
 * therefore is optimized (in order of importance) for :
 *  1- detecting potential conflict
 *  2- fast update of historical information for non-conflicting change
 *  3- fast and efficient purge
 *  4- compact
 *  5- solve conflict. This should also be as fast as possible but
 *     not at the cost of any of the other previous objectives
 *
 * One Historical object is created for each entry in the entry cache
 * each Historical Object contains a list of attribute historical information
 * This class is used to store historical information that is used to resolve modify conflicts
 * <p>
 * It is assumed that the common case is not to have conflict and therefore is optimized (in order
 * of importance) for:
 * <ol>
 * <li>detecting potential conflict</li>
 * <li>fast update of historical information for non-conflicting change</li>
 * <li>fast and efficient purge</li>
 * <li>compact</li>
 * <li>solve conflict. This should also be as fast as possible but not at the cost of any of the
 * other previous objectives</li>
 * </ol>
 * One Historical object is created for each entry in the entry cache each Historical Object
 * contains a list of attribute historical information
 */
public class EntryHistorical
{
  /**
   * Name of the attribute used to store historical information.
   */
  /** Name of the attribute used to store historical information. */
  public static final String HISTORICAL_ATTRIBUTE_NAME = "ds-sync-hist";
  /**
   * Name used to store attachment of historical information in the
   * operation. This attachment allows to use in several different places
   * the historical while reading/writing ONCE it from/to the entry.
   */
  public static final String HISTORICAL = "ds-synch-historical";
  /**
   * Name of the entryuuid attribute.
   */
  /** Name of the entryuuid attribute. */
  public static final String ENTRYUUID_ATTRIBUTE_NAME = "entryuuid";
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  /**
@@ -105,7 +100,6 @@
   */
  private int lastPurgedValuesCount;
  /**
   * The in-memory historical information is made of.
   *
@@ -129,10 +123,8 @@
   *                         AttrValueHistorical is the historical of the
   *                         the modification of one value
   *
   * AddTime             ::= CSN // last time the attribute was added
   *                                      // to the entry
   * DeleteTime          ::= CSN // last time the attribute was deleted
   *                                      // from the entry
   * AddTime             ::= CSN // last time the attribute was added to the entry
   * DeleteTime          ::= CSN // last time the attribute was deleted from the entry
   *
   * AttrValueHistorical ::= AttributeValue valueDeleteTime valueUpdateTime
   * valueDeleteTime     ::= CSN
@@ -142,7 +134,6 @@
   *     each value is the historical for this attribute
   *     an AttrInfoWithOptions is a set indexed on the optionValue(string) of
   *     AttributeInfo
   *
   */
  /** The date when the entry was added. */
@@ -175,8 +166,7 @@
   * @param modifiedEntry the entry that is being modified (before modification)
   * @return true if the replayed operation was in conflict
   */
  public boolean replayOperation(PreOperationModifyOperation modifyOperation,
                                 Entry modifiedEntry)
  public boolean replayOperation(PreOperationModifyOperation modifyOperation, Entry modifiedEntry)
  {
    boolean bConflict = false;
    List<Modification> mods = modifyOperation.getModifications();
@@ -215,8 +205,7 @@
   * @param modifyOperation
   *          the modification.
   */
  public void setHistoricalAttrToOperation(
      PreOperationModifyOperation modifyOperation)
  public void setHistoricalAttrToOperation(PreOperationModifyOperation modifyOperation)
  {
    List<Modification> mods = modifyOperation.getModifications();
    Entry modifiedEntry = modifyOperation.getModifiedEntry();
@@ -256,15 +245,14 @@
  /**
     * For a MODDN operation, add new or update existing historical information.
   * <p>
   * This method is NOT static because it relies on this Historical object created in the
   * HandleConflictResolution phase.
     *
     * This method is NOT static because it relies on this Historical object
     * created in the HandleConflictResolution phase.
     *
     * @param modifyDNOperation the modification for which the historical
     *                          information should be created.
   * @param modifyDNOperation
   *          the modification for which the historical information should be created.
     */
  public void setHistoricalAttrToOperation(
      PreOperationModifyDNOperation modifyDNOperation)
  public void setHistoricalAttrToOperation(PreOperationModifyDNOperation modifyDNOperation)
  {
    // Update this historical information with the operation CSN.
    this.entryMODDNDate = OperationContext.getCSN(modifyDNOperation);
@@ -305,23 +293,14 @@
   *   This method is static because there is no Historical object creation
   *   required here or before(in the HandleConflictResolution phase)
   *
   * @param addOperation The Operation to which the historical attribute will
   *                     be added.
   * @param addOperation The Operation to which the historical attribute will be added.
   */
  public static void setHistoricalAttrToOperation(
      PreOperationAddOperation addOperation)
  public static void setHistoricalAttrToOperation(PreOperationAddOperation addOperation)
  {
    AttributeType historicalAttrType =
      DirectoryServer.getSchema().getAttributeType(HISTORICAL_ATTRIBUTE_NAME);
    // Get the CSN from the attached synchronization context
    // Create the attribute (encoded)
    CSN addCSN = OperationContext.getCSN(addOperation);
    String attrValue = encodeHistorical(addCSN, "add");
    Attribute attr = Attributes.create(historicalAttrType, attrValue);
    // Set the created attribute to the operation
    addOperation.setAttribute(historicalAttrType, newLinkedList(attr));
    AttributeType attrType = DirectoryServer.getAttributeType(HISTORICAL_ATTRIBUTE_NAME);
    String attrValue = encodeHistorical(OperationContext.getCSN(addOperation), "add");
    List<Attribute> attrs = Attributes.createAsList(attrType, attrValue);
    addOperation.setAttribute(attrType, attrs);
  }
  /**
@@ -333,8 +312,7 @@
   *          The date when the ADD Operation happened.
   * @param operationType
   *          the operation type to encode
   * @return The attribute value containing the historical information for the
   *         Operation type.
   * @return The attribute value containing the historical information for the Operation type.
   */
  private static String encodeHistorical(CSN csn, String operationType)
  {
@@ -368,8 +346,7 @@
    // Read from this entryHistorical,
    // Create one empty if none was existing in this entryHistorical.
    AttrHistoricalWithOptions attrHistWithOptions =
      attributesHistorical.get(modAttrType);
    AttrHistoricalWithOptions attrHistWithOptions = attributesHistorical.get(modAttrType);
    AttrHistorical attrHist;
    if (attrHistWithOptions != null)
    {
@@ -421,8 +398,7 @@
      purgeDate = TimeThread.getTime() - purgeDelayInMillisec;
    }
    AttributeType historicalAttrType =
      DirectoryServer.getSchema().getAttributeType(HISTORICAL_ATTRIBUTE_NAME);
    AttributeType historicalAttrType = DirectoryServer.getAttributeType(HISTORICAL_ATTRIBUTE_NAME);
    AttributeBuilder builder = new AttributeBuilder(historicalAttrType);
    for (Map.Entry<AttributeType, AttrHistoricalWithOptions> entryWithOptions :
@@ -441,14 +417,13 @@
        CSN deleteTime = attrHist.getDeleteTime();
        /* generate the historical information for deleted attributes */
        boolean delAttr = deleteTime != null;
        boolean attrDel = deleteTime != null;
        for (AttrValueHistorical attrValHist : attrHist.getValuesHistorical())
        {
          final ByteString value = attrValHist.getAttributeValue();
          // Encode an attribute value
          final String strValue;
          if (attrValHist.getValueDeleteTime() != null)
          {
            if (needsPurge(attrValHist.getValueDeleteTime(), purgeDate))
@@ -456,8 +431,7 @@
              // this hist must be purged now, so skip its encoding
              continue;
            }
            strValue = encode("del", type, optionsString, attrValHist
                    .getValueDeleteTime(), value);
            String strValue = encode(DEL, type, optionsString, attrValHist.getValueDeleteTime(), value);
            builder.add(strValue);
          }
          else if (attrValHist.getValueUpdateTime() != null)
@@ -468,37 +442,38 @@
              continue;
            }
            String strValue;
            final CSN updateTime = attrValHist.getValueUpdateTime();
            // FIXME very suspicious use of == in the next if statement,
            // unit tests do not like changing it
            if (delAttr && updateTime == deleteTime && value != null)
            if (attrDel && updateTime == deleteTime && value != null)
            {
              strValue = encode("repl", type, optionsString, updateTime, value);
              delAttr = false;
              strValue = encode(REPL, type, optionsString, updateTime, value);
              attrDel = false;
            }
            else if (value != null)
            {
              strValue = encode("add", type, optionsString, updateTime, value);
              strValue = encode(ADD, type, optionsString, updateTime, value);
            }
            else
            {
              // "add" without any value is suspicious. Tests never go there.
              // Is this used to encode "add" with an empty string?
              strValue = encode("add", type, optionsString, updateTime);
              strValue = encode(ADD, type, optionsString, updateTime);
            }
            builder.add(strValue);
          }
        }
        if (delAttr)
        if (attrDel)
        {
          if (needsPurge(deleteTime, purgeDate))
          {
            // this hist must be purged now, so skip its encoding
            continue;
          }
          String strValue = encode("attrDel", type, optionsString, deleteTime);
          String strValue = encode(ATTRDEL, type, optionsString, deleteTime);
          builder.add(strValue);
        }
      }
@@ -546,18 +521,16 @@
    return needsPurge;
  }
  private String encode(String operation, AttributeType type,
  private String encode(HistAttrModificationKey modKey, AttributeType type,
      String optionsString, CSN changeTime)
  {
    return type.getNormalizedPrimaryName() + optionsString + ":" + changeTime
        + ":" + operation;
    return type.getNormalizedPrimaryName() + optionsString + ":" + changeTime + ":" + modKey;
  }
  private String encode(String operation, AttributeType type,
  private String encode(HistAttrModificationKey modKey, AttributeType type,
      String optionsString, CSN changeTime, ByteString value)
  {
    return type.getNormalizedPrimaryName() + optionsString + ":" + changeTime
        + ":" + operation + ":" + value;
    return type.getNormalizedPrimaryName() + optionsString + ":" + changeTime + ":" + modKey + ":" + value;
  }
  /**
@@ -585,7 +558,6 @@
    return csn.isOlderThan(entryADDDate) || csn.isOlderThan(entryMODDNDate);
  }
  /**
   * Returns the lastCSN when the entry DN was modified.
   *
@@ -613,14 +585,11 @@
  }
  /**
   * Construct an Historical object from the provided entry by reading the
   * historical attribute.
   * Return an empty object when the entry does not contain any
   * historical attribute.
   * Construct an Historical object from the provided entry by reading the historical attribute.
   * Return an empty object when the entry does not contain any historical attribute.
   *
   * @param entry The entry which historical information must be loaded
   * @return The constructed Historical information object
   *
   */
  public static EntryHistorical newInstanceFromEntry(Entry entry)
  {
@@ -727,7 +696,6 @@
    return newHistorical;
  }
  /**
   * Use this historical information to generate fake operations that would
   * result in this historical information.
@@ -735,8 +703,7 @@
   *        need to complete with DELETE.
   * @param entry The Entry to use to generate the FakeOperation Iterable.
   *
   * @return an Iterable of FakeOperation that would result in this historical
   *         information.
   * @return an Iterable of FakeOperation that would result in this historical information.
   */
  public static Iterable<FakeOperation> generateFakeOperations(Entry entry)
  {
@@ -748,23 +715,18 @@
      {
        for (ByteString val : attr)
        {
          HistoricalAttributeValue histVal =
            new HistoricalAttributeValue(val.toString());
          HistoricalAttributeValue histVal = new HistoricalAttributeValue(val.toString());
          if (histVal.isADDOperation())
          {
            // Found some historical information indicating that this
            // entry was just added.
            // Found some historical information indicating that this entry was just added.
            // Create the corresponding ADD operation.
            operations.put(histVal.getCSN(),
                new FakeAddOperation(histVal.getCSN(), entry));
            operations.put(histVal.getCSN(), new FakeAddOperation(histVal.getCSN(), entry));
          }
          else if (histVal.isMODDNOperation())
          {
            // Found some historical information indicating that this
            // entry was just renamed.
            // Found some historical information indicating that this entry was just renamed.
            // Create the corresponding ADD operation.
            operations.put(histVal.getCSN(),
                new FakeModdnOperation(histVal.getCSN(), entry));
            operations.put(histVal.getCSN(), new FakeModdnOperation(histVal.getCSN(), entry));
          }
          else
          {
@@ -777,8 +739,7 @@
            if (fakeOperation instanceof FakeModifyOperation)
            {
              FakeModifyOperation modifyFakeOperation =
                  (FakeModifyOperation) fakeOperation;
              FakeModifyOperation modifyFakeOperation = (FakeModifyOperation) fakeOperation;
              modifyFakeOperation.addModification(mod);
            }
            else
@@ -797,11 +758,9 @@
  }
  /**
   * Get the attribute used to store the historical information from
   * the provided Entry.
   * Get the attribute used to store the historical information from the provided Entry.
   *
   * @param   entry  The entry containing the historical information.
   *
   * @return  The Attribute used to store the historical information.
   *          Several values on the list if several options for this attribute.
   *          Null if not present.
@@ -815,15 +774,12 @@
   * Get the entry unique Id in String form.
   *
   * @param entry The entry for which the unique id should be returned.
   *
   * @return The Unique Id of the entry, or a fake one if none is found.
   */
  public static String getEntryUUID(Entry entry)
  {
    AttributeType entryuuidAttrType =
      DirectoryServer.getSchema().getAttributeType(ENTRYUUID_ATTRIBUTE_NAME);
    List<Attribute> uuidAttrs =
             entry.getOperationalAttribute(entryuuidAttrType);
    AttributeType attrType = DirectoryServer.getAttributeType(ENTRYUUID_ATTRIBUTE_NAME);
    List<Attribute> uuidAttrs = entry.getOperationalAttribute(attrType);
    return extractEntryUUID(uuidAttrs, entry.getName());
  }
@@ -837,10 +793,8 @@
   */
  public static String getEntryUUID(PreOperationAddOperation op)
  {
    Map<AttributeType, List<Attribute>> attrs = op.getOperationalAttributes();
    AttributeType entryuuidAttrType =
      DirectoryServer.getSchema().getAttributeType(ENTRYUUID_ATTRIBUTE_NAME);
    List<Attribute> uuidAttrs = attrs.get(entryuuidAttrType);
    AttributeType attrType = DirectoryServer.getAttributeType(ENTRYUUID_ATTRIBUTE_NAME);
    List<Attribute> uuidAttrs = op.getOperationalAttributes().get(attrType);
    return extractEntryUUID(uuidAttrs, op.getEntryDN());
  }
@@ -856,7 +810,7 @@
  public static boolean isHistoricalAttribute(Attribute attr)
  {
    AttributeType attrType = attr.getAttributeType();
    return EntryHistorical.HISTORICAL_ATTRIBUTE_NAME.equals(attrType.getNameOrOID());
    return HISTORICAL_ATTRIBUTE_NAME.equals(attrType.getNameOrOID());
  }
  /**
@@ -892,8 +846,7 @@
   * attributes. If the attribute is not present one is generated from the DN
   * using the same algorithm as the entryUUID virtual attribute provider.
   */
  private static String extractEntryUUID(List<Attribute> entryUUIDAttributes,
      DN entryDN)
  private static String extractEntryUUID(List<Attribute> entryUUIDAttributes, DN entryDN)
  {
    if (entryUUIDAttributes != null)
    {
@@ -905,9 +858,8 @@
    }
    // Generate a fake entryUUID: see OPENDJ-181. In rare pathological cases
    // an entryUUID attribute may not be present and this causes severe side
    // effects for replication which requires the attribute to always be
    // present.
    // an entryUUID attribute may not be present and this causes severe side effects
    // for replication which requires the attribute to always be present
    if (logger.isTraceEnabled())
    {
      logger.trace(
@@ -920,4 +872,3 @@
    return UUID.nameUUIDFromBytes(entryDN.toNormalizedByteString().toByteArray()).toString();
  }
}