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