mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

neil_a_wilson
31.55.2007 35463f0a916da67afa96909112b0314e25d4432f
opendj-sdk/opends/resource/config/config.ldif
@@ -182,7 +182,7 @@
ds-cfg-backend-import-thread-count: 8
ds-cfg-backend-entries-compressed: false
ds-cfg-backend-deadlock-retry-limit: 10
ds-cfg-backend-compact-encoding: false
ds-cfg-backend-compact-encoding: true
ds-cfg-database-cache-percent: 10
ds-cfg-database-cache-size: 0 megabytes
ds-cfg-database-txn-no-sync: false
opendj-sdk/opends/src/messages/messages/jeb.properties
@@ -314,3 +314,23 @@
 the modify DN operation to be aborted while moving and/or renaming an entry \
 from %s to %s because the change to that entry violated the server schema \
 configuration:  %s
SEVERE_ERR_JEB_COMPSCHEMA_CANNOT_DECODE_OC_TOKEN_165=An error occurred while \
 attempting to decode an object class set token from the JE compressed \
 schema definitions:  %s
SEVERE_ERR_JEB_COMPSCHEMA_CANNOT_DECODE_AD_TOKEN_166=An error occurred while \
 attempting to decode an attribute description token from the JE compressed \
 schema definitions:  %s
SEVERE_ERR_JEB_COMPSCHEMA_CANNOT_STORE_STATUS_167=An error occurred while \
 attempting to store compressed schema information in the database.  The \
 result returned was:  %s
SEVERE_ERR_JEB_COMPSCHEMA_CANNOT_STORE_EX_168=An error occurred while \
 attempting to store compressed schema information in the database:  %s
SEVERE_ERR_JEB_COMPSCHEMA_CANNOT_STORE_MULTIPLE_FAILURES_169=The server was \
 unable to store compressed schema information in the database after multiple \
 attempts
SEVERE_ERR_JEB_COMPSCHEMA_UNKNOWN_OC_TOKEN_170=Unable to decode the provided \
 object class set because it used an undefined token %s
SEVERE_ERR_JEB_COMPSCHEMA_UNRECOGNIZED_AD_TOKEN_171=Unable to decode the \
 provided attribute because it used an undefined attribute description token \
 %s
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/DataConfig.java
@@ -28,6 +28,8 @@
import org.opends.server.types.EntryEncodeConfig;
import static org.opends.server.util.Validator.*;
/**
 * Configuration class to indicate desired compression and cryptographic options
 * for the data stored in the database.
@@ -50,12 +52,25 @@
   * @param compressed true if data should be compressed, false if not.
   * @param compactEncoding true if data should be encoded in compact form,
   * false if not.
   * @param compressedSchema the compressed schema manager to use.  It must not
   * be {@code null} if compactEncoding is {@code true}.
   */
  public DataConfig(boolean compressed, boolean compactEncoding)
  public DataConfig(boolean compressed, boolean compactEncoding,
                    JECompressedSchema compressedSchema)
  {
    this.compressed = compressed;
    this.encodeConfig = new EntryEncodeConfig(false, compactEncoding,
                                              compactEncoding);
    if (compressedSchema == null)
    {
      ensureTrue(! compactEncoding);
      this.encodeConfig = new EntryEncodeConfig(false, compactEncoding, false);
    }
    else
    {
      this.encodeConfig =
           new EntryEncodeConfig(false, compactEncoding, compactEncoding,
                                 compressedSchema);
    }
  }
  /**
@@ -93,11 +108,24 @@
   * writing to the database.
   * @param compactEncoding true if data should be encoded in compact form,
   * false if not.
   * @param compressedSchema The compressed schema manager to use.  It must not
   * be {@code null} if compactEncoding is {@code true}.
   */
  public void setCompactEncoding(boolean compactEncoding)
  public void setCompactEncoding(boolean compactEncoding,
                                 JECompressedSchema compressedSchema)
  {
    this.encodeConfig = new EntryEncodeConfig(false, compactEncoding,
                                              compactEncoding);
    if (compressedSchema == null)
    {
      ensureTrue(! compactEncoding);
      this.encodeConfig = new EntryEncodeConfig(false, compactEncoding,
                                                compactEncoding);
    }
    else
    {
      this.encodeConfig = new EntryEncodeConfig(false, compactEncoding,
                                                compactEncoding,
                                                compressedSchema);
    }
  }
  /**
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -483,7 +483,8 @@
    {
      DataConfig entryDataConfig =
          new DataConfig(config.isBackendEntriesCompressed(),
                         config.isBackendCompactEncoding());
                         config.isBackendCompactEncoding(),
                         rootContainer.getCompressedSchema());
      id2entry = new ID2Entry(databasePrefix + "_" + ID2ENTRY_DATABASE_NAME,
                              entryDataConfig, env, this);
@@ -565,6 +566,18 @@
  }
  /**
   * Retrieves a reference to the root container in which this entry container
   * exists.
   *
   * @return  A reference to the root container in which this entry container
   *          exists.
   */
  public RootContainer getRootContainer()
  {
    return rootContainer;
  }
  /**
   * Get the DN database used by this entry entryContainer. The entryContainer
   * must have been opened.
   *
@@ -4270,7 +4283,8 @@
    DataConfig entryDataConfig =
        new DataConfig(cfg.isBackendEntriesCompressed(),
                       cfg.isBackendCompactEncoding());
                       cfg.isBackendCompactEncoding(),
                       rootContainer.getCompressedSchema());
    id2entry.setDataConfig(entryDataConfig);
    this.config = cfg;
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ExportJob.java
@@ -228,7 +228,8 @@
        Entry entry = null;
        try
        {
          entry = JebFormat.entryFromDatabase(data.getData());
          entry = JebFormat.entryFromDatabase(data.getData(),
                       entryContainer.getRootContainer().getCompressedSchema());
        }
        catch (Exception e)
        {
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java
@@ -242,7 +242,8 @@
      case JebFormat.FORMAT_VERSION :
        try
        {
          entry = JebFormat.entryFromDatabase(entryBytes);
          entry = JebFormat.entryFromDatabase(entryBytes,
                       entryContainer.getRootContainer().getCompressedSchema());
        }
        catch (Exception e)
        {
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/IndexRebuildThread.java
@@ -389,7 +389,8 @@
        try
        {
          EntryID entryID = new EntryID(key);
          Entry entry = JebFormat.entryFromDatabase(data.getData());
          Entry entry = JebFormat.entryFromDatabase(data.getData(),
                             ec.getRootContainer().getCompressedSchema());
          // Insert into dn2id.
          if (dn2id.insert(txn, entry.getDN(), entryID))
@@ -471,7 +472,8 @@
        try
        {
          EntryID entryID = new EntryID(key);
          Entry entry = JebFormat.entryFromDatabase(data.getData());
          Entry entry = JebFormat.entryFromDatabase(data.getData(),
                             ec.getRootContainer().getCompressedSchema());
          // Insert into dn2uri.
          if (dn2uri.addEntry(txn, entry))
@@ -556,7 +558,8 @@
        try
        {
          EntryID entryID = new EntryID(key);
          Entry entry = JebFormat.entryFromDatabase(data.getData());
          Entry entry = JebFormat.entryFromDatabase(data.getData(),
                             ec.getRootContainer().getCompressedSchema());
          // Check that the parent entry exists.
          DN parentDN = ec.getParentWithinBase(entry.getDN());
@@ -668,7 +671,8 @@
        try
        {
          EntryID entryID = new EntryID(key);
          Entry entry = JebFormat.entryFromDatabase(data.getData());
          Entry entry = JebFormat.entryFromDatabase(data.getData(),
                             ec.getRootContainer().getCompressedSchema());
          // Check that the parent entry exists.
          DN parentDN = ec.getParentWithinBase(entry.getDN());
@@ -805,7 +809,8 @@
        try
        {
          EntryID entryID = new EntryID(key);
          Entry entry = JebFormat.entryFromDatabase(data.getData());
          Entry entry = JebFormat.entryFromDatabase(data.getData(),
                             ec.getRootContainer().getCompressedSchema());
          // Insert into attribute indexType.
          if(index.addEntry(txn, entryID, entry))
@@ -882,7 +887,8 @@
        try
        {
          EntryID entryID = new EntryID(key);
          Entry entry = JebFormat.entryFromDatabase(data.getData());
          Entry entry = JebFormat.entryFromDatabase(data.getData(),
                             ec.getRootContainer().getCompressedSchema());
          // Insert into attribute indexType.
          if(vlvIndex.addEntry(txn, entryID, entry))
@@ -967,7 +973,8 @@
        try
        {
          EntryID entryID = new EntryID(key);
          Entry entry = JebFormat.entryFromDatabase(data.getData());
          Entry entry = JebFormat.entryFromDatabase(data.getData(),
                             ec.getRootContainer().getCompressedSchema());
          // Insert into attribute indexType.
          if(index.addEntry(txn, entryID, entry))
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JECompressedSchema.java
New file
@@ -0,0 +1,765 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.backends.jeb;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DeadlockException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
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.ASN1Element;
import org.opends.server.protocols.asn1.ASN1Exception;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.protocols.asn1.ASN1Sequence;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteArray;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.ObjectClass;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.messages.JebMessages.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * This class provides a compressed schema implementation whose definitions are
 * stored in a Berkeley DB JE database.
 */
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";
  /**
   * The name of the database used to store compressed object class set
   * definitions.
   */
  public 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<ByteArray,AttributeType> atDecodeMap;
  // The map between encoded representations and attribute options.
  private ConcurrentHashMap<ByteArray,LinkedHashSet<String>> aoDecodeMap;
  // The map between encoded representations and object class sets.
  private ConcurrentHashMap<ByteArray,Map<ObjectClass,String>> ocDecodeMap;
  // The map between attribute descriptions and their encoded
  // representations.
  private ConcurrentHashMap<AttributeType,
               ConcurrentHashMap<LinkedHashSet<String>,ByteArray>> adEncodeMap;
  // The map between object class sets and encoded representations.
  private ConcurrentHashMap<Map<ObjectClass,String>,ByteArray> ocEncodeMap;
  // 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;
  /**
   * 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 problem occurs while loading the
   *                             compressed schema definitions from the
   *                             database.
   */
  public JECompressedSchema(Environment environment)
         throws DatabaseException
  {
    this.environment = environment;
    atDecodeMap = new ConcurrentHashMap<ByteArray,AttributeType>();
    aoDecodeMap = new ConcurrentHashMap<ByteArray,LinkedHashSet<String>>();
    ocDecodeMap = new ConcurrentHashMap<ByteArray,Map<ObjectClass,String>>();
    adEncodeMap =
         new ConcurrentHashMap<AttributeType,
                  ConcurrentHashMap<LinkedHashSet<String>,ByteArray>>();
    ocEncodeMap = new ConcurrentHashMap<Map<ObjectClass,String>,ByteArray>();
    adCounter = new AtomicInteger(1);
    ocCounter = new AtomicInteger(1);
    load();
  }
  /**
   * Loads the compressed schema information from the database.
   *
   * @throws  DatabaseException  If a problem occurs while loading the
   *                             definitions from the database.
   */
  private void load()
          throws DatabaseException
  {
    DatabaseConfig dbConfig = new DatabaseConfig();
    if(environment.getConfig().getReadOnly())
    {
      dbConfig.setReadOnly(true);
      dbConfig.setAllowCreate(false);
      dbConfig.setTransactional(false);
    }
    else if(!environment.getConfig().getTransactional())
    {
      dbConfig.setAllowCreate(true);
      dbConfig.setTransactional(false);
      dbConfig.setDeferredWrite(true);
    }
    else
    {
      dbConfig.setAllowCreate(true);
      dbConfig.setTransactional(true);
    }
    adDatabase = environment.openDatabase(null, DB_NAME_AD, dbConfig);
    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
    // initialize the object class counter to one greater than that.
    Cursor ocCursor = ocDatabase.openCursor(null, null);
    int highestToken = 0;
    try
    {
      DatabaseEntry keyEntry   = new DatabaseEntry();
      DatabaseEntry valueEntry = new DatabaseEntry();
      OperationStatus status = ocCursor.getFirst(keyEntry, valueEntry,
                                                 LockMode.READ_UNCOMMITTED);
      while (status == OperationStatus.SUCCESS)
      {
        ByteArray token = new ByteArray(keyEntry.getData());
        highestToken = Math.max(highestToken, decodeInt(token.array()));
        ArrayList<ASN1Element> elements =
             ASN1Sequence.decodeAsSequence(valueEntry.getData()).elements();
        LinkedHashMap<ObjectClass,String> ocMap =
             new LinkedHashMap<ObjectClass,String>(elements.size());
        for (int i=0; i < elements.size(); i++)
        {
          ASN1OctetString os = elements.get(i).decodeAsOctetString();
          String ocName = os.stringValue();
          String lowerName = toLowerCase(ocName);
          ObjectClass oc = DirectoryServer.getObjectClass(lowerName, true);
          ocMap.put(oc, ocName);
        }
        ocEncodeMap.put(ocMap, token);
        ocDecodeMap.put(token, ocMap);
        status = ocCursor.getNext(keyEntry, valueEntry,
                                  LockMode.READ_UNCOMMITTED);
      }
    }
    catch (ASN1Exception ae)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, ae);
      }
      Message m =
           ERR_JEB_COMPSCHEMA_CANNOT_DECODE_OC_TOKEN.get(ae.getMessage());
      throw new DatabaseException(m.toString(), 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;
    try
    {
      DatabaseEntry keyEntry   = new DatabaseEntry();
      DatabaseEntry valueEntry = new DatabaseEntry();
      OperationStatus status = adCursor.getFirst(keyEntry, valueEntry,
                                                 LockMode.READ_UNCOMMITTED);
      while (status == OperationStatus.SUCCESS)
      {
        ByteArray token = new ByteArray(keyEntry.getData());
        highestToken = Math.max(highestToken, decodeInt(token.array()));
        ArrayList<ASN1Element> elements =
             ASN1Sequence.decodeAsSequence(valueEntry.getData()).elements();
        ASN1OctetString os = elements.get(0).decodeAsOctetString();
        String attrName = os.stringValue();
        String lowerName = toLowerCase(attrName);
        AttributeType attrType =
             DirectoryServer.getAttributeType(lowerName, true);
        LinkedHashSet<String> options =
             new LinkedHashSet<String>(elements.size()-1);
        for (int i=1; i < elements.size(); i++)
        {
          os = elements.get(i).decodeAsOctetString();
          options.add(os.stringValue());
        }
        atDecodeMap.put(token, attrType);
        aoDecodeMap.put(token, options);
        ConcurrentHashMap<LinkedHashSet<String>,ByteArray> map =
             adEncodeMap.get(attrType);
        if (map == null)
        {
          map = new ConcurrentHashMap<LinkedHashSet<String>,ByteArray>(1);
          map.put(options, token);
          adEncodeMap.put(attrType, map);
        }
        else
        {
          map.put(options, token);
        }
        status = adCursor.getNext(keyEntry, valueEntry,
                                  LockMode.READ_UNCOMMITTED);
      }
    }
    catch (ASN1Exception ae)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, ae);
      }
      Message m =
           ERR_JEB_COMPSCHEMA_CANNOT_DECODE_AD_TOKEN.get(ae.getMessage());
      throw new DatabaseException(m.toString(), 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 byte[] encodeObjectClasses(Map<ObjectClass,String> objectClasses)
         throws DirectoryException
  {
    ByteArray encodedClasses = ocEncodeMap.get(objectClasses);
    if (encodedClasses == null)
    {
      synchronized (ocEncodeMap)
      {
        int setValue = ocCounter.getAndIncrement();
        byte[] tokenArray = encodeInt(setValue);
        ArrayList<ASN1Element> elements =
             new ArrayList<ASN1Element>(objectClasses.size());
        for (String ocName : objectClasses.values())
        {
          elements.add(new ASN1OctetString(ocName));
        }
        byte[] encodedOCs = new ASN1Sequence(elements).encode();
        store(ocDatabase, tokenArray, encodedOCs);
        encodedClasses = new ByteArray(tokenArray);
        ocEncodeMap.put(objectClasses, encodedClasses);
        ocDecodeMap.put(encodedClasses, objectClasses);
        return tokenArray;
      }
    }
    else
    {
      return encodedClasses.array();
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public Map<ObjectClass,String> decodeObjectClasses(
                                      byte[] encodedObjectClasses)
         throws DirectoryException
  {
    ByteArray byteArray = new ByteArray(encodedObjectClasses);
    Map<ObjectClass,String> ocMap = ocDecodeMap.get(byteArray);
    if (ocMap == null)
    {
      Message message = ERR_JEB_COMPSCHEMA_UNKNOWN_OC_TOKEN.get(
                             bytesToHex(encodedObjectClasses));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message);
    }
    else
    {
      return ocMap;
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public byte[] encodeAttribute(Attribute attribute)
         throws DirectoryException
  {
    AttributeType type = attribute.getAttributeType();
    LinkedHashSet<String> options = attribute.getOptions();
    ConcurrentHashMap<LinkedHashSet<String>,ByteArray> map =
         adEncodeMap.get(type);
    if (map == null)
    {
      byte[] tokenArray;
      synchronized (adEncodeMap)
      {
        map = new ConcurrentHashMap<LinkedHashSet<String>,ByteArray>(1);
        int intValue = adCounter.getAndIncrement();
        tokenArray = encodeInt(intValue);
        ByteArray byteArray = new ByteArray(tokenArray);
        map.put(options,byteArray);
        ArrayList<ASN1Element> elements =
             new ArrayList<ASN1Element>(options.size()+1);
        elements.add(new ASN1OctetString(attribute.getName()));
        for (String option : options)
        {
          elements.add(new ASN1OctetString(option));
        }
        byte[] encodedValue = new ASN1Sequence(elements).encode();
        store(adDatabase, tokenArray, encodedValue);
        adEncodeMap.put(type, map);
        atDecodeMap.put(byteArray, type);
        aoDecodeMap.put(byteArray, options);
      }
      return encodeAttribute(tokenArray, attribute);
    }
    else
    {
      ByteArray byteArray = map.get(options);
      if (byteArray == null)
      {
        byte[] tokenArray;
        synchronized (map)
        {
          int intValue = adCounter.getAndIncrement();
          tokenArray = encodeInt(intValue);
          byteArray = new ByteArray(tokenArray);
          map.put(options,byteArray);
          ArrayList<ASN1Element> elements =
               new ArrayList<ASN1Element>(options.size()+1);
          elements.add(new ASN1OctetString(attribute.getName()));
          for (String option : options)
          {
            elements.add(new ASN1OctetString(option));
          }
          byte[] encodedValue = new ASN1Sequence(elements).encode();
          store(adDatabase, tokenArray, encodedValue);
          atDecodeMap.put(byteArray, type);
          aoDecodeMap.put(byteArray, options);
        }
        return encodeAttribute(tokenArray, attribute);
      }
      else
      {
        return encodeAttribute(byteArray.array(), attribute);
      }
    }
  }
  /**
   * Encodes the information in the provided attribute to a byte
   * array.
   *
   * @param  adArray    The byte array that is a placeholder for the
   *                    attribute type and set of options.
   * @param  attribute  The attribute to be encoded.
   *
   * @return  An encoded representation of the provided attribute.
   */
  private byte[] encodeAttribute(byte[] adArray, Attribute attribute)
  {
    LinkedHashSet<AttributeValue> values = attribute.getValues();
    int totalValuesLength = 0;
    byte[][] subArrays = new  byte[values.size()*2][];
    int pos = 0;
    for (AttributeValue v : values)
    {
      byte[] vBytes = v.getValueBytes();
      byte[] lBytes = ASN1Element.encodeLength(vBytes.length);
      subArrays[pos++] = lBytes;
      subArrays[pos++] = vBytes;
      totalValuesLength += lBytes.length + vBytes.length;
    }
    byte[] adArrayLength = ASN1Element.encodeLength(adArray.length);
    byte[] countBytes = ASN1Element.encodeLength(values.size());
    int totalLength = adArrayLength.length + adArray.length +
                      countBytes.length + totalValuesLength;
    byte[] array = new byte[totalLength];
    System.arraycopy(adArrayLength, 0, array, 0,
                     adArrayLength.length);
    pos = adArrayLength.length;
    System.arraycopy(adArray, 0, array, pos, adArray.length);
    pos += adArray.length;
    System.arraycopy(countBytes, 0, array, pos, countBytes.length);
    pos += countBytes.length;
    for (int i=0; i < subArrays.length; i++)
    {
      System.arraycopy(subArrays[i], 0, array, pos,
                       subArrays[i].length);
      pos += subArrays[i].length;
    }
    return array;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public Attribute decodeAttribute(byte[] encodedEntry, int startPos,
                                   int length)
         throws DirectoryException
  {
    // Figure out how many bytes are in the token that is the placeholder for
    // the attribute description.
    int pos = startPos;
    int adArrayLength = encodedEntry[pos] & 0x7F;
    if (adArrayLength != encodedEntry[pos++])
    {
      int numLengthBytes = adArrayLength;
      adArrayLength = 0;
      for (int i=0; i < numLengthBytes; i++, pos++)
      {
        adArrayLength = (adArrayLength << 8) | (encodedEntry[pos] & 0xFF);
      }
    }
    // Get the attribute description token and make sure it resolves to an
    // attribute type and option set.
    ByteArray adArray = new ByteArray(new byte[adArrayLength]);
    System.arraycopy(encodedEntry, pos, adArray.array(), 0, adArrayLength);
    pos += adArrayLength;
    AttributeType attrType = atDecodeMap.get(adArray);
    LinkedHashSet<String> options = aoDecodeMap.get(adArray);
    if ((attrType == null) || (options == null))
    {
      Message message = ERR_JEB_COMPSCHEMA_UNRECOGNIZED_AD_TOKEN.get(
                             bytesToHex(adArray.array()));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message);
    }
    // Determine the number of values for the attribute.
    int numValues = encodedEntry[pos] & 0x7F;
    if (numValues != encodedEntry[pos++])
    {
      int numValuesBytes = numValues;
      numValues = 0;
      for (int i=0; i < numValuesBytes; i++, pos++)
      {
        numValues = (numValues << 8) | (encodedEntry[pos] & 0xFF);
      }
    }
    // Read the appropriate number of values.
    LinkedHashSet<AttributeValue> values =
         new LinkedHashSet<AttributeValue>(numValues);
    for (int i=0; i < numValues; i++)
    {
      int valueLength = encodedEntry[pos] & 0x7F;
      if (valueLength != encodedEntry[pos++])
      {
        int valueLengthBytes = valueLength;
        valueLength = 0;
        for (int j=0; j < valueLengthBytes; j++, pos++)
        {
          valueLength = (valueLength << 8) | (encodedEntry[pos] & 0xFF);
        }
      }
      byte[] valueBytes = new byte[valueLength];
      System.arraycopy(encodedEntry, pos, valueBytes, 0, valueLength);
      pos += valueLength;
      values.add(new AttributeValue(attrType, new ASN1OctetString(valueBytes)));
    }
    return new Attribute(attrType, attrType.getPrimaryName(), options, values);
  }
  /**
   * 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, byte[] valueBytes)
          throws DirectoryException
  {
    boolean successful = false;
    DatabaseEntry keyEntry   = new DatabaseEntry(keyBytes);
    DatabaseEntry valueEntry = new DatabaseEntry(valueBytes);
    for (int i=0; i < 3; i++)
    {
      try
      {
        OperationStatus status = database.putNoOverwrite(null, keyEntry,
                                                         valueEntry);
        if (status == OperationStatus.SUCCESS)
        {
          successful = true;
          break;
        }
        else
        {
          Message m = ERR_JEB_COMPSCHEMA_CANNOT_STORE_STATUS.get(
                           status.toString());
          throw new DirectoryException(
                         DirectoryServer.getServerErrorResultCode(), m);
        }
      }
      catch (DeadlockException de)
      {
        continue;
      }
      catch (DatabaseException de)
      {
        Message m = ERR_JEB_COMPSCHEMA_CANNOT_STORE_EX.get(de.getMessage());
        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                     m, de);
      }
    }
    if (! successful)
    {
      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;
  }
}
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JebFormat.java
@@ -27,6 +27,7 @@
package org.opends.server.backends.jeb;
import org.opends.server.api.CompressedSchema;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.asn1.ASN1Element;
import org.opends.server.protocols.asn1.ASN1Exception;
@@ -145,6 +146,7 @@
   * </pre>
   *
   * @param bytes A byte array containing the encoded database value.
   * @param compressedSchema The compressed schema manager to use when decoding.
   * @return The decoded entry.
   * @throws ASN1Exception If the data is not in the expected ASN.1 encoding
   * format.
@@ -154,17 +156,19 @@
   * compressed data.
   * @throws DirectoryException If a Directory Server error occurs.
   */
  static public Entry entryFromDatabase(byte[] bytes)
  static public Entry entryFromDatabase(byte[] bytes,
                                        CompressedSchema compressedSchema)
       throws DirectoryException,ASN1Exception,LDAPException,DataFormatException
  {
    byte[] uncompressedBytes = decodeDatabaseEntry(bytes);
    return decodeDirectoryServerEntry(uncompressedBytes);
    return decodeDirectoryServerEntry(uncompressedBytes, compressedSchema);
  }
  /**
   * Decode an entry from a ASN1 encoded DirectoryServerEntry.
   *
   * @param bytes A byte array containing the encoding of DirectoryServerEntry.
   * @param compressedSchema The compressed schema manager to use when decoding.
   * @return The decoded entry.
   * @throws ASN1Exception If the data is not in the expected ASN.1 encoding
   * format.
@@ -172,10 +176,11 @@
   * format.
   * @throws DirectoryException If a Directory Server error occurs.
   */
  static private Entry decodeDirectoryServerEntry(byte[] bytes)
  static private Entry decodeDirectoryServerEntry(byte[] bytes,
                            CompressedSchema compressedSchema)
       throws DirectoryException,ASN1Exception,LDAPException
  {
    return Entry.decode(bytes);
    return Entry.decode(bytes, compressedSchema);
  }
  /**
@@ -261,7 +266,7 @@
  static public byte[] entryToDatabase(Entry entry)
         throws DirectoryException
  {
    return entryToDatabase(entry, new DataConfig(false, false));
    return entryToDatabase(entry, new DataConfig(false, false, null));
  }
  /**
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/RootContainer.java
@@ -105,6 +105,13 @@
  private AtomicLong nextid = new AtomicLong(1);
  /**
   * The compressed schema manager for this backend.
   */
  private JECompressedSchema compressedSchema;
  /**
   * Creates a new RootContainer object. Each root container represents a JE
   * environment.
   *
@@ -119,6 +126,7 @@
    this.entryContainers = new ConcurrentHashMap<DN, EntryContainer>();
    this.backend = backend;
    this.config = config;
    this.compressedSchema = null;
    config.addJEChangeListener(this);
  }
@@ -217,6 +225,7 @@
      TRACER.debugInfo("Free memory in heap: %d bytes", heapFreeSize);
    }
    compressedSchema = new JECompressedSchema(env);
    openAndRegisterEntryContainers(config.getBackendBaseDN());
  }
@@ -322,6 +331,16 @@
  }
  /**
   * Retrieves the compressed schema manager for this backend.
   *
   * @return  The compressed schema manager for this backend.
   */
  public JECompressedSchema getCompressedSchema()
  {
    return compressedSchema;
  }
  /**
   * Get the DatabaseEnvironmentMonitor object for JE environment used by this
   * root container.
   *
@@ -493,6 +512,8 @@
      }
    }
    compressedSchema.close();
    if (env != null)
    {
      env.close();
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/VerifyJob.java
@@ -472,7 +472,8 @@
        Entry entry;
        try
        {
          entry = JebFormat.entryFromDatabase(data.getData());
          entry = JebFormat.entryFromDatabase(data.getData(),
                                 rootContainer.getCompressedSchema());
        }
        catch (Exception e)
        {
opendj-sdk/opends/src/server/org/opends/server/tools/DBTest.java
@@ -1319,7 +1319,9 @@
              try
              {
                formatedData = System.getProperty("line.separator") +
                    JebFormat.entryFromDatabase(data.getData()).toLDIFString();
                    JebFormat.entryFromDatabase(data.getData(),
                         ec.getRootContainer().getCompressedSchema()).
                              toLDIFString();
                dataLabel = INFO_LABEL_DBTEST_ENTRY.get();
              }
              catch(Exception e)
opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
@@ -45,6 +45,7 @@
import java.util.concurrent.locks.Lock;
import org.opends.server.api.AttributeValueDecoder;
import org.opends.server.api.CompressedSchema;
import org.opends.server.api.ProtocolElement;
import org.opends.server.api.plugin.LDIFPluginResult;
import org.opends.server.core.DirectoryServer;
@@ -4394,12 +4395,36 @@
  public static Entry decode(byte[] entryBytes)
         throws DirectoryException
  {
    return decode(entryBytes,
                  DirectoryServer.getDefaultCompressedSchema());
  }
  /**
   * Decodes the provided byte array as an entry.
   *
   * @param  entryBytes        The byte array containing the data to
   *                           be decoded.
   * @param  compressedSchema  The compressed schema manager to use
   *                           when decoding tokenized schema
   *                           elements.
   *
   * @return  The decoded entry.
   *
   * @throws  DirectoryException  If the provided byte array cannot be
   *                              decoded as an entry.
   */
  public static Entry decode(byte[] entryBytes,
                             CompressedSchema compressedSchema)
         throws DirectoryException
  {
    switch(entryBytes[0])
    {
      case 0x01:
        return decodeV1(entryBytes);
      case 0x02:
        return decodeV2(entryBytes);
        return decodeV2(entryBytes, compressedSchema);
      default:
        Message message = ERR_ENTRY_DECODE_UNRECOGNIZED_VERSION.get(
            byteToHex(entryBytes[0]));
@@ -4781,15 +4806,19 @@
   * Decodes the provided byte array as an entry using the V2
   * encoding.
   *
   * @param  entryBytes  The byte array containing the data to be
   *                     decoded.
   * @param  entryBytes        The byte array containing the data to
   *                           be decoded.
   * @param  compressedSchema  The compressed schema manager to use
   *                           when decoding tokenized schema
   *                           elements.
   *
   * @return  The decoded entry.
   *
   * @throws  DirectoryException  If the provided byte array cannot be
   *                              decoded as an entry.
   */
  public static Entry decodeV2(byte[] entryBytes)
  public static Entry decodeV2(byte[] entryBytes,
                               CompressedSchema compressedSchema)
         throws DirectoryException
  {
    try
@@ -4824,7 +4853,8 @@
      // Next is the encoded configuration itself.
      EntryEncodeConfig config =
           EntryEncodeConfig.decode(entryBytes, pos, configLength);
           EntryEncodeConfig.decode(entryBytes, pos, configLength,
                                    compressedSchema);
      pos += configLength;
opendj-sdk/opends/src/server/org/opends/server/types/EntryEncodeConfig.java
@@ -275,20 +275,23 @@
   * Decodes the entry encode configuration from the specified portion
   * of the given byte array.
   *
   * @param  encodedEntry  The byte array containing the encoded
   *                       entry.
   * @param  startPos      The position at which to start decoding the
   *                       encode configuration.
   * @param  length        The number of bytes contained in the encode
   *                       configuration.
   * @param  encodedEntry      The byte array containing the encoded
   *                           entry.
   * @param  startPos          The position at which to start decoding
   *                           the encode configuration.
   * @param  length            The number of bytes contained in the
   *                           encode configuration.
   * @param  compressedSchema  The compressed schema manager to use
   *                           when decoding.
   *
   * @return  The decoded configuration.
   *
   * @throws  DirectoryException  If the configuration cannot be
   *                              properly decoded.
   */
  public static EntryEncodeConfig decode(byte[] encodedEntry,
                                         int startPos, int length)
  public static EntryEncodeConfig
                     decode(byte[] encodedEntry, int startPos,
                     int length, CompressedSchema compressedSchema)
         throws DirectoryException
  {
    if (length != 1)
@@ -321,7 +324,8 @@
    }
    return new EntryEncodeConfig(excludeDN, compressAttrDescriptions,
                                 compressObjectClassSets);
                                 compressObjectClassSets,
                                 compressedSchema);
  }
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestJebFormat.java
@@ -207,7 +207,8 @@
    while ((entryBefore = reader.readEntry(false)) != null) {
      byte[] bytes = JebFormat.entryToDatabase(entryBefore);
      entryAfter = JebFormat.entryFromDatabase(bytes);
      entryAfter = JebFormat.entryFromDatabase(bytes,
                        DirectoryServer.getDefaultCompressedSchema());
      // check DN and number of attributes
      assertEquals(entryBefore.getAttributes().size(), entryAfter
@@ -329,14 +330,15 @@
    Entry entryBefore, entryAfterGeneric, entryAfterV2;
    while ((entryBefore = reader.readEntry(false)) != null) {
      byte[] entryBytes = entryBefore.encodeV2(config);
      entryAfterGeneric = Entry.decode(entryBytes);
      entryAfterGeneric = Entry.decode(entryBytes,
                                       config.getCompressedSchema());
      if (config.excludeDN())
      {
        entryAfterGeneric.setDN(entryBefore.getDN());
      }
      assertEquals(entryBefore, entryAfterGeneric);
      entryAfterV2 = Entry.decodeV2(entryBytes);
      entryAfterV2 = Entry.decodeV2(entryBytes, config.getCompressedSchema());
      if (config.excludeDN())
      {
        entryAfterV2.setDN(entryBefore.getDN());
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestVerifyJob.java
@@ -443,7 +443,8 @@
      DatabaseEntry key= new DatabaseEntry(shortBytes);
      Entry testEntry=bldStatEntry(junkDN);
      byte []entryBytes =
           JebFormat.entryToDatabase(testEntry, new DataConfig(false, false));
           JebFormat.entryToDatabase(testEntry,
                                     new DataConfig(false, false, null));
      DatabaseEntry data= new DatabaseEntry(entryBytes);
      assertTrue(id2entry.putRaw(txn, key, data));
@@ -773,7 +774,8 @@
    DatabaseEntry key= new EntryID(id).getDatabaseEntry();
    Entry testEntry=bldStatEntry(dn);
    byte []entryBytes =
         JebFormat.entryToDatabase(testEntry, new DataConfig(false, false));
         JebFormat.entryToDatabase(testEntry,
                                   new DataConfig(false, false, null));
    if(trashFormat)
      entryBytes[0] = 0x67;
    DatabaseEntry data= new DatabaseEntry(entryBytes);