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

neil_a_wilson
31.55.2007 8466db8c12700bbab0835f5f1bfa71762e34ba4d
Add support into the JE backend that can be used to store its compact schema
encoding data in the database itself, and re-enable compact encoding by default
in the configuration. By storing the compact schema encoding data in the
database (a separate copy for each backend), we have eliminated the potential
problems in backup/restore and binary copy initialization if there are
dependencies on an external schematokens.dat file.

OpenDS Issue Number: 2158
1 files added
15 files modified
991 ■■■■■ changed files
opends/resource/config/config.ldif 2 ●●● patch | view | raw | blame | history
opends/src/messages/messages/jeb.properties 20 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/DataConfig.java 40 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/EntryContainer.java 18 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/ExportJob.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/ID2Entry.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/IndexRebuildThread.java 21 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/JECompressedSchema.java 765 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/JebFormat.java 15 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/RootContainer.java 21 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/VerifyJob.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/DBTest.java 4 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/Entry.java 40 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/EntryEncodeConfig.java 22 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestJebFormat.java 8 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestVerifyJob.java 6 ●●●●● patch | view | raw | blame | history
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
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
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);
    }
  }
  /**
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;
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)
        {
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)
        {
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))
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;
  }
}
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));
  }
  /**
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();
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)
        {
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)
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;
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);
  }
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());
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);