From e038f697b622bbe1b55fe661f1fc47bd48cf6477 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 25 Jan 2013 17:41:37 +0000
Subject: [PATCH] Fix OPENDJ-169: Modifying an existing object class definition requires server restart

---
 opendj-sdk/opends/src/server/org/opends/server/types/LDAPSyntaxDescription.java                          |   20 
 opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java                                 |   18 
 opendj-sdk/opends/src/server/org/opends/server/types/SchemaFileElement.java                              |   34 
 opendj-sdk/opends/src/server/org/opends/server/types/Schema.java                                         |   73 +
 opendj-sdk/opends/src/server/org/opends/server/types/NameForm.java                                       |   18 
 opendj-sdk/opends/src/server/org/opends/server/types/ObjectClass.java                                    |   50 
 opendj-sdk/opends/src/server/org/opends/server/types/DITStructureRule.java                               |   19 
 opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JECompressedSchema.java                      |  753 +++++-------------
 opendj-sdk/opends/src/server/org/opends/server/types/MatchingRuleUse.java                                |   19 
 opendj-sdk/opends/src/server/org/opends/server/types/DITContentRule.java                                 |   19 
 opendj-sdk/opends/src/server/org/opends/server/api/CompressedSchema.java                                 |  635 ++++++++++++++-
 opendj-sdk/opends/src/server/org/opends/server/types/AttributeType.java                                  |   51 
 opendj-sdk/opends/src/server/org/opends/server/types/DirectoryConfig.java                                |    3 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java |  115 ++
 opendj-sdk/opends/src/server/org/opends/server/core/DefaultCompressedSchema.java                         |  620 ++++-----------
 15 files changed, 1,220 insertions(+), 1,227 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/CompressedSchema.java b/opendj-sdk/opends/src/server/org/opends/server/api/CompressedSchema.java
index 7d30c84..f18490a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/api/CompressedSchema.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/CompressedSchema.java
@@ -23,95 +23,614 @@
  *
  *
  *      Copyright 2009 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.api;
 
 
 
-import java.util.Map;
+import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.util.StaticUtils.toLowerCase;
 
-import org.opends.server.types.*;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.opends.messages.Message;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeBuilder;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.AttributeValues;
+import org.opends.server.types.Attributes;
+import org.opends.server.types.ByteSequenceReader;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ByteStringBuilder;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.ObjectClass;
+
 
 
 /**
- * This class provides a utility for interacting with compressed
- * representations of schema elements.
+ * This class provides a utility for interacting with compressed representations
+ * of schema elements. The default implementation does not persist encoded
+ * attributes and object classes.
  */
 @org.opends.server.types.PublicAPI(
-     stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
-     mayInstantiate=false,
-     mayExtend=true,
-     mayInvoke=false)
-public abstract class CompressedSchema
+    stability = org.opends.server.types.StabilityLevel.UNCOMMITTED,
+    mayInstantiate = false,
+    mayExtend = true,
+    mayInvoke = false)
+public class CompressedSchema
 {
+  // Maps attribute description to ID.
+  private final List<Entry<AttributeType, Set<String>>> adDecodeMap;
+
+  // Maps ID to attribute description.
+  private final Map<Entry<AttributeType, Set<String>>, Integer> adEncodeMap;
+
+  // The map between encoded representations and object class sets.
+  private final List<Map<ObjectClass, String>> ocDecodeMap;
+
+  // The map between object class sets and encoded representations.
+  private final Map<Map<ObjectClass, String>, Integer> ocEncodeMap;
+
+
+
   /**
-   * 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  entryBuffer   The buffer to encode the object classes to.
-   * @param  objectClasses The set of object classes for which to
-   *                       retrieve the corresponding byte array
-   *                       token.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting
-   *                              to determine the appropriate
-   *                              identifier.
+   * Creates a new empty instance of this compressed schema.
    */
-  public abstract void
-         encodeObjectClasses(ByteStringBuilder entryBuffer,
-                             Map<ObjectClass,String> objectClasses)
-         throws DirectoryException;
+  public CompressedSchema()
+  {
+    adDecodeMap = new CopyOnWriteArrayList<Entry<AttributeType, Set<String>>>();
+    ocDecodeMap = new CopyOnWriteArrayList<Map<ObjectClass, String>>();
+    adEncodeMap = new ConcurrentHashMap<Entry<AttributeType, Set<String>>,
+                                        Integer>();
+    ocEncodeMap = new ConcurrentHashMap<Map<ObjectClass, String>, Integer>();
+  }
+
+
+
+  /**
+   * Decodes the contents of the provided array as an attribute at the current
+   * position.
+   *
+   * @param reader
+   *          The byte string reader containing the encoded entry.
+   * @return The decoded attribute.
+   * @throws DirectoryException
+   *           If the attribute could not be decoded properly for some reason.
+   */
+  public final Attribute decodeAttribute(final ByteSequenceReader reader)
+      throws DirectoryException
+  {
+    // First decode the encoded attribute description id.
+    final int length = reader.getBERLength();
+    final byte[] idBytes = new byte[length];
+    reader.get(idBytes);
+    final int id = decodeId(idBytes);
+
+    // Look up the attribute description.
+    Entry<AttributeType, Set<String>> ad = adDecodeMap.get(id);
+    if (ad == null)
+    {
+      final Message message = ERR_COMPRESSEDSCHEMA_UNRECOGNIZED_AD_TOKEN
+          .get(String.valueOf(id));
+      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+          message);
+    }
+
+    // Before returning the attribute, make sure that the attribute type is not
+    // stale.
+    AttributeType attrType = ad.getKey();
+    Set<String> options = ad.getValue();
+    if (attrType.isDirty())
+    {
+      ad = loadAttribute(idBytes, attrType.getNameOrOID(), options);
+      attrType = ad.getKey();
+      options = ad.getValue();
+    }
+
+    // Determine the number of values for the attribute.
+    final int numValues = reader.getBERLength();
+
+    // For the common case of a single value with no options, generate
+    // less garbage.
+    if (numValues == 1 && options.isEmpty())
+    {
+      final int valueLength = reader.getBERLength();
+      final ByteString valueBytes = reader.getByteSequence(valueLength)
+          .toByteString();
+      return Attributes.create(attrType,
+          AttributeValues.create(attrType, valueBytes));
+    }
+    else
+    {
+      // Read the appropriate number of values.
+      final AttributeBuilder builder = new AttributeBuilder(attrType);
+      builder.setOptions(options);
+      builder.setInitialCapacity(numValues);
+      for (int i = 0; i < numValues; i++)
+      {
+        final int valueLength = reader.getBERLength();
+        final ByteString valueBytes = reader.getByteSequence(valueLength)
+            .toByteString();
+        builder.add(AttributeValues.create(attrType, valueBytes));
+      }
+      return builder.toAttribute();
+    }
+  }
 
 
 
   /**
    * Decodes an object class set from the provided byte string.
    *
-   * @param  entryBuffer         The byte string 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.
+   * @param reader
+   *          The byte string reader containing the object class set identifier.
+   * @return The decoded object class set.
+   * @throws DirectoryException
+   *           If the provided byte string reader cannot be decoded as an object
+   *           class set.
    */
-  public abstract Map<ObjectClass,String>
-         decodeObjectClasses(ByteSequenceReader entryBuffer)
-         throws DirectoryException;
+  public final Map<ObjectClass, String> decodeObjectClasses(
+      final ByteSequenceReader reader) throws DirectoryException
+  {
+    // First decode the encoded object class id.
+    final int length = reader.getBERLength();
+    final byte[] idBytes = new byte[length];
+    reader.get(idBytes);
+    final int id = decodeId(idBytes);
+
+    // Look up the object classes.
+    final Map<ObjectClass, String> ocMap = ocDecodeMap.get(id);
+    if (ocMap != null)
+    {
+      // Before returning the object classes, make sure that none of them are
+      // stale.
+      for (final ObjectClass oc : ocMap.keySet())
+      {
+        if (oc.isDirty())
+        {
+          // Found at least one object class which is dirty so refresh them.
+          return loadObjectClasses(idBytes, ocMap.values());
+        }
+      }
+      return ocMap;
+    }
+    else
+    {
+      final Message message = ERR_COMPRESSEDSCHEMA_UNKNOWN_OC_TOKEN.get(String
+          .valueOf(id));
+      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+          message);
+    }
+  }
 
 
 
   /**
-   * Encodes the information in the provided attribute to a byte
-   * array.
+   * Encodes the information in the provided attribute to a byte array.
    *
-   * @param  entryBuffer The buffer to encode the attribute to.
-   * @param  attribute   The attribute to be encoded.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting
-   *                              to determine the appropriate
-   *                              identifier.
+   * @param builder
+   *          The buffer to encode the attribute to.
+   * @param attribute
+   *          The attribute to be encoded.
+   * @throws DirectoryException
+   *           If a problem occurs while attempting to determine the appropriate
+   *           identifier.
    */
-  public abstract void encodeAttribute(ByteStringBuilder entryBuffer,
-                                       Attribute attribute)
-         throws DirectoryException;
+  public final void encodeAttribute(final ByteStringBuilder builder,
+      final Attribute attribute) throws DirectoryException
+  {
+    // Re-use or allocate a new ID.
+    final AttributeType type = attribute.getAttributeType();
+    final Set<String> options = attribute.getOptions();
+    final Entry<AttributeType, Set<String>> ad =
+        new SimpleImmutableEntry<AttributeType, Set<String>>(type, options);
+
+    // Use double checked locking to avoid lazy registration races.
+    Integer id = adEncodeMap.get(ad);
+    if (id == null)
+    {
+      synchronized (adEncodeMap)
+      {
+        id = adEncodeMap.get(ad);
+        if (id == null)
+        {
+          id = adDecodeMap.size();
+          adDecodeMap.add(ad);
+          adEncodeMap.put(ad, id);
+          storeAttribute(encodeId(id), type.getNameOrOID(), options);
+        }
+      }
+    }
+
+    // Encode the attribute.
+    final byte[] idBytes = encodeId(id);
+    builder.appendBERLength(idBytes.length);
+    builder.append(idBytes);
+    builder.appendBERLength(attribute.size());
+    for (final AttributeValue v : attribute)
+    {
+      builder.appendBERLength(v.getValue().length());
+      builder.append(v.getValue());
+    }
+  }
 
 
 
   /**
-   * Decodes the contents of the provided array as an attribute at the
-   * current position.
+   * 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  entryBuffer   The byte array containing the encoded
-   *                       entry.
-   *
-   * @return  The decoded attribute.
-   *
-   * @throws  DirectoryException  If the attribute could not be
-   *                              decoded properly for some reason.
+   * @param builder
+   *          The buffer to encode the object classes to.
+   * @param objectClasses
+   *          The set of object classes for which to retrieve the corresponding
+   *          byte array token.
+   * @throws DirectoryException
+   *           If a problem occurs while attempting to determine the appropriate
+   *           identifier.
    */
-  public abstract Attribute decodeAttribute(
-      ByteSequenceReader entryBuffer) throws DirectoryException;
+  public final void encodeObjectClasses(final ByteStringBuilder builder,
+      final Map<ObjectClass, String> objectClasses) throws DirectoryException
+  {
+    // Re-use or allocate a new ID.
+    // Use double checked locking to avoid lazy registration races.
+    Integer id = ocEncodeMap.get(objectClasses);
+    if (id == null)
+    {
+      synchronized (ocEncodeMap)
+      {
+        id = ocEncodeMap.get(objectClasses);
+        if (id == null)
+        {
+          id = ocDecodeMap.size();
+          ocDecodeMap.add(objectClasses);
+          ocEncodeMap.put(objectClasses, id);
+          storeObjectClasses(encodeId(id), objectClasses.values());
+        }
+      }
+    }
+
+    // Encode the object classes.
+    final byte[] idBytes = encodeId(id);
+    builder.appendBERLength(idBytes.length);
+    builder.append(idBytes);
+  }
+
+
+
+  /**
+   * Returns a view of the encoded attributes in this compressed schema which
+   * can be used for saving the entire content to disk. The iterator returned by
+   * this method is not thread safe.
+   *
+   * @return A view of the encoded attributes in this compressed schema.
+   */
+  protected final Iterable<Entry<byte[],
+                                 Entry<String,
+                                       Collection<String>>>> getAllAttributes()
+  {
+    return new Iterable<Entry<byte[], Entry<String, Collection<String>>>>()
+    {
+
+      @Override
+      public Iterator<Entry<byte[],
+                            Entry<String, Collection<String>>>> iterator()
+      {
+        return new Iterator<Entry<byte[], Entry<String, Collection<String>>>>()
+        {
+          private int id = 0;
+
+
+
+          @Override
+          public boolean hasNext()
+          {
+            return id < adDecodeMap.size();
+          }
+
+
+
+          @Override
+          public Entry<byte[], Entry<String, Collection<String>>> next()
+          {
+            final byte[] encodedAttribute = encodeId(id);
+            final Entry<AttributeType, Set<String>> ad = adDecodeMap.get(id++);
+            return new SimpleImmutableEntry<byte[],
+                                            Entry<String, Collection<String>>>(
+                encodedAttribute,
+                new SimpleImmutableEntry<String, Collection<String>>(ad
+                    .getKey().getNameOrOID(), ad.getValue()));
+          }
+
+
+
+          @Override
+          public void remove()
+          {
+            throw new UnsupportedOperationException();
+          }
+        };
+      }
+    };
+  }
+
+
+
+  /**
+   * Returns a view of the encoded object classes in this compressed schema
+   * which can be used for saving the entire content to disk. The iterator
+   * returned by this method is not thread safe.
+   *
+   * @return A view of the encoded object classes in this compressed schema.
+   */
+  protected final Iterable<Entry<byte[],
+                                 Collection<String>>> getAllObjectClasses()
+  {
+    return new Iterable<Entry<byte[], Collection<String>>>()
+    {
+
+      @Override
+      public Iterator<Entry<byte[], Collection<String>>> iterator()
+      {
+        return new Iterator<Map.Entry<byte[], Collection<String>>>()
+        {
+          private int id = 0;
+
+
+
+          @Override
+          public boolean hasNext()
+          {
+            return id < ocDecodeMap.size();
+          }
+
+
+
+          @Override
+          public Entry<byte[], Collection<String>> next()
+          {
+            final byte[] encodedObjectClasses = encodeId(id);
+            final Map<ObjectClass, String> ocMap = ocDecodeMap.get(id++);
+            return new SimpleImmutableEntry<byte[], Collection<String>>(
+                encodedObjectClasses, ocMap.values());
+          }
+
+
+
+          @Override
+          public void remove()
+          {
+            throw new UnsupportedOperationException();
+          }
+        };
+      }
+    };
+  }
+
+
+
+  /**
+   * Loads an encoded attribute into this compressed schema. This method may
+   * called by implementations during initialization when loading content from
+   * disk.
+   *
+   * @param encodedAttribute
+   *          The encoded attribute description.
+   * @param attributeName
+   *          The user provided attribute type name.
+   * @param attributeOptions
+   *          The non-null but possibly empty set of attribute options.
+   * @return The attribute type description.
+   */
+  protected final Entry<AttributeType, Set<String>> loadAttribute(
+      final byte[] encodedAttribute, final String attributeName,
+      final Collection<String> attributeOptions)
+  {
+    final AttributeType type = DirectoryServer.getAttributeType(
+        toLowerCase(attributeName), true);
+    final Set<String> options;
+    switch (attributeOptions.size())
+    {
+    case 0:
+      options = Collections.emptySet();
+      break;
+    case 1:
+      options = Collections.singleton(attributeOptions.iterator().next());
+      break;
+    default:
+      options = new LinkedHashSet<String>(attributeOptions);
+      break;
+    }
+    final Entry<AttributeType, Set<String>> ad =
+        new SimpleImmutableEntry<AttributeType, Set<String>>(type, options);
+    final int id = decodeId(encodedAttribute);
+    synchronized (adEncodeMap)
+    {
+      adEncodeMap.put(ad, id);
+      if (id < adDecodeMap.size())
+      {
+        adDecodeMap.set(id, ad);
+      }
+      else
+      {
+        // Grow the decode array.
+        while (id > adDecodeMap.size())
+        {
+          adDecodeMap.add(null);
+        }
+        adDecodeMap.add(ad);
+      }
+    }
+    return ad;
+  }
+
+
+
+  /**
+   * Loads an encoded object class into this compressed schema. This method may
+   * called by implementations during initialization when loading content from
+   * disk.
+   *
+   * @param encodedObjectClasses
+   *          The encoded object classes.
+   * @param objectClassNames
+   *          The user provided set of object class names.
+   * @return The object class set.
+   */
+  protected final Map<ObjectClass, String> loadObjectClasses(
+      final byte[] encodedObjectClasses,
+      final Collection<String> objectClassNames)
+  {
+    final LinkedHashMap<ObjectClass, String> ocMap =
+        new LinkedHashMap<ObjectClass, String>(objectClassNames.size());
+    for (final String name : objectClassNames)
+    {
+      final String lowerName = toLowerCase(name);
+      final ObjectClass oc = DirectoryServer.getObjectClass(lowerName, true);
+      ocMap.put(oc, name);
+    }
+    final int id = decodeId(encodedObjectClasses);
+    synchronized (ocEncodeMap)
+    {
+      ocEncodeMap.put(ocMap, id);
+      if (id < ocDecodeMap.size())
+      {
+        ocDecodeMap.set(id, ocMap);
+      }
+      else
+      {
+        // Grow the decode array.
+        while (id > ocDecodeMap.size())
+        {
+          ocDecodeMap.add(null);
+        }
+        ocDecodeMap.add(ocMap);
+      }
+    }
+    return ocMap;
+  }
+
+
+
+  /**
+   * Persists the provided encoded attribute. The default implementation is to
+   * do nothing. Calls to this method are synchronized, so implementations can
+   * assume that this method is not being called by other threads. Note that
+   * this method is not thread-safe with respect to
+   * {@link #storeObjectClasses(byte[], Collection)}.
+   *
+   * @param encodedAttribute
+   *          The encoded attribute description.
+   * @param attributeName
+   *          The user provided attribute type name.
+   * @param attributeOptions
+   *          The non-null but possibly empty set of attribute options.
+   * @throws DirectoryException
+   *           If an error occurred while persisting the encoded attribute.
+   */
+  protected void storeAttribute(final byte[] encodedAttribute,
+      final String attributeName, final Collection<String> attributeOptions)
+      throws DirectoryException
+  {
+    // Do nothing by default.
+  }
+
+
+
+  /**
+   * Persists the provided encoded object classes. The default implementation is
+   * to do nothing. Calls to this method are synchronized, so implementations
+   * can assume that this method is not being called by other threads. Note that
+   * this method is not thread-safe with respect to
+   * {@link #storeAttribute(byte[], String, Collection)}.
+   *
+   * @param encodedObjectClasses
+   *          The encoded object classes.
+   * @param objectClassNames
+   *          The user provided set of object class names.
+   * @throws DirectoryException
+   *           If an error occurred while persisting the encoded object classes.
+   */
+  protected void storeObjectClasses(final byte[] encodedObjectClasses,
+      final Collection<String> objectClassNames) throws DirectoryException
+  {
+    // Do nothing by default.
+  }
+
+
+
+  /**
+   * Decodes the provided encoded schema element ID.
+   *
+   * @param idBytes
+   *          The encoded schema element ID.
+   * @return The schema element ID.
+   */
+  private int decodeId(final byte[] idBytes)
+  {
+    int id = 0;
+    for (final byte b : idBytes)
+    {
+      id <<= 8;
+      id |= (b & 0xFF);
+    }
+    return id - 1; // Subtract 1 to compensate for old behavior.
+  }
+
+
+
+  /**
+   * Encodes the provided schema element ID.
+   *
+   * @param id
+   *          The schema element ID.
+   * @return The encoded schema element ID.
+   */
+  private byte[] encodeId(final int id)
+  {
+    final int value = id + 1; // Add 1 to compensate for old behavior.
+    final byte[] idBytes;
+    if (value <= 0xFF)
+    {
+      idBytes = new byte[1];
+      idBytes[0] = (byte) (value & 0xFF);
+    }
+    else if (value <= 0xFFFF)
+    {
+      idBytes = new byte[2];
+      idBytes[0] = (byte) ((value >> 8) & 0xFF);
+      idBytes[1] = (byte) (value & 0xFF);
+    }
+    else if (value <= 0xFFFFFF)
+    {
+      idBytes = new byte[3];
+      idBytes[0] = (byte) ((value >> 16) & 0xFF);
+      idBytes[1] = (byte) ((value >> 8) & 0xFF);
+      idBytes[2] = (byte) (value & 0xFF);
+    }
+    else
+    {
+      idBytes = new byte[4];
+      idBytes[0] = (byte) ((value >> 24) & 0xFF);
+      idBytes[1] = (byte) ((value >> 16) & 0xFF);
+      idBytes[2] = (byte) ((value >> 8) & 0xFF);
+      idBytes[3] = (byte) (value & 0xFF);
+    }
+    return idBytes;
+  }
 }
-
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JECompressedSchema.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JECompressedSchema.java
index 5e0365e..9f790bb 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JECompressedSchema.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JECompressedSchema.java
@@ -23,29 +23,33 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.backends.jeb;
 
 
 
 import static org.opends.messages.JebMessages.*;
-import static org.opends.server.loggers.debug.DebugLogger.*;
-import static org.opends.server.util.StaticUtils.*;
+import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import static org.opends.server.loggers.debug.DebugLogger.getTracer;
 
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.io.IOException;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
 
 import org.opends.messages.Message;
 import org.opends.server.api.CompressedSchema;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.protocols.asn1.*;
-import org.opends.server.types.*;
+import org.opends.server.protocols.asn1.ASN1;
+import org.opends.server.protocols.asn1.ASN1Exception;
+import org.opends.server.protocols.asn1.ASN1Reader;
+import org.opends.server.protocols.asn1.ASN1Writer;
+import org.opends.server.types.ByteStringBuilder;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.InitializationException;
 
 import com.sleepycat.je.Cursor;
 import com.sleepycat.je.Database;
@@ -63,129 +67,190 @@
  * This class provides a compressed schema implementation whose definitions are
  * stored in a Berkeley DB JE database.
  */
-public final class JECompressedSchema
-       extends CompressedSchema
+public final class JECompressedSchema extends CompressedSchema
 {
   /**
-   * The tracer object for the debug logger.
-   */
-  private static final DebugTracer TRACER = getTracer();
-
-
-
-  /**
    * The name of the database used to store compressed attribute description
    * definitions.
    */
-  public static final String DB_NAME_AD = "compressed_attributes";
-
-
+  private static final String DB_NAME_AD = "compressed_attributes";
 
   /**
    * The name of the database used to store compressed object class set
    * definitions.
    */
-  public static final String DB_NAME_OC = "compressed_object_classes";
+  private static final String DB_NAME_OC = "compressed_object_classes";
 
-
-
-  // The counter used for attribute descriptions.
-  private AtomicInteger adCounter;
-
-  // The counter used for object class sets.
-  private AtomicInteger ocCounter;
-
-  // The map between encoded representations and attribute types.
-  private ConcurrentHashMap<ByteSequence,AttributeType> atDecodeMap;
-
-  // The map between encoded representations and attribute options.
-  private ConcurrentHashMap<ByteSequence,Set<String>> aoDecodeMap;
-
-  // The map between encoded representations and object class sets.
-  private ConcurrentHashMap<ByteSequence,Map<ObjectClass,String>> ocDecodeMap;
-
-  // The map between attribute descriptions and their encoded
-  // representations.
-  private final ConcurrentHashMap<AttributeType,
-                ConcurrentHashMap<Set<String>, ByteSequence>> adEncodeMap;
-
-  // The map between object class sets and encoded representations.
-  private final ConcurrentHashMap<Map<ObjectClass,String>,
-      ByteSequence> ocEncodeMap;
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
 
   // The compressed attribute description schema database.
   private Database adDatabase;
 
-  // The compresesd object class set schema database.
-  private Database ocDatabase;
-
   // The environment in which the databases are held.
   private Environment environment;
 
-  private final ByteStringBuilder storeWriterBuffer;
-  private final ASN1Writer storeWriter;
+  // The compresesd object class set schema database.
+  private Database ocDatabase;
+
+  private final ByteStringBuilder storeAttributeWriterBuffer =
+      new ByteStringBuilder();
+  private final ASN1Writer storeAttributeWriter = ASN1
+      .getWriter(storeAttributeWriterBuffer);
+  private final ByteStringBuilder storeObjectClassesWriterBuffer =
+      new ByteStringBuilder();
+  private final ASN1Writer storeObjectClassesWriter = ASN1
+      .getWriter(storeObjectClassesWriterBuffer);
 
 
 
   /**
    * Creates a new instance of this JE compressed schema manager.
    *
-   * @param  environment  A reference to the database environment in which the
-   *                      databases will be held.
-   *
-   * @throws DatabaseException       If a database problem occurs while loading
-   *                                 the compressed schema definitions from the
-   *                                 database.
-   * @throws InitializationException If an error occurs while loading and
-   *                                 processing the compressed schema
-   *                                 definitions.
+   * @param environment
+   *          A reference to the database environment in which the databases
+   *          will be held.
+   * @throws DatabaseException
+   *           If a database problem occurs while loading the compressed schema
+   *           definitions from the database.
+   * @throws InitializationException
+   *           If an error occurs while loading and processing the compressed
+   *           schema definitions.
    */
-  public JECompressedSchema(Environment environment)
-         throws DatabaseException, InitializationException
+  public JECompressedSchema(final Environment environment)
+      throws DatabaseException, InitializationException
   {
     this.environment = environment;
-
-    atDecodeMap = new ConcurrentHashMap<ByteSequence,AttributeType>();
-    aoDecodeMap = new ConcurrentHashMap<ByteSequence,Set<String>>();
-    ocDecodeMap = new ConcurrentHashMap<ByteSequence,Map<ObjectClass,String>>();
-    adEncodeMap =
-         new ConcurrentHashMap<AttributeType,
-                  ConcurrentHashMap<Set<String>, ByteSequence>>();
-    ocEncodeMap = new ConcurrentHashMap<Map<ObjectClass,String>,
-        ByteSequence>();
-
-    adCounter = new AtomicInteger(1);
-    ocCounter = new AtomicInteger(1);
-
-    storeWriterBuffer = new ByteStringBuilder();
-    storeWriter = ASN1.getWriter(storeWriterBuffer);
-
     load();
   }
 
 
 
   /**
+   * Closes the databases and releases any resources held by this compressed
+   * schema manager.
+   */
+  public void close()
+  {
+    try
+    {
+      adDatabase.sync();
+    }
+    catch (final Exception e)
+    {
+      // Ignore.
+    }
+
+    try
+    {
+      adDatabase.close();
+    }
+    catch (final Exception e)
+    {
+      // Ignore.
+    }
+
+    try
+    {
+      ocDatabase.sync();
+    }
+    catch (final Exception e)
+    {
+      // Ignore.
+    }
+
+    try
+    {
+      ocDatabase.close();
+    }
+    catch (final Exception e)
+    {
+      // Ignore.
+    }
+
+    adDatabase = null;
+    ocDatabase = null;
+    environment = null;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void storeAttribute(final byte[] encodedAttribute,
+      final String attributeName, final Collection<String> attributeOptions)
+      throws DirectoryException
+  {
+    try
+    {
+      storeAttributeWriterBuffer.clear();
+      storeAttributeWriter.writeStartSequence();
+      storeAttributeWriter.writeOctetString(attributeName);
+      for (final String option : attributeOptions)
+      {
+        storeAttributeWriter.writeOctetString(option);
+      }
+      storeAttributeWriter.writeEndSequence();
+      store(adDatabase, encodedAttribute, storeAttributeWriterBuffer);
+    }
+    catch (final IOException e)
+    {
+      // TODO: Shouldn't happen but should log a message
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void storeObjectClasses(final byte[] encodedObjectClasses,
+      final Collection<String> objectClassNames) throws DirectoryException
+  {
+    try
+    {
+      storeObjectClassesWriterBuffer.clear();
+      storeObjectClassesWriter.writeStartSequence();
+      for (final String ocName : objectClassNames)
+      {
+        storeObjectClassesWriter.writeOctetString(ocName);
+      }
+      storeObjectClassesWriter.writeEndSequence();
+      store(ocDatabase, encodedObjectClasses, storeObjectClassesWriterBuffer);
+    }
+    catch (final IOException e)
+    {
+      // TODO: Shouldn't happen but should log a message
+    }
+  }
+
+
+
+  /**
    * Loads the compressed schema information from the database.
    *
-   * @throws DatabaseException       If a database error occurs while
-   *                                 loading the definitions from the
-   *                                 database.
-   * @throws InitializationException If an error occurs while loading
-   *                                 and processing the definitions.
+   * @throws DatabaseException
+   *           If a database error occurs while loading the definitions from the
+   *           database.
+   * @throws InitializationException
+   *           If an error occurs while loading and processing the definitions.
    */
-  private void load()
-          throws DatabaseException, InitializationException
+  private void load() throws DatabaseException, InitializationException
   {
-    DatabaseConfig dbConfig = new DatabaseConfig();
+    final DatabaseConfig dbConfig = new DatabaseConfig();
 
-    if(environment.getConfig().getReadOnly())
+    if (environment.getConfig().getReadOnly())
     {
       dbConfig.setReadOnly(true);
       dbConfig.setAllowCreate(false);
       dbConfig.setTransactional(false);
     }
-    else if(!environment.getConfig().getTransactional())
+    else if (!environment.getConfig().getTransactional())
     {
       dbConfig.setAllowCreate(true);
       dbConfig.setTransactional(false);
@@ -201,53 +266,40 @@
     ocDatabase = environment.openDatabase(null, DB_NAME_OC, dbConfig);
 
     // Cursor through the object class database and load the object class set
-    // definitions.  At the same time, figure out the highest token value and
+    // definitions. At the same time, figure out the highest token value and
     // initialize the object class counter to one greater than that.
-    Cursor ocCursor = ocDatabase.openCursor(null, null);
-    int highestToken = 0;
-
+    final Cursor ocCursor = ocDatabase.openCursor(null, null);
     try
     {
-      DatabaseEntry keyEntry   = new DatabaseEntry();
-      DatabaseEntry valueEntry = new DatabaseEntry();
+      final DatabaseEntry keyEntry = new DatabaseEntry();
+      final DatabaseEntry valueEntry = new DatabaseEntry();
       OperationStatus status = ocCursor.getFirst(keyEntry, valueEntry,
-                                                 LockMode.READ_UNCOMMITTED);
+          LockMode.READ_UNCOMMITTED);
       while (status == OperationStatus.SUCCESS)
       {
-        byte[] tokenBytes = keyEntry.getData();
-        ByteString token = ByteString.wrap(tokenBytes);
-        highestToken = Math.max(highestToken, decodeInt(tokenBytes));
-
-        ASN1Reader reader =
-            ASN1.getReader(valueEntry.getData());
+        final byte[] encodedObjectClasses = keyEntry.getData();
+        final ASN1Reader reader = ASN1.getReader(valueEntry.getData());
         reader.readStartSequence();
-        LinkedHashMap<ObjectClass,String> ocMap =
-             new LinkedHashMap<ObjectClass,String>();
-        while(reader.hasNextElement())
+        final List<String> objectClassNames = new LinkedList<String>();
+        while (reader.hasNextElement())
         {
-          String ocName = reader.readOctetStringAsString();
-          String lowerName = toLowerCase(ocName);
-          ObjectClass oc = DirectoryServer.getObjectClass(lowerName, true);
-          ocMap.put(oc, ocName);
+          objectClassNames.add(reader.readOctetStringAsString());
         }
         reader.readEndSequence();
-
-        ocEncodeMap.put(ocMap, token);
-        ocDecodeMap.put(token, ocMap);
-
+        loadObjectClasses(encodedObjectClasses, objectClassNames);
         status = ocCursor.getNext(keyEntry, valueEntry,
-                                  LockMode.READ_UNCOMMITTED);
+            LockMode.READ_UNCOMMITTED);
       }
     }
-    catch (ASN1Exception ae)
+    catch (final ASN1Exception ae)
     {
       if (debugEnabled())
       {
         TRACER.debugCaught(DebugLogLevel.ERROR, ae);
       }
 
-      Message m =
-           ERR_JEB_COMPSCHEMA_CANNOT_DECODE_OC_TOKEN.get(ae.getMessage());
+      final Message m = ERR_JEB_COMPSCHEMA_CANNOT_DECODE_OC_TOKEN.get(ae
+          .getMessage());
       throw new InitializationException(m, ae);
     }
     finally
@@ -255,393 +307,64 @@
       ocCursor.close();
     }
 
-    ocCounter.set(highestToken+1);
-
-
     // Cursor through the attribute description database and load the attribute
     // set definitions.
-    Cursor adCursor = adDatabase.openCursor(null, null);
-    highestToken = 0;
-
+    final Cursor adCursor = adDatabase.openCursor(null, null);
     try
     {
-      DatabaseEntry keyEntry   = new DatabaseEntry();
-      DatabaseEntry valueEntry = new DatabaseEntry();
+      final DatabaseEntry keyEntry = new DatabaseEntry();
+      final DatabaseEntry valueEntry = new DatabaseEntry();
       OperationStatus status = adCursor.getFirst(keyEntry, valueEntry,
-                                                 LockMode.READ_UNCOMMITTED);
+          LockMode.READ_UNCOMMITTED);
       while (status == OperationStatus.SUCCESS)
       {
-        byte[] tokenBytes = keyEntry.getData();
-        ByteString token = ByteString.wrap(tokenBytes);
-        highestToken = Math.max(highestToken, decodeInt(tokenBytes));
-
-        ASN1Reader reader =
-            ASN1.getReader(valueEntry.getData());
+        final byte[] encodedAttribute = keyEntry.getData();
+        final ASN1Reader reader = ASN1.getReader(valueEntry.getData());
         reader.readStartSequence();
-        String attrName = reader.readOctetStringAsString();
-        String lowerName = toLowerCase(attrName);
-        AttributeType attrType =
-            DirectoryServer.getAttributeType(lowerName, true);
-
-        LinkedHashSet<String> options =
-            new LinkedHashSet<String>();
-        while(reader.hasNextElement())
+        final String attributeName = reader.readOctetStringAsString();
+        final List<String> attributeOptions = new LinkedList<String>();
+        while (reader.hasNextElement())
         {
-          options.add(reader.readOctetStringAsString());
+          attributeOptions.add(reader.readOctetStringAsString());
         }
         reader.readEndSequence();
-
-        atDecodeMap.put(token, attrType);
-        aoDecodeMap.put(token, options);
-
-        ConcurrentHashMap<Set<String>, ByteSequence> map = adEncodeMap
-            .get(attrType);
-        if (map == null)
-        {
-          map = new ConcurrentHashMap<Set<String>, ByteSequence>(1);
-          map.put(options, token);
-          adEncodeMap.put(attrType, map);
-        }
-        else
-        {
-          map.put(options, token);
-        }
-
+        loadAttribute(encodedAttribute, attributeName, attributeOptions);
         status = adCursor.getNext(keyEntry, valueEntry,
-                                  LockMode.READ_UNCOMMITTED);
+            LockMode.READ_UNCOMMITTED);
       }
     }
-    catch (ASN1Exception ae)
+    catch (final ASN1Exception ae)
     {
       if (debugEnabled())
       {
         TRACER.debugCaught(DebugLogLevel.ERROR, ae);
       }
 
-      Message m =
-           ERR_JEB_COMPSCHEMA_CANNOT_DECODE_AD_TOKEN.get(ae.getMessage());
+      final Message m = ERR_JEB_COMPSCHEMA_CANNOT_DECODE_AD_TOKEN.get(ae
+          .getMessage());
       throw new InitializationException(m, ae);
     }
     finally
     {
       adCursor.close();
     }
-
-    adCounter.set(highestToken+1);
   }
 
 
 
-  /**
-   * Closes the databases and releases any resources held by this compressed
-   * schema manager.
-   */
-  public void close()
-  {
-    try
-    {
-      adDatabase.sync();
-    } catch (Exception e) {}
-
-    try
-    {
-      adDatabase.close();
-    } catch (Exception e) {}
-
-    try
-    {
-      ocDatabase.sync();
-    } catch (Exception e) {}
-
-    try
-    {
-      ocDatabase.close();
-    } catch (Exception e) {}
-
-    adDatabase  = null;
-    ocDatabase  = null;
-    environment = null;
-    atDecodeMap = null;
-    aoDecodeMap = null;
-    ocDecodeMap = null;
-    //adEncodeMap = null;
-    //ocEncodeMap = null;
-    adCounter   = null;
-    ocCounter   = null;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override()
-  public void encodeObjectClasses(ByteStringBuilder entryBuffer,
-                                  Map<ObjectClass,String> objectClasses)
-         throws DirectoryException
-  {
-    ByteSequence encodedClasses = ocEncodeMap.get(objectClasses);
-    if (encodedClasses == null)
-    {
-      synchronized (ocEncodeMap)
-      {
-        int setValue = ocCounter.getAndIncrement();
-        byte[] tokenArray = encodeInt(setValue);
-        encodedClasses = ByteString.wrap(tokenArray);
-
-        storeObjectClass(tokenArray, objectClasses);
-        ocEncodeMap.put(objectClasses, encodedClasses);
-        ocDecodeMap.put(encodedClasses, objectClasses);
-      }
-    }
-
-    entryBuffer.appendBERLength(encodedClasses.length());
-    encodedClasses.copyTo(entryBuffer);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override()
-  public Map<ObjectClass,String> decodeObjectClasses(
-      ByteSequenceReader entryBufferReader) throws DirectoryException
-  {
-    int tokenLength = entryBufferReader.getBERLength();
-    ByteSequence byteArray = entryBufferReader.getByteSequence(tokenLength);
-    Map<ObjectClass,String> ocMap = ocDecodeMap.get(byteArray);
-    if (ocMap == null)
-    {
-      Message message = ERR_JEB_COMPSCHEMA_UNKNOWN_OC_TOKEN.get(byteArray
-          .toByteString().toHex());
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-          message);
-    }
-    else
-    {
-      return ocMap;
-    }
-  }
-
-  private void storeObjectClass(byte[] token,
-                                Map<ObjectClass,String> objectClasses)
-      throws DirectoryException
-  {
-    synchronized(storeWriter)
-    {
-      try
-      {
-        storeWriterBuffer.clear();
-        storeWriter.writeStartSequence();
-        for (String ocName : objectClasses.values())
-        {
-          storeWriter.writeOctetString(ocName);
-        }
-        storeWriter.writeEndSequence();
-        store(ocDatabase, token, storeWriterBuffer);
-      }
-      catch(IOException ioe)
-      {
-        // TODO: Shouldn't happen but should log a message
-      }
-    }
-  }
-
-  private void storeAttribute(byte[] token,
-                              AttributeType attrType, Set<String> options)
-      throws DirectoryException
-  {
-    synchronized(storeWriter)
-    {
-      try
-      {
-        storeWriterBuffer.clear();
-        storeWriter.writeStartSequence();
-        storeWriter.writeOctetString(attrType.getNameOrOID());
-        for (String option : options)
-        {
-          storeWriter.writeOctetString(option);
-        }
-        storeWriter.writeEndSequence();
-        store(adDatabase, token, storeWriterBuffer);
-      }
-      catch(IOException ioe)
-      {
-        // TODO: Shouldn't happen but should log a message
-      }
-    }
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override()
-  public void encodeAttribute(ByteStringBuilder entryBuffer,
-                              Attribute attribute) throws DirectoryException
-  {
-    AttributeType type = attribute.getAttributeType();
-    Set<String> options = attribute.getOptions();
-
-    ConcurrentHashMap<Set<String>, ByteSequence> map = adEncodeMap.get(type);
-    if (map == null)
-    {
-      byte[] tokenArray;
-      ByteString byteString;
-      synchronized (adEncodeMap)
-      {
-        map = new ConcurrentHashMap<Set<String>, ByteSequence>(1);
-
-        int intValue = adCounter.getAndIncrement();
-        tokenArray = encodeInt(intValue);
-        byteString = ByteString.wrap(tokenArray);
-        map.put(options,byteString);
-
-        storeAttribute(tokenArray, type, options);
-        adEncodeMap.put(type, map);
-        atDecodeMap.put(byteString, type);
-        aoDecodeMap.put(byteString, options);
-      }
-
-      encodeAttribute(entryBuffer, byteString, attribute);
-    }
-    else
-    {
-      ByteSequence byteArray = map.get(options);
-      if (byteArray == null)
-      {
-        byte[] tokenArray;
-        synchronized (map)
-        {
-          int intValue = adCounter.getAndIncrement();
-          tokenArray = encodeInt(intValue);
-          byteArray = ByteString.wrap(tokenArray);
-          map.put(options,byteArray);
-
-          storeAttribute(tokenArray, type, options);
-          atDecodeMap.put(byteArray, type);
-          aoDecodeMap.put(byteArray, options);
-        }
-      }
-
-      encodeAttribute(entryBuffer, byteArray, attribute);
-    }
-  }
-
-
-
-  /**
-   * Encodes the information in the provided attribute to a byte
-   * array.
-   *
-   * @param  buffer     The byte buffer to encode the attribute into.
-   * @param  adArray    The byte array that is a placeholder for the
-   *                    attribute type and set of options.
-   * @param  attribute  The attribute to be encoded.
-   */
-  private void encodeAttribute(ByteStringBuilder buffer, ByteSequence adArray,
-                                 Attribute attribute)
-  {
-    // Write the length of the adArray followed by the adArray.
-    buffer.appendBERLength(adArray.length());
-    adArray.copyTo(buffer);
-
-    // Write the number of attributes
-    buffer.appendBERLength(attribute.size());
-
-    // Write the attribute values as length / value pairs
-    for(AttributeValue v : attribute)
-    {
-      buffer.appendBERLength(v.getValue().length());
-      buffer.append(v.getValue());
-    }
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override()
-  public Attribute decodeAttribute(ByteSequenceReader entryBufferReader)
-         throws DirectoryException
-  {
-    // Figure out how many bytes are in the token that is the placeholder for
-    // the attribute description.
-    int adArrayLength = entryBufferReader.getBERLength();
-
-
-    // Get the attribute description token and make sure it resolves to an
-    // attribute type and option set.
-    ByteSequence adArray = entryBufferReader.getByteSequence(adArrayLength);
-
-    AttributeType attrType = atDecodeMap.get(adArray);
-    Set<String> options = aoDecodeMap.get(adArray);
-    if ((attrType == null) || (options == null))
-    {
-      Message message = ERR_JEB_COMPSCHEMA_UNRECOGNIZED_AD_TOKEN.get(adArray
-          .toByteString().toHex());
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-          message);
-    }
-
-
-    // Determine the number of values for the attribute.
-    int numValues = entryBufferReader.getBERLength();
-
-
-    // For the common case of a single value with no options, generate
-    // less garbage.
-    if (numValues == 1 && options.isEmpty())
-    {
-      int valueLength = entryBufferReader.getBERLength();
-
-      ByteString valueBytes =
-          entryBufferReader.getByteSequence(valueLength).toByteString();
-      return Attributes.create(attrType,
-          AttributeValues.create(attrType, valueBytes));
-    }
-    else
-    {
-      // Read the appropriate number of values.
-      AttributeBuilder builder = new AttributeBuilder(attrType);
-      builder.setOptions(options);
-      builder.setInitialCapacity(numValues);
-      for (int i = 0; i < numValues; i++)
-      {
-        int valueLength = entryBufferReader.getBERLength();
-
-        ByteString valueBytes =
-            entryBufferReader.getByteSequence(valueLength).toByteString();
-        builder.add(AttributeValues.create(attrType,
-            valueBytes));
-      }
-
-      return builder.toAttribute();
-    }
-  }
-
-  /**
-   * Stores the provided key-value pair in the specified database container.
-   *
-   * @param  database    The database in which to store the information.
-   * @param  keyBytes    The byte array containing the key to store.
-   * @param  valueBytes  The byte array containing the value to store.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting to store
-   *                              the data.
-   */
-  private void store(Database database, byte[] keyBytes,
-                     ByteStringBuilder valueBytes)
-          throws DirectoryException
+  private void store(final Database database, final byte[] key,
+      final ByteStringBuilder value) throws DirectoryException
   {
     boolean successful = false;
-    DatabaseEntry keyEntry   = new DatabaseEntry(keyBytes);
-    DatabaseEntry valueEntry = new DatabaseEntry(valueBytes.getBackingArray(),
-        0, valueBytes.length());
-
-    for (int i=0; i < 3; i++)
+    final DatabaseEntry keyEntry = new DatabaseEntry(key);
+    final DatabaseEntry valueEntry = new DatabaseEntry(value.getBackingArray(),
+        0, value.length());
+    for (int i = 0; i < 3; i++)
     {
       try
       {
-        OperationStatus status = database.putNoOverwrite(null, keyEntry,
-                                                         valueEntry);
+        final OperationStatus status = database.putNoOverwrite(null, keyEntry,
+            valueEntry);
         if (status == OperationStatus.SUCCESS)
         {
           successful = true;
@@ -649,95 +372,31 @@
         }
         else
         {
-          Message m = ERR_JEB_COMPSCHEMA_CANNOT_STORE_STATUS.get(
-                           status.toString());
+          final Message m = ERR_JEB_COMPSCHEMA_CANNOT_STORE_STATUS.get(status
+              .toString());
           throw new DirectoryException(
-                         DirectoryServer.getServerErrorResultCode(), m);
+              DirectoryServer.getServerErrorResultCode(), m);
         }
       }
-      catch (LockConflictException ce)
+      catch (final LockConflictException ce)
       {
         continue;
       }
-      catch (DatabaseException de)
+      catch (final DatabaseException de)
       {
-        Message m = ERR_JEB_COMPSCHEMA_CANNOT_STORE_EX.get(de.getMessage());
-        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-                                     m, de);
+        final Message m = ERR_JEB_COMPSCHEMA_CANNOT_STORE_EX.get(de
+            .getMessage());
+        throw new DirectoryException(
+            DirectoryServer.getServerErrorResultCode(), m, de);
       }
     }
 
-    if (! successful)
+    if (!successful)
     {
-      Message m = ERR_JEB_COMPSCHEMA_CANNOT_STORE_MULTIPLE_FAILURES.get();
-      throw new DirectoryException(
-                     DirectoryServer.getServerErrorResultCode(), m);
+      final Message m = ERR_JEB_COMPSCHEMA_CANNOT_STORE_MULTIPLE_FAILURES.get();
+      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+          m);
     }
   }
 
-
-
-
-  /**
-   * Encodes the provided int value to a byte array.
-   *
-   * @param  intValue  The int value to be encoded.
-   *
-   * @return  The byte array containing the encoded int value.
-   */
-  private byte[] encodeInt(int intValue)
-  {
-    byte[] array;
-    if (intValue <= 0xFF)
-    {
-      array = new byte[1];
-      array[0] = (byte) (intValue & 0xFF);
-    }
-    else if (intValue <= 0xFFFF)
-    {
-      array = new byte[2];
-      array[0] = (byte) ((intValue >> 8)  & 0xFF);
-      array[1] = (byte) (intValue & 0xFF);
-    }
-    else if (intValue <= 0xFFFFFF)
-    {
-      array = new byte[3];
-      array[0] = (byte) ((intValue >> 16) & 0xFF);
-      array[1] = (byte) ((intValue >> 8)  & 0xFF);
-      array[2] = (byte) (intValue & 0xFF);
-    }
-    else
-    {
-      array = new byte[4];
-      array[0] = (byte) ((intValue >> 24) & 0xFF);
-      array[1] = (byte) ((intValue >> 16) & 0xFF);
-      array[2] = (byte) ((intValue >> 8)  & 0xFF);
-      array[3] = (byte) (intValue & 0xFF);
-    }
-
-    return array;
-  }
-
-
-
-  /**
-   * Decodes the contents of the provided byte array as an int.
-   *
-   * @param  byteArray  The byte array containing the data to decode.
-   *
-   * @return  The decoded int value.
-   */
-  private int decodeInt(byte[] byteArray)
-  {
-    int intValue = 0;
-
-    for (byte b : byteArray)
-    {
-      intValue <<= 8;
-      intValue |= (b & 0xFF);
-    }
-
-    return intValue;
-  }
 }
-
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/DefaultCompressedSchema.java b/opendj-sdk/opends/src/server/org/opends/server/core/DefaultCompressedSchema.java
index d4316bd..e4f4530 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/DefaultCompressedSchema.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/DefaultCompressedSchema.java
@@ -23,33 +23,35 @@
  *
  *
  *      Copyright 2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.core;
 
 
 
+import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import static org.opends.server.loggers.debug.DebugLogger.getTracer;
+import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map.Entry;
 
 import org.opends.messages.Message;
 import org.opends.server.api.CompressedSchema;
 import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.protocols.asn1.ASN1Reader;
 import org.opends.server.protocols.asn1.ASN1;
+import org.opends.server.protocols.asn1.ASN1Reader;
 import org.opends.server.protocols.asn1.ASN1Writer;
-import org.opends.server.types.*;
-
-import static org.opends.server.config.ConfigConstants.*;
-import static org.opends.server.loggers.debug.DebugLogger.*;
-import static org.opends.messages.CoreMessages.*;
-import static org.opends.server.util.StaticUtils.*;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
 
 
 
@@ -58,39 +60,15 @@
  * that will store the schema definitions in a binary file
  * (config/schematokens.dat).
  */
-public final class DefaultCompressedSchema
-       extends CompressedSchema
+public final class DefaultCompressedSchema extends CompressedSchema
 {
   /**
    * The tracer object for the debug logger.
    */
   private static final DebugTracer TRACER = getTracer();
 
-
-
-  // The counter used for attribute descriptions.
-  private AtomicInteger adCounter;
-
-  // The counter used for object class sets.
-  private AtomicInteger ocCounter;
-
-  // The map between encoded representations and attribute types.
-  private ConcurrentHashMap<ByteSequence,AttributeType> atDecodeMap;
-
-  // The map between encoded representations and attribute options.
-  private ConcurrentHashMap<ByteSequence,Set<String>> aoDecodeMap;
-
-  // The map between encoded representations and object class sets.
-  private ConcurrentHashMap<ByteSequence,Map<ObjectClass,String>> ocDecodeMap;
-
-  // The map between attribute descriptions and their encoded
-  // representations.
-  private final ConcurrentHashMap<AttributeType,
-                ConcurrentHashMap<Set<String>, ByteSequence>> adEncodeMap;
-
-  // The map between object class sets and encoded representations.
-  private final ConcurrentHashMap<Map<ObjectClass,String>,
-      ByteSequence> ocEncodeMap;
+  // Synchronizes calls to save.
+  private final Object saveLock = new Object();
 
 
 
@@ -99,24 +77,37 @@
    */
   public DefaultCompressedSchema()
   {
-    atDecodeMap = new ConcurrentHashMap<ByteSequence, AttributeType>();
-    aoDecodeMap = new ConcurrentHashMap<ByteSequence, Set<String>>();
-    ocDecodeMap =
-        new ConcurrentHashMap<ByteSequence, Map<ObjectClass, String>>();
-    adEncodeMap = new ConcurrentHashMap
-      <AttributeType, ConcurrentHashMap<Set<String>, ByteSequence>>();
-    ocEncodeMap = new ConcurrentHashMap<Map<ObjectClass, String>,
-        ByteSequence>();
-
-    adCounter = new AtomicInteger(1);
-    ocCounter = new AtomicInteger(1);
-
     load();
   }
 
 
 
   /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void storeAttribute(final byte[] encodedAttribute,
+      final String attributeName, final Collection<String> attributeOptions)
+      throws DirectoryException
+  {
+    save();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void storeObjectClasses(final byte[] encodedObjectClasses,
+      final Collection<String> objectClassNames) throws DirectoryException
+  {
+    save();
+  }
+
+
+
+  /**
    * Loads the compressed schema information from disk.
    */
   private void load()
@@ -125,99 +116,68 @@
 
     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
+      // 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.getInstanceRoot() + File.separator +
-                    CONFIG_DIR_NAME + File.separator +
-                    COMPRESSED_SCHEMA_FILE_NAME;
-      if (! new File(path).exists())
+      final String path = DirectoryServer.getInstanceRoot() + File.separator
+          + CONFIG_DIR_NAME + File.separator + COMPRESSED_SCHEMA_FILE_NAME;
+      if (!new File(path).exists())
       {
         return;
       }
       inputStream = new FileInputStream(path);
-      ASN1Reader reader = ASN1.getReader(inputStream);
-
+      final ASN1Reader reader = ASN1.getReader(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
+      // 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.
       reader.readStartSequence();
-      while(reader.hasNextElement())
+      while (reader.hasNextElement())
       {
         reader.readStartSequence();
-        ByteSequence token = reader.readOctetString();
-
-        LinkedHashMap<ObjectClass,String> ocMap =
-             new LinkedHashMap<ObjectClass,String>();
-        while(reader.hasNextElement())
+        final byte[] encodedObjectClasses = reader.readOctetString()
+            .toByteArray();
+        final List<String> objectClassNames = new LinkedList<String>();
+        while (reader.hasNextElement())
         {
-          String ocName = reader.readOctetStringAsString();
-          String lowerName = toLowerCase(ocName);
-          ObjectClass oc = DirectoryServer.getObjectClass(lowerName, true);
-          ocMap.put(oc, ocName);
+          objectClassNames.add(reader.readOctetStringAsString());
         }
         reader.readEndSequence();
-
-        ocEncodeMap.put(ocMap, token);
-        ocDecodeMap.put(token, ocMap);
+        loadObjectClasses(encodedObjectClasses, objectClassNames);
       }
       reader.readEndSequence();
 
-
       // The second element in the file should be an integer element that holds
       // the value to use to initialize the object class counter.
-      ocCounter.set((int)reader.readInteger());
-
+      reader.readInteger(); // No longer used.
 
       // The third element in the file should be a sequence of attribute
-      // description components.  Each attribute description component will
+      // 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.
       reader.readStartSequence();
-      while(reader.hasNextElement())
+      while (reader.hasNextElement())
       {
         reader.readStartSequence();
-        ByteSequence token = reader.readOctetString();
-        String attrName = reader.readOctetStringAsString();
-        String lowerName = toLowerCase(attrName);
-        AttributeType attrType =
-            DirectoryServer.getAttributeType(lowerName, true);
-
-        LinkedHashSet<String> options =
-            new LinkedHashSet<String>();
-        while(reader.hasNextElement())
+        final byte[] encodedAttribute = reader.readOctetString().toByteArray();
+        final String attributeName = reader.readOctetStringAsString();
+        final List<String> attributeOptions = new LinkedList<String>();
+        while (reader.hasNextElement())
         {
-          options.add(reader.readOctetStringAsString());
+          attributeOptions.add(reader.readOctetStringAsString());
         }
         reader.readEndSequence();
-
-        atDecodeMap.put(token, attrType);
-        aoDecodeMap.put(token, options);
-
-        ConcurrentHashMap<Set<String>, ByteSequence> map = adEncodeMap
-            .get(attrType);
-        if (map == null)
-        {
-          map = new ConcurrentHashMap<Set<String>, ByteSequence>(1);
-          map.put(options, token);
-          adEncodeMap.put(attrType, map);
-        }
-        else
-        {
-          map.put(options, token);
-        }
+        loadAttribute(encodedAttribute, attributeName, attributeOptions);
       }
       reader.readEndSequence();
 
-
       // The fourth element in the file should be an integer element that holds
       // the value to use to initialize the attribute description counter.
-      adCounter.set((int)reader.readInteger());
+      reader.readInteger(); // No longer used.
     }
-    catch (Exception e)
+    catch (final Exception e)
     {
       if (debugEnabled())
       {
@@ -236,7 +196,7 @@
           inputStream.close();
         }
       }
-      catch (Exception e)
+      catch (final Exception e)
       {
         if (debugEnabled())
         {
@@ -251,368 +211,124 @@
   /**
    * 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
+  private void save() throws DirectoryException
   {
-    FileOutputStream outputStream = null;
-    try
+    synchronized (saveLock)
     {
-      // 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.getInstanceRoot() + File.separator +
-                    CONFIG_DIR_NAME + File.separator +
-                    COMPRESSED_SCHEMA_FILE_NAME;
-      String tempPath = path + ".tmp";
-
-      outputStream = new FileOutputStream(tempPath);
-      ASN1Writer writer = ASN1.getWriter(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.
-      writer.writeStartSequence();
-      for (Map.Entry<ByteSequence,Map<ObjectClass,String>> mapEntry :
-           ocDecodeMap.entrySet())
-      {
-        writer.writeStartSequence();
-        writer.writeOctetString(mapEntry.getKey());
-        Map<ObjectClass,String> ocMap = mapEntry.getValue();
-
-        for (String ocName : ocMap.values())
-        {
-          writer.writeOctetString(ocName);
-        }
-        writer.writeEndSequence();
-      }
-      writer.writeEndSequence();
-
-
-      // The second element in the file should be an integer element that holds
-      // the value to use to initialize the object class counter.
-      writer.writeInteger(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.
-      writer.writeStartSequence();
-      for (ByteSequence token : atDecodeMap.keySet())
-      {
-        writer.writeStartSequence();
-        AttributeType attrType = atDecodeMap.get(token);
-        Set<String> options = aoDecodeMap.get(token);
-
-        writer.writeOctetString(token);
-        writer.writeOctetString(attrType.getNameOrOID());
-        for (String option : options)
-        {
-          writer.writeOctetString(option);
-        }
-        writer.writeEndSequence();
-      }
-      writer.writeEndSequence();
-
-
-      // The fourth element in the file should be an integer element that holds
-      // the value to use to initialize the attribute description counter.
-      writer.writeInteger(adCounter.get());
-
-
-      // Close the writer and swing the temp file into place.
-      outputStream.close();
-      File liveFile = new File(path);
-      File tempFile = new File(tempPath);
-
-      if (liveFile.exists())
-      {
-        File saveFile = new File(liveFile.getAbsolutePath() + ".save");
-        if (saveFile.exists())
-        {
-          saveFile.delete();
-        }
-        liveFile.renameTo(saveFile);
-      }
-      tempFile.renameTo(liveFile);
-    }
-    catch (Exception e)
-    {
-      if (debugEnabled())
-      {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      Message message = ERR_COMPRESSEDSCHEMA_CANNOT_WRITE_UPDATED_DATA.get(
-                             stackTraceToSingleLineString(e));
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-                                   message, e);
-    }
-    finally
-    {
+      FileOutputStream outputStream = null;
       try
       {
-        if (outputStream != null)
+        // 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.
+        final String path = DirectoryServer.getInstanceRoot() + File.separator
+            + CONFIG_DIR_NAME + File.separator + COMPRESSED_SCHEMA_FILE_NAME;
+        final String tempPath = path + ".tmp";
+
+        outputStream = new FileOutputStream(tempPath);
+        final ASN1Writer writer = ASN1.getWriter(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.
+        writer.writeStartSequence();
+        int ocCounter = 1;
+        for (final Entry<byte[], Collection<String>> mapEntry :
+            getAllObjectClasses())
         {
-          outputStream.close();
+          writer.writeStartSequence();
+          writer.writeOctetString(ByteString.wrap(mapEntry.getKey()));
+          final Collection<String> objectClassNames = mapEntry.getValue();
+          for (final String ocName : objectClassNames)
+          {
+            writer.writeOctetString(ocName);
+          }
+          writer.writeEndSequence();
+          ocCounter++;
         }
+        writer.writeEndSequence();
+
+        // The second element in the file should be an integer element that
+        // holds the value to use to initialize the object class counter.
+        writer.writeInteger(ocCounter); // No longer used.
+
+        // 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.
+        writer.writeStartSequence();
+        int adCounter = 1;
+        for (final Entry<byte[], Entry<String, Collection<String>>> mapEntry :
+            getAllAttributes())
+        {
+          writer.writeStartSequence();
+          writer.writeOctetString(ByteString.wrap(mapEntry.getKey()));
+          writer.writeOctetString(mapEntry.getValue().getKey());
+          for (final String option : mapEntry.getValue().getValue())
+          {
+            writer.writeOctetString(option);
+          }
+          writer.writeEndSequence();
+          adCounter++;
+        }
+        writer.writeEndSequence();
+
+        // The fourth element in the file should be an integer element that
+        // holds the value to use to initialize the attribute description
+        // counter.
+        writer.writeInteger(adCounter); // No longer used.
+
+        // Close the writer and swing the temp file into place.
+        outputStream.close();
+        final File liveFile = new File(path);
+        final File tempFile = new File(tempPath);
+
+        if (liveFile.exists())
+        {
+          final File saveFile = new File(liveFile.getAbsolutePath() + ".save");
+          if (saveFile.exists())
+          {
+            saveFile.delete();
+          }
+          liveFile.renameTo(saveFile);
+        }
+        tempFile.renameTo(liveFile);
       }
-      catch (Exception e)
+      catch (final Exception e)
       {
         if (debugEnabled())
         {
           TRACER.debugCaught(DebugLogLevel.ERROR, e);
         }
+
+        final Message message = ERR_COMPRESSEDSCHEMA_CANNOT_WRITE_UPDATED_DATA
+            .get(stackTraceToSingleLineString(e));
+        throw new DirectoryException(
+            DirectoryServer.getServerErrorResultCode(), message, e);
       }
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override()
-  public void encodeObjectClasses(ByteStringBuilder entryBuffer,
-                                  Map<ObjectClass,String> objectClasses)
-         throws DirectoryException
-  {
-    ByteSequence encodedClasses = ocEncodeMap.get(objectClasses);
-    if (encodedClasses == null)
-    {
-      synchronized (ocEncodeMap)
+      finally
       {
-        int setValue = ocCounter.getAndIncrement();
-
-        encodedClasses = ByteString.wrap(encodeInt(setValue));
-        ocEncodeMap.put(objectClasses, encodedClasses);
-        ocDecodeMap.put(encodedClasses, objectClasses);
-
-        save();
-      }
-    }
-
-    entryBuffer.appendBERLength(encodedClasses.length());
-    encodedClasses.copyTo(entryBuffer);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override()
-  public Map<ObjectClass,String> decodeObjectClasses(
-      ByteSequenceReader entryBufferReader) throws DirectoryException
-  {
-    int tokenLength = entryBufferReader.getBERLength();
-    ByteSequence byteArray = entryBufferReader.getByteSequence(tokenLength);
-    Map<ObjectClass,String> ocMap = ocDecodeMap.get(byteArray);
-    if (ocMap == null)
-    {
-      Message message = ERR_COMPRESSEDSCHEMA_UNKNOWN_OC_TOKEN.get(byteArray
-          .toByteString().toHex());
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-          message);
-    }
-    else
-    {
-      return ocMap;
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override()
-  public void encodeAttribute(ByteStringBuilder entryBuffer,
-                              Attribute attribute) throws DirectoryException
-  {
-    AttributeType type = attribute.getAttributeType();
-    Set<String> options = attribute.getOptions();
-
-    ConcurrentHashMap<Set<String>, ByteSequence> map = adEncodeMap.get(type);
-    if (map == null)
-    {
-      ByteString byteArray;
-      synchronized (adEncodeMap)
-      {
-        map = new ConcurrentHashMap<Set<String>, ByteSequence>(1);
-
-        int intValue = adCounter.getAndIncrement();
-        byteArray = ByteString.wrap(encodeInt(intValue));
-        map.put(options,byteArray);
-
-        adEncodeMap.put(type, map);
-        atDecodeMap.put(byteArray, type);
-        aoDecodeMap.put(byteArray, options);
-        save();
-      }
-
-      encodeAttribute(entryBuffer, byteArray, attribute);
-    }
-    else
-    {
-      ByteSequence byteArray = map.get(options);
-      if (byteArray == null)
-      {
-        synchronized (map)
+        try
         {
-          int intValue = adCounter.getAndIncrement();
-          byteArray = ByteString.wrap(encodeInt(intValue));
-          map.put(options,byteArray);
-
-          atDecodeMap.put(byteArray, type);
-          aoDecodeMap.put(byteArray, options);
-          save();
+          if (outputStream != null)
+          {
+            outputStream.close();
+          }
+        }
+        catch (final Exception e)
+        {
+          if (debugEnabled())
+          {
+            TRACER.debugCaught(DebugLogLevel.ERROR, e);
+          }
         }
       }
-
-      encodeAttribute(entryBuffer, byteArray, attribute);
     }
   }
 
-
-
-  /**
-   * Encodes the information in the provided attribute to a byte
-   * array.
-   *
-   * @param  buffer     The byte buffer to encode the attribute into.
-   * @param  adArray    The byte array that is a placeholder for the
-   *                    attribute type and set of options.
-   * @param  attribute  The attribute to be encoded.
-   */
-  private void encodeAttribute(ByteStringBuilder buffer, ByteSequence adArray,
-                               Attribute attribute)
-  {
-    // Write the length of the adArray followed by the adArray.
-    buffer.appendBERLength(adArray.length());
-    adArray.copyTo(buffer);
-
-    // Write the number of attributes
-    buffer.appendBERLength(attribute.size());
-
-    // Write the attribute values as length / value pairs
-    for(AttributeValue v : attribute)
-    {
-      buffer.appendBERLength(v.getValue().length());
-      buffer.append(v.getValue());
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override()
-  public Attribute decodeAttribute(ByteSequenceReader entryBufferReader)
-         throws DirectoryException
-  {
-    // Figure out how many bytes are in the token that is the placeholder for
-    // the attribute description.
-    int adArrayLength = entryBufferReader.getBERLength();
-
-
-    // Get the attribute description token and make sure it resolves to an
-    // attribute type and option set.
-    ByteSequence adArray = entryBufferReader.getByteSequence(adArrayLength);
-
-    AttributeType attrType = atDecodeMap.get(adArray);
-    Set<String> options = aoDecodeMap.get(adArray);
-    if ((attrType == null) || (options == null))
-    {
-      Message message = ERR_COMPRESSEDSCHEMA_UNRECOGNIZED_AD_TOKEN.get(adArray
-          .toByteString().toHex());
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-          message);
-    }
-
-
-    // Determine the number of values for the attribute.
-    int numValues = entryBufferReader.getBERLength();
-
-
-    // For the common case of a single value with no options, generate
-    // less garbage.
-    if (numValues == 1 && options.isEmpty())
-    {
-      int valueLength = entryBufferReader.getBERLength();
-
-      ByteString valueBytes =
-          entryBufferReader.getByteSequence(valueLength).toByteString();
-      return Attributes.create(attrType,
-          AttributeValues.create(attrType,valueBytes));
-    }
-    else
-    {
-      // Read the appropriate number of values.
-      AttributeBuilder builder = new AttributeBuilder(attrType);
-      builder.setOptions(options);
-      builder.setInitialCapacity(numValues);
-      for (int i = 0; i < numValues; i++)
-      {
-        int valueLength = entryBufferReader.getBERLength();
-
-        ByteString valueBytes =
-            entryBufferReader.getByteSequence(valueLength).toByteString();
-        builder.add(AttributeValues.create(attrType,
-            valueBytes));
-      }
-
-      return builder.toAttribute();
-    }
-  }
-
-
-
-  /**
-   * Encodes the provided int value to a byte array.
-   *
-   * @param  intValue  The int value to be encoded.
-   *
-   * @return  The byte array containing the encoded int value.
-   */
-  private byte[] encodeInt(int intValue)
-  {
-    byte[] array;
-    if (intValue <= 0xFF)
-    {
-      array = new byte[1];
-      array[0] = (byte) (intValue & 0xFF);
-    }
-    else if (intValue <= 0xFFFF)
-    {
-      array = new byte[2];
-      array[0] = (byte) ((intValue >> 8)  & 0xFF);
-      array[1] = (byte) (intValue & 0xFF);
-    }
-    else if (intValue <= 0xFFFFFF)
-    {
-      array = new byte[3];
-      array[0] = (byte) ((intValue >> 16) & 0xFF);
-      array[1] = (byte) ((intValue >> 8)  & 0xFF);
-      array[2] = (byte) (intValue & 0xFF);
-    }
-    else
-    {
-      array = new byte[4];
-      array[0] = (byte) ((intValue >> 24) & 0xFF);
-      array[1] = (byte) ((intValue >> 16) & 0xFF);
-      array[2] = (byte) ((intValue >> 8)  & 0xFF);
-      array[3] = (byte) (intValue & 0xFF);
-    }
-
-    return array;
-  }
 }
-
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
index db60db4..96418ed 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -23,7 +23,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2010-2012 ForgeRock AS.
+ *      Portions Copyright 2010-2013 ForgeRock AS.
  */
 package org.opends.server.core;
 
@@ -3681,11 +3681,11 @@
       String oid        = lowerName + "-oid";
       String definition = "( " + oid + " NAME '" + name + "' ABSTRACT )";
 
+      // Temporary object classes are immediately dirty.
       objectClass = new ObjectClass(definition, name,
-                                    Collections.singleton(name), oid, null,
-                                    Collections.singleton(getTopObjectClass()),
-                                    null, null,
-                                    ObjectClassType.ABSTRACT, false, null);
+          Collections.singleton(name), oid, null,
+          Collections.singleton(getTopObjectClass()), null, null,
+          ObjectClassType.ABSTRACT, false, null).setDirty();
     }
 
     return objectClass;
@@ -3936,10 +3936,11 @@
     String definition = "( " + oid + " NAME '" + name + "' SYNTAX " +
                         syntax.getOID() + " )";
 
+    // Temporary attribute types are immediately dirty.
     return new AttributeType(definition, name, Collections.singleton(name),
                              oid, null, null, syntax,
                              AttributeUsage.USER_APPLICATIONS, false, false,
-                             false, false);
+                             false, false).setDirty();
   }
 
 
@@ -3947,9 +3948,10 @@
   /**
    * Retrieves the set of attribute syntaxes defined in the Directory Server.
    *
-   * @return  The set of attribute syntaxes defined in the Directory Server.
+   * @return The set of attribute syntaxes defined in the Directory Server.
    */
-  public static ConcurrentHashMap<String,AttributeSyntax> getAttributeSyntaxes()
+  public static ConcurrentHashMap<String,
+                                  AttributeSyntax<?>> getAttributeSyntaxes()
   {
     return directoryServer.schema.getSyntaxes();
   }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/AttributeType.java b/opendj-sdk/opends/src/server/org/opends/server/types/AttributeType.java
index d7f2d5e..9a6a42a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/AttributeType.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/AttributeType.java
@@ -23,7 +23,7 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2012 ForgeRock AS
+ *      Portions Copyright 2011-2013 ForgeRock AS
  */
 package org.opends.server.types;
 
@@ -127,6 +127,9 @@
   // The substring matching rule for this attribute type.
   private final SubstringMatchingRule substringMatchingRule;
 
+  // True once this attribute type has been removed from the schema.
+  private volatile boolean isDirty = false;
+
 
 
   /**
@@ -431,31 +434,17 @@
   }
 
   /**
-   * Creates a new instance of this attribute type based on the
-   * definition string.  It will also preserve other state information
-   * associated with this attribute type that is not included in the
-   * definition string (e.g., the name of the schema file with which
-   * it is associated).
-   *
-   * @return  The new instance of this attribute type based on the
-   *          definition string.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting
-   *                              to create a new attribute type
-   *                              instance from the definition string.
+   * {@inheritDoc}
    */
-  public AttributeType recreateFromDefinition()
+  public AttributeType recreateFromDefinition(Schema schema)
          throws DirectoryException
   {
     ByteString value  = ByteString.valueOf(definition);
-    Schema     schema = DirectoryServer.getSchema();
-
     AttributeType at =
          AttributeTypeSyntax.decodeAttributeType(value, schema,
                                               false);
     at.setSchemaFile(getSchemaFile());
     at.mayHaveSubordinateTypes = mayHaveSubordinateTypes;
-
     return at;
   }
 
@@ -772,5 +761,33 @@
     return getNormalizedPrimaryNameOrOID().compareTo(
       o.getNormalizedPrimaryNameOrOID());
   }
+
+
+
+  /**
+   * Marks this attribute type as dirty, indicating that it has been removed or
+   * replaced in the schema.
+   *
+   * @return A reference to this attribute type.
+   */
+  public AttributeType setDirty()
+  {
+    isDirty = true;
+    return this;
+  }
+
+
+
+  /**
+   * Returns {@code true} if this attribute type has been removed or replaced in
+   * the schema.
+   *
+   * @return {@code true} if this attribute type has been removed or replaced in
+   *         the schema.
+   */
+  public boolean isDirty()
+  {
+    return isDirty;
+  }
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/DITContentRule.java b/opendj-sdk/opends/src/server/org/opends/server/types/DITContentRule.java
index ae855cf..0f8b887 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/DITContentRule.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/DITContentRule.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types;
 
@@ -252,30 +253,16 @@
 
 
   /**
-   * Creates a new instance of this DIT content rule based on the
-   * definition string.  It will also preserve other state information
-   * associated with this DIT content rule that is not included in the
-   * definition string (e.g., the name of the schema file with which
-   * it is associated).
-   *
-   * @return  The new instance of this DIT content rule based on the
-   *          definition string.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting
-   *                              to create a new DIT content rule
-   *                              instance from the definition string.
+   * {@inheritDoc}
    */
-  public DITContentRule recreateFromDefinition()
+  public DITContentRule recreateFromDefinition(Schema schema)
          throws DirectoryException
   {
     ByteString value  = ByteString.valueOf(definition);
-    Schema     schema = DirectoryConfig.getSchema();
-
     DITContentRule dcr =
          DITContentRuleSyntax.decodeDITContentRule(value, schema,
                                                    false);
     dcr.setSchemaFile(getSchemaFile());
-
     return dcr;
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/DITStructureRule.java b/opendj-sdk/opends/src/server/org/opends/server/types/DITStructureRule.java
index 525b603..777384c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/DITStructureRule.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/DITStructureRule.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types;
 
@@ -202,30 +203,16 @@
 
 
   /**
-   * Creates a new instance of this DIT structure rule based on the
-   * definition string.  It will also preserve other state information
-   * associated with this DIT structure rule that is not included in
-   * the definition string (e.g., the name of the schema file with
-   * which it is associated).
-   *
-   * @return  The new instance of this DIT structure rule based on the
-   *          definition string.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting
-   *                              to create a new DIT structure rule
-   *                              instance from the definition string.
+   * {@inheritDoc}
    */
-  public DITStructureRule recreateFromDefinition()
+  public DITStructureRule recreateFromDefinition(Schema schema)
          throws DirectoryException
   {
     ByteString value  = ByteString.valueOf(definition);
-    Schema     schema = DirectoryConfig.getSchema();
-
     DITStructureRule dsr =
          DITStructureRuleSyntax.decodeDITStructureRule(value, schema,
                                                        false);
     dsr.setSchemaFile(getSchemaFile());
-
     return dsr;
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/DirectoryConfig.java b/opendj-sdk/opends/src/server/org/opends/server/types/DirectoryConfig.java
index 96407c4..bbc0bd5 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/DirectoryConfig.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/DirectoryConfig.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types;
 import java.util.List;
@@ -411,7 +412,7 @@
    * @return  The set of attribute syntaxes defined in the Directory
    *          Server.
    */
-  public static Map<String,AttributeSyntax>
+  public static Map<String,AttributeSyntax<?>>
        getAttributeSyntaxes()
   {
     return DirectoryServer.getAttributeSyntaxes();
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/LDAPSyntaxDescription.java b/opendj-sdk/opends/src/server/org/opends/server/types/LDAPSyntaxDescription.java
index 3e07676..5587a59 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/LDAPSyntaxDescription.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/LDAPSyntaxDescription.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2009 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 
 
@@ -166,31 +167,16 @@
 
 
   /**
-   * Creates a new instance of this ldap syntax based on the
-   * definition string.  It will also preserve other state
-   * information associated with this ldap syntax that is not
-   * included in the definition string (e.g., the name of the schema
-   * file with which it is associated).
-   *
-   * @return  The new instance of this ldap syntax based on the
-   *          definition string.
-   *
-   * @throws  DirectoryException  If a problem occurs while
-   *                              attempting to create a new ldap
-   *                              syntax instance from the definition
-   *                              string.
+   * {@inheritDoc}
    */
-  public LDAPSyntaxDescription recreateFromDefinition()
+  public LDAPSyntaxDescription recreateFromDefinition(Schema schema)
          throws DirectoryException
   {
     ByteString value  = ByteString.valueOf(definition);
-    Schema     schema = DirectoryConfig.getSchema();
-
     LDAPSyntaxDescription ls =
             LDAPSyntaxDescriptionSyntax.decodeLDAPSyntax(value,
             schema, false);
     ls.setSchemaFile(getSchemaFile());
-
     return ls;
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/MatchingRuleUse.java b/opendj-sdk/opends/src/server/org/opends/server/types/MatchingRuleUse.java
index 7b00944..47cb0d8 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/MatchingRuleUse.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/MatchingRuleUse.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types;
 
@@ -202,30 +203,16 @@
 
 
   /**
-   * Creates a new instance of this matching rule use based on the
-   * definition string.  It will also preserve other state information
-   * associated with this matching rule use that is not included in
-   * the definition string (e.g., the name of the schema file with
-   * which it is associated).
-   *
-   * @return  The new instance of this matching rule use based on the
-   *          definition string.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting
-   *                              to create a new matching rule use
-   *                              instance from the definition string.
+   * {@inheritDoc}
    */
-  public MatchingRuleUse recreateFromDefinition()
+  public MatchingRuleUse recreateFromDefinition(Schema schema)
          throws DirectoryException
   {
     ByteString value  = ByteString.valueOf(definition);
-    Schema     schema = DirectoryConfig.getSchema();
-
     MatchingRuleUse mru =
          MatchingRuleUseSyntax.decodeMatchingRuleUse(value, schema,
                                                      false);
     mru.setSchemaFile(getSchemaFile());
-
     return mru;
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/NameForm.java b/opendj-sdk/opends/src/server/org/opends/server/types/NameForm.java
index 623db7f..864b8d9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/NameForm.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/NameForm.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types;
 
@@ -218,27 +219,14 @@
 
 
   /**
-   * Creates a new instance of this name form based on the definition
-   * string.  It will also preserve other state information associated
-   * with this name form that is not included in the definition string
-   * (e.g., the name of the schema file with which it is associated).
-   *
-   * @return  The new instance of this name form based on the
-   *          definition string.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting
-   *                              to create a new name form instance
-   *                              from the definition string.
+   * {@inheritDoc}
    */
-  public NameForm recreateFromDefinition()
+  public NameForm recreateFromDefinition(Schema schema)
          throws DirectoryException
   {
     ByteString value  = ByteString.valueOf(definition);
-    Schema     schema = DirectoryConfig.getSchema();
-
     NameForm nf = NameFormSyntax.decodeNameForm(value, schema, false);
     nf.setSchemaFile(getSchemaFile());
-
     return nf;
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/ObjectClass.java b/opendj-sdk/opends/src/server/org/opends/server/types/ObjectClass.java
index 9971281..0b0a317 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/ObjectClass.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/ObjectClass.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types;
 
@@ -106,6 +107,9 @@
   // The definition string used to create this objectclass.
   private final String definition;
 
+  // True once this object class has been removed from the schema.
+  private volatile boolean isDirty = false;
+
 
 
   /**
@@ -312,29 +316,15 @@
 
 
   /**
-   * Creates a new instance of this objectclass based on the
-   * definition string.  It will also preserve other state information
-   * associated with this objectclass that is not included in the
-   * definition string (e.g., the name of the schema file with which
-   * it is associated).
-   *
-   * @return  The new instance of this objectclass based on the
-   *          definition string.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting
-   *                              to create a new objectclass instance
-   *                              from the definition string.
+   * {@inheritDoc}
    */
-  public ObjectClass recreateFromDefinition()
+  public ObjectClass recreateFromDefinition(Schema schema)
          throws DirectoryException
   {
     ByteString value  = ByteString.valueOf(definition);
-    Schema     schema = DirectoryConfig.getSchema();
-
     ObjectClass oc = ObjectClassSyntax.decodeObjectClass(value,
                                             schema, false);
     oc.setSchemaFile(getSchemaFile());
-
     return oc;
   }
 
@@ -608,4 +598,32 @@
       }
     }
   }
+
+
+
+  /**
+   * Marks this object class as dirty, indicating that it has been removed or
+   * replaced in the schema.
+   *
+   * @return A reference to this object class.
+   */
+  public ObjectClass setDirty()
+  {
+    isDirty = true;
+    return this;
   }
+
+
+
+  /**
+   * Returns {@code true} if this object class has been removed or replaced in
+   * the schema.
+   *
+   * @return {@code true} if this object class has been removed or replaced in
+   *         the schema.
+   */
+  public boolean isDirty()
+  {
+    return isDirty;
+  }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java b/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
index f3f2d42..f28b5bd 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
@@ -23,7 +23,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011 ForgeRock AS
+ *      Portions Copyright 2011-2013 ForgeRock AS
  */
 package org.opends.server.types;
 
@@ -120,7 +120,7 @@
 
   // The set of attribute syntaxes for this schema, mapped between the
   // OID for the syntax and the syntax itself.
-  private ConcurrentHashMap<String,AttributeSyntax> syntaxes;
+  private ConcurrentHashMap<String,AttributeSyntax<?>> syntaxes;
 
   // The entire set of matching rules for this schema, mapped between
   // the lowercase names and OID for the definition and the matching
@@ -246,7 +246,7 @@
   {
     attributeTypes = new ConcurrentHashMap<String,AttributeType>();
     objectClasses = new ConcurrentHashMap<String,ObjectClass>();
-    syntaxes = new ConcurrentHashMap<String,AttributeSyntax>();
+    syntaxes = new ConcurrentHashMap<String,AttributeSyntax<?>>();
     matchingRules = new ConcurrentHashMap<String,MatchingRule>();
     approximateMatchingRules =
          new ConcurrentHashMap<String,ApproximateMatchingRule>();
@@ -406,8 +406,14 @@
         }
       }
 
-      attributeTypes.put(toLowerCase(attributeType.getOID()),
-                         attributeType);
+      AttributeType old = attributeTypes.put(
+          toLowerCase(attributeType.getOID()), attributeType);
+      if (old != null && old != attributeType)
+      {
+        // Mark the old attribute type as stale so that caches (such as
+        // compressed schema) can detect changes.
+        old.setDirty();
+      }
 
       for (String name : attributeType.getNormalizedNames())
       {
@@ -446,8 +452,13 @@
   {
     synchronized (attributeTypes)
     {
-      attributeTypes.remove(toLowerCase(attributeType.getOID()),
-                            attributeType);
+      if (attributeTypes.remove(toLowerCase(attributeType.getOID()),
+          attributeType))
+      {
+        // Mark the old attribute type as stale so that caches (such as
+        // compressed schema) can detect changes.
+        attributeType.setDirty();
+      }
 
       for (String name : attributeType.getNormalizedNames())
       {
@@ -693,8 +704,14 @@
         }
       }
 
-      objectClasses.put(toLowerCase(objectClass.getOID()),
-                        objectClass);
+      ObjectClass old = objectClasses.put(toLowerCase(objectClass.getOID()),
+          objectClass);
+      if (old != null && old != objectClass)
+      {
+        // Mark the old object class as stale so that caches (such as compressed
+        // schema) can detect changes.
+        old.setDirty();
+      }
 
       for (String name : objectClass.getNormalizedNames())
       {
@@ -725,8 +742,12 @@
   {
     synchronized (objectClasses)
     {
-      objectClasses.remove(toLowerCase(objectClass.getOID()),
-                           objectClass);
+      if (objectClasses.remove(toLowerCase(objectClass.getOID()), objectClass))
+      {
+        // Mark the old object class as stale so that caches (such as
+        // compressed schema) can detect changes.
+        objectClass.setDirty();
+      }
 
       for (String name : objectClass.getNormalizedNames())
       {
@@ -770,7 +791,7 @@
    *
    * @return  The attribute syntax definitions for this schema.
    */
-  public ConcurrentHashMap<String,AttributeSyntax> getSyntaxes()
+  public ConcurrentHashMap<String,AttributeSyntax<?>> getSyntaxes()
   {
     return syntaxes;
   }
@@ -816,7 +837,7 @@
    * @return  The requested attribute syntax, or <CODE>null</CODE> if
    *          no syntax is registered with the provided OID.
    */
-  public AttributeSyntax getSyntax(String lowerName)
+  public AttributeSyntax<?> getSyntax(String lowerName)
   {
     return syntaxes.get(lowerName);
   }
@@ -838,7 +859,7 @@
    *                              <CODE>overwriteExisting</CODE> flag
    *                              is set to <CODE>false</CODE>
    */
-  public void registerSyntax(AttributeSyntax syntax,
+  public void registerSyntax(AttributeSyntax<?> syntax,
                              boolean overwriteExisting)
          throws DirectoryException
   {
@@ -849,7 +870,7 @@
         String oid = toLowerCase(syntax.getOID());
         if (syntaxes.containsKey(oid))
         {
-          AttributeSyntax conflictingSyntax = syntaxes.get(oid);
+          AttributeSyntax<?> conflictingSyntax = syntaxes.get(oid);
 
           Message message = ERR_SCHEMA_CONFLICTING_SYNTAX_OID.
               get(syntax.getSyntaxName(), oid,
@@ -882,7 +903,7 @@
    * @param  syntax  The attribute syntax to deregister with this
    *                 schema.
    */
-  public void deregisterSyntax(AttributeSyntax syntax)
+  public void deregisterSyntax(AttributeSyntax<?> syntax)
   {
     synchronized (syntaxes)
     {
@@ -3160,7 +3181,7 @@
         if ((at.getSuperiorType() != null) &&
             at.getSuperiorType().equals(t))
         {
-          AttributeType newAT = at.recreateFromDefinition();
+          AttributeType newAT = at.recreateFromDefinition(this);
           deregisterAttributeType(at);
           registerAttributeType(newAT, true);
           rebuildDependentElements(at, depth+1);
@@ -3172,7 +3193,7 @@
         if (oc.getRequiredAttributes().contains(t) ||
             oc.getOptionalAttributes().contains(t))
         {
-          ObjectClass newOC = oc.recreateFromDefinition();
+          ObjectClass newOC = oc.recreateFromDefinition(this);
           deregisterObjectClass(oc);
           registerObjectClass(newOC, true);
           rebuildDependentElements(oc, depth+1);
@@ -3186,7 +3207,7 @@
           if (nf.getRequiredAttributes().contains(t) ||
               nf.getOptionalAttributes().contains(t))
           {
-            NameForm newNF = nf.recreateFromDefinition();
+            NameForm newNF = nf.recreateFromDefinition(this);
             deregisterNameForm(nf);
             registerNameForm(newNF, true);
             rebuildDependentElements(nf, depth+1);
@@ -3200,7 +3221,7 @@
             dcr.getOptionalAttributes().contains(t) ||
             dcr.getProhibitedAttributes().contains(t))
         {
-          DITContentRule newDCR = dcr.recreateFromDefinition();
+          DITContentRule newDCR = dcr.recreateFromDefinition(this);
           deregisterDITContentRule(dcr);
           registerDITContentRule(newDCR, true);
           rebuildDependentElements(dcr, depth+1);
@@ -3211,7 +3232,7 @@
       {
         if (mru.getAttributes().contains(t))
         {
-          MatchingRuleUse newMRU = mru.recreateFromDefinition();
+          MatchingRuleUse newMRU = mru.recreateFromDefinition(this);
           deregisterMatchingRuleUse(mru);
           registerMatchingRuleUse(newMRU, true);
           rebuildDependentElements(mru, depth+1);
@@ -3226,7 +3247,7 @@
       {
         if (oc.getSuperiorClasses().contains(c))
         {
-          ObjectClass newOC = oc.recreateFromDefinition();
+          ObjectClass newOC = oc.recreateFromDefinition(this);
           deregisterObjectClass(oc);
           registerObjectClass(newOC, true);
           rebuildDependentElements(oc, depth+1);
@@ -3240,7 +3261,7 @@
         {
           if (nf != null)
           {
-            NameForm newNF = nf.recreateFromDefinition();
+            NameForm newNF = nf.recreateFromDefinition(this);
             deregisterNameForm(nf);
             registerNameForm(newNF, true);
             rebuildDependentElements(nf, depth+1);
@@ -3253,7 +3274,7 @@
         if (dcr.getStructuralClass().equals(c) ||
             dcr.getAuxiliaryClasses().contains(c))
         {
-          DITContentRule newDCR = dcr.recreateFromDefinition();
+          DITContentRule newDCR = dcr.recreateFromDefinition(this);
           deregisterDITContentRule(dcr);
           registerDITContentRule(newDCR, true);
           rebuildDependentElements(dcr, depth+1);
@@ -3266,7 +3287,7 @@
       DITStructureRule dsr = ditStructureRulesByNameForm.get(n);
       if (dsr != null)
       {
-        DITStructureRule newDSR = dsr.recreateFromDefinition();
+        DITStructureRule newDSR = dsr.recreateFromDefinition(this);
         deregisterDITStructureRule(dsr);
         registerDITStructureRule(newDSR, true);
         rebuildDependentElements(dsr, depth+1);
@@ -3279,7 +3300,7 @@
       {
         if (dsr.getSuperiorRules().contains(d))
         {
-          DITStructureRule newDSR = dsr.recreateFromDefinition();
+          DITStructureRule newDSR = dsr.recreateFromDefinition(this);
           deregisterDITStructureRule(dsr);
           registerDITStructureRule(newDSR, true);
           rebuildDependentElements(dsr, depth+1);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/SchemaFileElement.java b/opendj-sdk/opends/src/server/org/opends/server/types/SchemaFileElement.java
index cf6399a..7636620 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/SchemaFileElement.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/SchemaFileElement.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2008 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 package org.opends.server.types;
 
@@ -88,25 +89,24 @@
 
 
   /**
-   * Creates a new instance of this schema element based on the
-   * definition from the schema file.  The new instance should also
-   * be created with all appropriate state information that may not
-   * be directly represented in the schema definition (e.g., the name
-   * of the schema file containing the definition).
-   * <BR><BR>
-   * Whenever an existing schema file element is modified with the
-   * server online, this method will be invoked to recreate any
-   * schema elements that might have been dependent upon the
-   * modified element.
+   * Creates a new instance of this schema element based on the definition from
+   * the schema file. The new instance should also be created with all
+   * appropriate state information that may not be directly represented in the
+   * schema definition (e.g., the name of the schema file containing the
+   * definition). <BR>
+   * <BR>
+   * Whenever an existing schema file element is modified with the server
+   * online, this method will be invoked to recreate any schema elements that
+   * might have been dependent upon the modified element.
    *
-   * @return  A new instance of this schema element based on the
-   *          definition.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting
-   *                              to create the new instance of this
-   *                              schema element.
+   * @param schema
+   *          The schema which should be used for resolving dependencies.
+   * @return A new instance of this schema element based on the definition.
+   * @throws DirectoryException
+   *           If a problem occurs while attempting to create the new instance
+   *           of this schema element.
    */
-  public SchemaFileElement recreateFromDefinition()
+  public SchemaFileElement recreateFromDefinition(Schema schema)
          throws DirectoryException;
 }
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java
index bf0b91b..881f5f0 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java
@@ -23,7 +23,7 @@
  *
  *
  *      Copyright 2006-2011 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2012 ForgeRock AS
+ *      Portions Copyright 2011-2013 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -66,6 +66,7 @@
 import static org.testng.Assert.*;
 
 import static org.opends.server.TestCaseUtils.TEST_BACKEND_ID;
+import static org.opends.server.TestCaseUtils.applyModifications;
 import static org.opends.server.protocols.ldap.LDAPConstants.*;
 
 
@@ -1298,7 +1299,7 @@
     ModifyOperation modifyOperation =
          conn.processModify(ByteString.valueOf("uid=test.user," + baseDN),
                             mods);
-    assertFalse(modifyOperation.getResultCode() == ResultCode.SUCCESS);
+    assertEquals(modifyOperation.getResultCode(), ResultCode.OBJECTCLASS_VIOLATION);
     retrieveFailedOperationElements(modifyOperation);
   }
 
@@ -5164,7 +5165,7 @@
     InternalClientConnection conn =
          InternalClientConnection.getRootConnection();
 
-    String certificateValue = 
+    String certificateValue =
       "MIICpTCCAg6gAwIBAgIJALeoA6I3ZC/cMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV" +
       "BAYTAlVTMRMwEQYDVQQHEwpDdXBlcnRpb25lMRwwGgYDVQQLExNQcm9kdWN0IERl" +
       "dmVsb3BtZW50MRQwEgYDVQQDEwtCYWJzIEplbnNlbjAeFw0xMjA1MDIxNjM0MzVa" +
@@ -5177,8 +5178,8 @@
       "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE" +
       "FLlZD3aKDa8jdhzoByOFMAJDs2osMB8GA1UdIwQYMBaAFLlZD3aKDa8jdhzoByOF" +
       "MAJDs2osMA0GCSqGSIb3DQEBBQUAA4GBAE5vccY8Ydd7by2bbwiDKgQqVyoKrkUg" +
-      "6CD0WRmc2pBeYX2z94/PWO5L3Fx+eIZh2wTxScF+FdRWJzLbUaBuClrxuy0Y5ifj" + 
-      "axuJ8LFNbZtsp1ldW3i84+F5+SYT+xI67ZcoAtwx/VFVI9s5I/Gkmu9f9nxjPpK7" + 
+      "6CD0WRmc2pBeYX2z94/PWO5L3Fx+eIZh2wTxScF+FdRWJzLbUaBuClrxuy0Y5ifj" +
+      "axuJ8LFNbZtsp1ldW3i84+F5+SYT+xI67ZcoAtwx/VFVI9s5I/Gkmu9f9nxjPpK7" +
       "1AIUXiE3Qcck";
 
     ArrayList<ByteString> values = new ArrayList<ByteString>();
@@ -5205,5 +5206,109 @@
     assertEquals(Base64.encode(a.iterator().next().getValue()), certificateValue);
   }
 
+
+
+  /**
+   * Tests to ensure that the compressed schema is refreshed after an object
+   * class is changed (OPENDJ-169).
+   *
+   * @param baseDN
+   *          The base DN to use.
+   * @throws Exception
+   *           If an unexpected problem occurs.
+   */
+  @Test(dataProvider = "baseDNs")
+  public void testCompressedSchemaRefresh(String baseDN) throws Exception
+  {
+    TestCaseUtils.clearJEBackend(true, "userRoot", baseDN);
+
+    Entry entry = TestCaseUtils.makeEntry("dn: cn=Test User," + baseDN,
+        "objectClass: top", "objectClass: person",
+        "objectClass: organizationalPerson", "sn: User", "cn: Test User");
+
+    InternalClientConnection conn = InternalClientConnection
+        .getRootConnection();
+
+    AddOperation addOperation = conn.processAdd(entry.getDN(),
+        entry.getObjectClasses(), entry.getUserAttributes(),
+        entry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    // First check that adding "dc" fails because it is not allowed by
+    // inetOrgPerson.
+    ArrayList<ByteString> values = new ArrayList<ByteString>();
+    values.add(ByteString.valueOf("foo"));
+    LDAPAttribute attr = new LDAPAttribute("dc", values);
+    ArrayList<RawModification> mods = new ArrayList<RawModification>();
+    mods.add(new LDAPModification(ModificationType.ADD, attr));
+    ModifyOperation modifyOperation = conn.processModify(
+        ByteString.valueOf("cn=Test User," + baseDN), mods);
+    assertEquals(modifyOperation.getResultCode(), ResultCode.OBJECTCLASS_VIOLATION);
+
+    assertEquals(
+        applyModifications(
+            false,
+            "dn: cn=schema",
+            "changetype: modify",
+            "delete: objectclasses",
+            "objectClasses: ( 2.5.6.6 NAME 'person' SUP top STRUCTURAL MUST ( sn $ cn )"
+                + "  MAY ( userPassword $ telephoneNumber $ seeAlso $ description )"
+                + "  X-ORIGIN 'RFC 4519' )",
+            "-",
+            "add: objectclasses",
+            "objectClasses: ( 2.5.6.6 NAME 'person' SUP top STRUCTURAL MUST ( sn $ cn )"
+                + "  MAY ( dc $ userPassword $ telephoneNumber $ seeAlso $ description )"
+                + "  X-ORIGIN 'RFC 4519' )"), 0, "Schema update failed");
+    try
+    {
+      // Modify existing entry.
+      modifyOperation = conn.processModify(
+          ByteString.valueOf("cn=Test User," + baseDN), mods);
+      assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+
+      // Add new entry and modify.
+      entry = TestCaseUtils.makeEntry("dn: cn=Test User2," + baseDN,
+          "objectClass: top", "objectClass: person",
+          "objectClass: organizationalPerson", "sn: User2", "cn: Test User2");
+
+      addOperation = conn.processAdd(entry.getDN(), entry.getObjectClasses(),
+          entry.getUserAttributes(), entry.getOperationalAttributes());
+      assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+      modifyOperation = conn.processModify(
+          ByteString.valueOf("cn=Test User2," + baseDN), mods);
+      assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+    }
+    finally
+    {
+      assertEquals(
+          applyModifications(
+              false,
+              "dn: cn=schema",
+              "changetype: modify",
+              "delete: objectclasses",
+              "objectClasses: ( 2.5.6.6 NAME 'person' SUP top STRUCTURAL MUST ( sn $ cn )"
+                  + "  MAY ( dc $ userPassword $ telephoneNumber $ seeAlso $ description )"
+                  + "  X-ORIGIN 'RFC 4519' )",
+              "-",
+              "add: objectclasses",
+              "objectClasses: ( 2.5.6.6 NAME 'person' SUP top STRUCTURAL MUST ( sn $ cn )"
+                  + "  MAY ( userPassword $ telephoneNumber $ seeAlso $ description )"
+                  + "  X-ORIGIN 'RFC 4519' )"), 0, "Schema update failed");
+
+      // Add new entry and modify (this time it should fail).
+      entry = TestCaseUtils.makeEntry("dn: cn=Test User3," + baseDN,
+          "objectClass: top", "objectClass: person",
+          "objectClass: organizationalPerson", "sn: User3", "cn: Test User3");
+      addOperation = conn.processAdd(entry.getDN(), entry.getObjectClasses(),
+          entry.getUserAttributes(), entry.getOperationalAttributes());
+      assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+      modifyOperation = conn.processModify(
+          ByteString.valueOf("cn=Test User3," + baseDN), mods);
+      assertEquals(modifyOperation.getResultCode(), ResultCode.OBJECTCLASS_VIOLATION);
+    }
+
+  }
 }
 

--
Gitblit v1.10.0