| | |
| | | * |
| | | * |
| | | * 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; |
| | | |
| | | |
| | | |
| | |
| | | * 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(); |
| | | |
| | | |
| | | |
| | |
| | | */ |
| | | 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() |
| | |
| | | |
| | | 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()) |
| | | { |
| | |
| | | inputStream.close(); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | catch (final Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | |
| | | /** |
| | | * 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; |
| | | } |
| | | } |
| | | |