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

neil_a_wilson
29.10.2007 8a060091156c631c76476582458a32daa50a5bb2
opends/src/server/org/opends/server/core/DefaultCompressedSchema.java
File was renamed from opends/src/server/org/opends/server/types/CompressedSchema.java
@@ -24,8 +24,7 @@
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import org.opends.messages.Message;
package org.opends.server.core;
@@ -39,7 +38,8 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.opends.server.core.DirectoryServer;
import org.opends.messages.Message;
import org.opends.server.api.CompressedSchema;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.asn1.ASN1Element;
import org.opends.server.protocols.asn1.ASN1Integer;
@@ -47,6 +47,13 @@
import org.opends.server.protocols.asn1.ASN1Reader;
import org.opends.server.protocols.asn1.ASN1Sequence;
import org.opends.server.protocols.asn1.ASN1Writer;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteArray;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.ObjectClass;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
@@ -56,15 +63,12 @@
/**
 * This class provides a utility for interacting with compressed
 * representations of schema elements.
 * This class provides a default implementation of a compressed schema manager
 * that will store the schema definitions in a binary file
 * (config/schematokens.dat).
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.PRIVATE,
     mayInstantiate=false,
     mayExtend=false,
     mayInvoke=false)
public final class CompressedSchema
public final class DefaultCompressedSchema
       extends CompressedSchema
{
  /**
   * The tracer object for the debug logger.
@@ -73,13 +77,6 @@
  /**
   * The singleton instance that will be used to perform the mapping.
   */
  private static CompressedSchema instance = new CompressedSchema();
  // The counter used for attribute descriptions.
  private AtomicInteger adCounter;
@@ -90,40 +87,33 @@
  private ConcurrentHashMap<ByteArray,AttributeType> atDecodeMap;
  // The map between encoded representations and attribute options.
  private ConcurrentHashMap<ByteArray,
               LinkedHashSet<String>> aoDecodeMap;
  private ConcurrentHashMap<ByteArray,LinkedHashSet<String>> aoDecodeMap;
  // The map between encoded representations and object class sets.
  private ConcurrentHashMap<ByteArray,
               Map<ObjectClass,String>> ocDecodeMap;
  private ConcurrentHashMap<ByteArray,Map<ObjectClass,String>> ocDecodeMap;
  // The map between attribute descriptions and their encoded
  // representations.
  private ConcurrentHashMap<AttributeType,
               ConcurrentHashMap<LinkedHashSet<String>,ByteArray>>
                    adEncodeMap;
               ConcurrentHashMap<LinkedHashSet<String>,ByteArray>> adEncodeMap;
  // The map between object class sets and encoded representations.
  private ConcurrentHashMap<Map<ObjectClass,String>,
               ByteArray> ocEncodeMap;
  private ConcurrentHashMap<Map<ObjectClass,String>,ByteArray> ocEncodeMap;
  /**
   * Creates a new instance of this compressed schema manager.
   */
  private CompressedSchema()
  public DefaultCompressedSchema()
  {
    atDecodeMap = new ConcurrentHashMap<ByteArray,AttributeType>();
    aoDecodeMap = new ConcurrentHashMap<ByteArray,
                           LinkedHashSet<String>>();
    ocDecodeMap = new ConcurrentHashMap<ByteArray,
                           Map<ObjectClass,String>>();
    adEncodeMap = new ConcurrentHashMap<AttributeType,
                           ConcurrentHashMap<LinkedHashSet<String>,
                                ByteArray>>();
    ocEncodeMap = new ConcurrentHashMap<Map<ObjectClass,String>,
                           ByteArray>();
    aoDecodeMap = new ConcurrentHashMap<ByteArray,LinkedHashSet<String>>();
    ocDecodeMap = new ConcurrentHashMap<ByteArray,Map<ObjectClass,String>>();
    adEncodeMap =
         new ConcurrentHashMap<AttributeType,
                  ConcurrentHashMap<LinkedHashSet<String>,ByteArray>>();
    ocEncodeMap = new ConcurrentHashMap<Map<ObjectClass,String>,ByteArray>();
    adCounter = new AtomicInteger(1);
    ocCounter = new AtomicInteger(1);
@@ -142,10 +132,9 @@
    try
    {
      // Determine the location of the compressed schema data file
      // It should be in the config directory with a name of
      // "schematokens.dat".  If that file doesn't exist, then don't
      // do anything.
      // Determine the location of the compressed schema data file.  It should
      // be in the config directory with a name of "schematokens.dat".  If that
      // file doesn't exist, then don't do anything.
      String path = DirectoryServer.getServerRoot() + File.separator +
                    CONFIG_DIR_NAME + File.separator +
                    COMPRESSED_SCHEMA_FILE_NAME;
@@ -157,17 +146,14 @@
      reader = new ASN1Reader(inputStream);
      // The first element in the file should be a sequence of object
      // class sets.  Each object class set will itself be a sequence
      // of octet strings, where the first one is the token and the
      // remaining elements are the names of the associated object
      // classes.
      ASN1Sequence ocSequence =
           reader.readElement().decodeAsSequence();
      // The first element in the file should be a sequence of object class
      // sets.  Each object class set will itself be a sequence of octet
      // strings, where the first one is the token and the remaining elements
      // are the names of the associated object classes.
      ASN1Sequence ocSequence = reader.readElement().decodeAsSequence();
      for (ASN1Element element : ocSequence.elements())
      {
        ArrayList<ASN1Element> elements =
             element.decodeAsSequence().elements();
        ArrayList<ASN1Element> elements = element.decodeAsSequence().elements();
        ASN1OctetString os = elements.get(0).decodeAsOctetString();
        ByteArray token = new ByteArray(os.value());
@@ -178,8 +164,7 @@
          os = elements.get(i).decodeAsOctetString();
          String ocName = os.stringValue();
          String lowerName = toLowerCase(ocName);
          ObjectClass oc =
               DirectoryServer.getObjectClass(lowerName, true);
          ObjectClass oc = DirectoryServer.getObjectClass(lowerName, true);
          ocMap.put(oc, ocName);
        }
@@ -188,24 +173,21 @@
      }
      // The second element in the file should be an integer element
      // that holds the value to use to initialize the object class
      // counter.
      // The second element in the file should be an integer element that holds
      // the value to use to initialize the object class counter.
      ASN1Element counterElement = reader.readElement();
      ocCounter.set(counterElement.decodeAsInteger().intValue());
      // The third element in the file should be a sequence of
      // attribute description components.  Each attribute description
      // component will itself be a sequence of octet strings, where
      // the first one is the token, the second is the attribute name,
      // and all remaining elements are the attribute options.
      ASN1Sequence adSequence =
           reader.readElement().decodeAsSequence();
      // The third element in the file should be a sequence of attribute
      // description components.  Each attribute description component will
      // itself be a sequence of octet strings, where the first one is the
      // token, the second is the attribute name, and all remaining elements are
      // the attribute options.
      ASN1Sequence adSequence = reader.readElement().decodeAsSequence();
      for (ASN1Element element : adSequence.elements())
      {
        ArrayList<ASN1Element> elements =
             element.decodeAsSequence().elements();
        ArrayList<ASN1Element> elements = element.decodeAsSequence().elements();
        ASN1OctetString os = elements. get(0).decodeAsOctetString();
        ByteArray token = new ByteArray(os.value());
@@ -230,8 +212,7 @@
             adEncodeMap.get(attrType);
        if (map == null)
        {
          map = new ConcurrentHashMap<LinkedHashSet<String>,
                         ByteArray>(1);
          map = new ConcurrentHashMap<LinkedHashSet<String>,ByteArray>(1);
          map.put(options, token);
          adEncodeMap.put(attrType, map);
        }
@@ -242,9 +223,8 @@
      }
      // The fourth element in the file should be an integer element
      // that holds the value to use to initialize the attribute
      // description counter.
      // The fourth element in the file should be an integer element that holds
      // the value to use to initialize the attribute description counter.
      counterElement = reader.readElement();
      adCounter.set(counterElement.decodeAsInteger().intValue());
    }
@@ -282,8 +262,8 @@
  /**
   * Writes the compressed schema information to disk.
   *
   * @throws  DirectoryException  If a problem occurs while writing
   *                              the updated information.
   * @throws  DirectoryException  If a problem occurs while writing the updated
   *                              information.
   */
  private void save()
          throws DirectoryException
@@ -291,9 +271,9 @@
    ASN1Writer writer = null;
    try
    {
      // Determine the location of the "live" compressed schema data
      // file, and then append ".tmp" to get the name of the temporary
      // file that we will use.
      // Determine the location of the "live" compressed schema data file, and
      // then append ".tmp" to get the name of the temporary file that we will
      // use.
      String path = DirectoryServer.getServerRoot() + File.separator +
                    CONFIG_DIR_NAME + File.separator +
                    COMPRESSED_SCHEMA_FILE_NAME;
@@ -303,11 +283,10 @@
      writer = new ASN1Writer(outputStream);
      // The first element in the file should be a sequence of object
      // class sets.  Each object class set will itself be a sequence
      // of octet strings, where the first one is the token and the
      // remaining elements are the names of the associated object
      // classes.
      // The first element in the file should be a sequence of object class
      // sets.  Each object class set will itself be a sequence of octet
      // strings, where the first one is the token and the remaining elements
      // are the names of the associated object classes.
      ArrayList<ASN1Element> ocElements =
           new ArrayList<ASN1Element>(ocDecodeMap.size());
      for (Map.Entry<ByteArray,Map<ObjectClass,String>> mapEntry :
@@ -330,17 +309,16 @@
      writer.writeElement(new ASN1Sequence(ocElements));
      // The second element in the file should be an integer element
      // that holds the value to use to initialize the object class
      // counter.
      // The second element in the file should be an integer element that holds
      // the value to use to initialize the object class counter.
      writer.writeElement(new ASN1Integer(ocCounter.get()));
      // The third element in the file should be a sequence of
      // attribute description components.  Each attribute description
      // component will itself be a sequence of octet strings, where
      // the first one is the token, the second is the attribute name,
      // and all remaining elements are the attribute options.
      // The third element in the file should be a sequence of attribute
      // description components.  Each attribute description component will
      // itself be a sequence of octet strings, where the first one is the
      // token, the second is the attribute name, and all remaining elements are
      // the attribute options.
      ArrayList<ASN1Element> adElements =
           new ArrayList<ASN1Element>(atDecodeMap.size());
      for (ByteArray token : atDecodeMap.keySet())
@@ -362,9 +340,8 @@
      writer.writeElement(new ASN1Sequence(adElements));
      // The fourth element in the file should be an integer element
      // that holds the value to use to initialize the attribute
      // description counter.
      // The fourth element in the file should be an integer element that holds
      // the value to use to initialize the attribute description counter.
      writer.writeElement(new ASN1Integer(adCounter.get()));
@@ -375,8 +352,7 @@
      if (liveFile.exists())
      {
        File saveFile =
                  new File(liveFile.getAbsolutePath() + ".save");
        File saveFile = new File(liveFile.getAbsolutePath() + ".save");
        if (saveFile.exists())
        {
          saveFile.delete();
@@ -392,12 +368,10 @@
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      Message message =
        ERR_COMPRESSEDSCHEMA_CANNOT_WRITE_UPDATED_DATA.
            get(stackTraceToSingleLineString(e));
      throw new DirectoryException(
                     DirectoryServer.getServerErrorResultCode(),
                     message, e);
      Message message = ERR_COMPRESSEDSCHEMA_CANNOT_WRITE_UPDATED_DATA.get(
                             stackTraceToSingleLineString(e));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message, e);
    }
    finally
    {
@@ -421,39 +395,25 @@
  /**
   * Encodes the provided set of object classes to a byte array.  If
   * the same set had been previously encoded, then the cached value
   * will be used.  Otherwise, a new value will be created.
   *
   * @param  objectClasses  The set of object classes for which to
   *                        retrieve the corresponding byte array
   *                        token.
   *
   * @return  A byte array containing the identifier assigned to the
   *          object class set.
   *
   * @throws  DirectoryException  If a problem occurs while attempting
   *                              to determine the appropriate
   *                              identifier.
   * {@inheritDoc}
   */
  public static byte[]
         encodeObjectClasses(Map<ObjectClass,String> objectClasses)
  @Override()
  public byte[] encodeObjectClasses(Map<ObjectClass,String> objectClasses)
         throws DirectoryException
  {
    ByteArray encodedClasses =
                    instance.ocEncodeMap.get(objectClasses);
    ByteArray encodedClasses = ocEncodeMap.get(objectClasses);
    if (encodedClasses == null)
    {
      synchronized (instance.ocEncodeMap)
      synchronized (ocEncodeMap)
      {
        int setValue = instance.ocCounter.getAndIncrement();
        int setValue = ocCounter.getAndIncrement();
        byte[] array = encodeInt(setValue);
        encodedClasses = new ByteArray(array);
        instance.ocEncodeMap.put(objectClasses, encodedClasses);
        instance.ocDecodeMap.put(encodedClasses, objectClasses);
        ocEncodeMap.put(objectClasses, encodedClasses);
        ocDecodeMap.put(encodedClasses, objectClasses);
        instance.save();
        save();
        return array;
      }
    }
@@ -466,30 +426,21 @@
  /**
   * Decodes an object class set from the provided byte array.
   *
   * @param  encodedObjectClasses  The byte array containing the
   *                               object class set identifier.
   *
   * @return  The decoded object class set.
   *
   * @throws  DirectoryException  If the provided byte array cannot be
   *                              decoded as an object class set.
   * {@inheritDoc}
   */
  public static Map<ObjectClass,String>
         decodeObjectClasses(byte[] encodedObjectClasses)
  @Override()
  public Map<ObjectClass,String> decodeObjectClasses(
                                      byte[] encodedObjectClasses)
         throws DirectoryException
  {
    ByteArray byteArray = new ByteArray(encodedObjectClasses);
    Map<ObjectClass,String> ocMap =
         instance.ocDecodeMap.get(byteArray);
    Map<ObjectClass,String> ocMap = ocDecodeMap.get(byteArray);
    if (ocMap == null)
    {
      Message message = ERR_COMPRESSEDSCHEMA_UNKNOWN_OC_TOKEN.get(
          bytesToHex(encodedObjectClasses));
      throw new DirectoryException(
                     DirectoryServer.getServerErrorResultCode(),
                     message);
                             bytesToHex(encodedObjectClasses));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message);
    }
    else
    {
@@ -500,42 +451,33 @@
  /**
   * Encodes the information in the provided attribute to a byte
   * array.
   *
   * @param  attribute  The attribute to be encoded.
   *
   * @return  An encoded representation of the provided attribute.
   *
   * @throws  DirectoryException  If a problem occurs while attempting
   *                              to determine the appropriate
   *                              identifier.
   * {@inheritDoc}
   */
  public static byte[] encodeAttribute(Attribute attribute)
  @Override()
  public byte[] encodeAttribute(Attribute attribute)
         throws DirectoryException
  {
    AttributeType type = attribute.getAttributeType();
    LinkedHashSet<String> options = attribute.getOptions();
    ConcurrentHashMap<LinkedHashSet<String>,ByteArray> map =
         instance.adEncodeMap.get(type);
         adEncodeMap.get(type);
    if (map == null)
    {
      byte[] array;
      synchronized (instance.adEncodeMap)
      synchronized (adEncodeMap)
      {
        map = new ConcurrentHashMap<LinkedHashSet<String>,
                       ByteArray>(1);
        map = new ConcurrentHashMap<LinkedHashSet<String>,ByteArray>(1);
        int intValue = instance.adCounter.getAndIncrement();
        int intValue = adCounter.getAndIncrement();
        array = encodeInt(intValue);
        ByteArray byteArray = new ByteArray(array);
        map.put(options,byteArray);
        instance.adEncodeMap.put(type, map);
        instance.atDecodeMap.put(byteArray, type);
        instance.aoDecodeMap.put(byteArray, options);
        instance.save();
        adEncodeMap.put(type, map);
        atDecodeMap.put(byteArray, type);
        aoDecodeMap.put(byteArray, options);
        save();
      }
      return encodeAttribute(array, attribute);
@@ -548,14 +490,14 @@
        byte[] array;
        synchronized (map)
        {
          int intValue = instance.adCounter.getAndIncrement();
          int intValue = adCounter.getAndIncrement();
          array = encodeInt(intValue);
          byteArray = new ByteArray(array);
          map.put(options,byteArray);
          instance.atDecodeMap.put(byteArray, type);
          instance.aoDecodeMap.put(byteArray, options);
          instance.save();
          atDecodeMap.put(byteArray, type);
          aoDecodeMap.put(byteArray, options);
          save();
        }
        return encodeAttribute(array, attribute);
@@ -579,8 +521,7 @@
   *
   * @return  An encoded representation of the provided attribute.
   */
  private static byte[] encodeAttribute(byte[] adArray,
                                        Attribute attribute)
  private byte[] encodeAttribute(byte[] adArray, Attribute attribute)
  {
    LinkedHashSet<AttributeValue> values = attribute.getValues();
    int totalValuesLength = 0;
@@ -624,26 +565,15 @@
  /**
   * Decodes the contents of the provided array as an attribute.
   *
   * @param  encodedEntry  The byte array containing the encoded
   *                       entry.
   * @param  startPos      The position within the array of the first
   *                       byte for the attribute to decode.
   * @param  length        The number of bytes contained in the
   *                       encoded attribute.
   *
   * @return  The decoded attribute.
   *
   * @throws  DirectoryException  If the attribute could not be
   *                              decoded properly for some reason.
   * {@inheritDoc}
   */
  public static Attribute decodeAttribute(byte[] encodedEntry,
                                          int startPos, int length)
  @Override()
  public Attribute decodeAttribute(byte[] encodedEntry, int startPos,
                                   int length)
         throws DirectoryException
  {
    // Figure out how many bytes are in the token that is the
    // placeholder for the attribute description.
    // Figure out how many bytes are in the token that is the placeholder for
    // the attribute description.
    int pos = startPos;
    int adArrayLength = encodedEntry[pos] & 0x7F;
    if (adArrayLength != encodedEntry[pos++])
@@ -652,27 +582,24 @@
      adArrayLength = 0;
      for (int i=0; i < numLengthBytes; i++, pos++)
      {
        adArrayLength =
             (adArrayLength << 8) | (encodedEntry[pos] & 0xFF);
        adArrayLength = (adArrayLength << 8) | (encodedEntry[pos] & 0xFF);
      }
    }
    // Get the attribute description token and make sure it resolves
    // to an attribute type and option set.
    // Get the attribute description token and make sure it resolves to an
    // attribute type and option set.
    ByteArray adArray = new ByteArray(new byte[adArrayLength]);
    System.arraycopy(encodedEntry, pos, adArray.array(), 0,
                     adArrayLength);
    System.arraycopy(encodedEntry, pos, adArray.array(), 0, adArrayLength);
    pos += adArrayLength;
    AttributeType attrType = instance.atDecodeMap.get(adArray);
    LinkedHashSet<String> options = instance.aoDecodeMap.get(adArray);
    AttributeType attrType = atDecodeMap.get(adArray);
    LinkedHashSet<String> options = aoDecodeMap.get(adArray);
    if ((attrType == null) || (options == null))
    {
      Message message = ERR_COMPRESSEDSCHEMA_UNRECOGNIZED_AD_TOKEN.
          get(bytesToHex(adArray.array()));
      throw new DirectoryException(
                     DirectoryServer.getServerErrorResultCode(),
                     message);
      Message message = ERR_COMPRESSEDSCHEMA_UNRECOGNIZED_AD_TOKEN.get(
                             bytesToHex(adArray.array()));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message);
    }
@@ -701,20 +628,17 @@
        valueLength = 0;
        for (int j=0; j < valueLengthBytes; j++, pos++)
        {
          valueLength =
               (valueLength << 8) | (encodedEntry[pos] & 0xFF);
          valueLength = (valueLength << 8) | (encodedEntry[pos] & 0xFF);
        }
      }
      byte[] valueBytes = new byte[valueLength];
      System.arraycopy(encodedEntry, pos, valueBytes, 0, valueLength);
      pos += valueLength;
      values.add(new AttributeValue(attrType,
                                    new ASN1OctetString(valueBytes)));
      values.add(new AttributeValue(attrType, new ASN1OctetString(valueBytes)));
    }
    return new Attribute(attrType, attrType.getPrimaryName(), options,
                         values);
    return new Attribute(attrType, attrType.getPrimaryName(), options, values);
  }
@@ -726,7 +650,7 @@
   *
   * @return  The byte array containing the encoded int value.
   */
  private static byte[] encodeInt(int intValue)
  private byte[] encodeInt(int intValue)
  {
    byte[] array;
    if (intValue <= 0xFF)