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; } } 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; } } 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; } } 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(); } 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; } } 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; } 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; } 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(); 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; } 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; } 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; } 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; } } 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); 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; } 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); } } }