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