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

Fabio Pistolesi
20.29.2016 d8ecac31301960d58a6dc856939a97d709e82773
OPENDJ-2617 Add confidentiality (encryption) option for replication changelog

Add an option to encrypt records in the replication change-log, optionally specifying a cipher and key length.
Similar to what is done for id2entry, each record is encrypted independently, with two bytes prepended to the cipher text with a tag and a version byte.
Existing data is not rewritten encrypted; change number indexer is not encrypted either.
24 files modified
504 ■■■■ changed files
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/ReplicationServerConfiguration.xml 100 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/resource/schema/02-config.ldif 5 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/AttributeIndex.java 5 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DefaultIndex.java 17 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java 35 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java 4 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoSuite.java 85 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/protocol/ReplicationMsg.java 5 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServer.java 20 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/BlockLogWriter.java 4 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/FileChangeNumberIndexDB.java 3 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/FileChangelogDB.java 9 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/FileReplicaDB.java 78 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/RecordParser.java 6 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/ReplicationEnvironment.java 7 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/types/CryptoManager.java 3 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/messages/org/opends/messages/replication.properties 5 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/DefaultIndexTest.java 8 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/OnDiskMergeImporterTest.java 7 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/replication/server/ReplServerFakeConfiguration.java 24 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/BlockLogReaderWriterTest.java 5 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/FileReplicaDBTest.java 41 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/LogFileTest.java 3 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/ReplicationEnvironmentTest.java 25 ●●●●● patch | view | raw | blame | history
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/ReplicationServerConfiguration.xml
@@ -13,7 +13,7 @@
  information: "Portions Copyright [year] [name of copyright owner]".
  Copyright 2007-2010 Sun Microsystems, Inc.
  Portions copyright 2011-2015 ForgeRock AS.
  Portions copyright 2011-2016 ForgeRock AS.
  ! -->
<adm:managed-object name="replication-server"
  plural-name="replication-servers"
@@ -368,4 +368,102 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="cipher-transformation">
    <adm:synopsis>
      Specifies the cipher for the directory server.
      The syntax is "algorithm/mode/padding".
    </adm:synopsis>
    <adm:description>
      The full transformation is required: specifying only an algorithm
      and allowing the cipher provider to supply the default mode and
      padding is not supported, because there is no guarantee these
      default values are the same among different implementations.
      Some cipher algorithms, including RC4 and ARCFOUR, do not have a
      mode or padding, and hence must be specified using NONE for the
      mode field and NoPadding for the padding field. For example,
      RC4/NONE/NoPadding.
    </adm:description>
    <adm:requires-admin-action>
      <adm:none>
        <adm:synopsis>
          Changes to this property take effect immediately but
          only affect cryptographic operations performed after the
          change.
        </adm:synopsis>
      </adm:none>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>AES/CBC/PKCS5Padding</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-cipher-transformation</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="cipher-key-length">
    <adm:synopsis>
      Specifies the key length in bits for the preferred cipher.
    </adm:synopsis>
    <adm:requires-admin-action>
      <adm:none>
        <adm:synopsis>
          Changes to this property take effect immediately but
          only affect cryptographic operations performed after the
          change.
        </adm:synopsis>
      </adm:none>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>128</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-cipher-key-length</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="confidentiality-enabled">
    <adm:synopsis>
      Indicates whether the replication change-log should make records readable only by Directory Server.
      Throughput and disk space are affected by the more expensive operations taking place.
    </adm:synopsis>
    <adm:description>
      Confidentiality is achieved by encrypting records on all domains managed by this replication server.
      Encrypting the records prevents unauthorized parties from accessing contents of LDAP operations.
      For complete protection, consider enabling secure communications between servers.
      Change number indexing is not affected by the setting.
    </adm:description>
    <adm:requires-admin-action>
      <adm:none>
        <adm:synopsis>
          Changes to this property take effect immediately but
          only affect operations performed after the change.
        </adm:synopsis>
      </adm:none>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-confidentiality-enabled</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opendj-server-legacy/resource/schema/02-config.ldif
@@ -4560,7 +4560,10 @@
        ds-cfg-weight $
        ds-cfg-monitoring-period $
        ds-cfg-compute-change-number $
        ds-cfg-source-address )
        ds-cfg-source-address $
        ds-cfg-cipher-transformation $
        ds-cfg-cipher-key-length $
        ds-cfg-confidentiality-enabled)
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.65
  NAME 'ds-backup-directory'
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/AttributeIndex.java
@@ -108,11 +108,10 @@
    private MatchingRuleIndex(EntryContainer entryContainer, AttributeType attributeType, State state, Indexer indexer,
        int indexEntryLimit, boolean encryptValues, CryptoSuite cryptoSuite)
    {
      super(getIndexName(entryContainer, attributeType, indexer.getIndexID()), state, indexEntryLimit, entryContainer);
      super(getIndexName(entryContainer, attributeType, indexer.getIndexID()), state, indexEntryLimit, entryContainer,
          cryptoSuite);
      this.attributeType = attributeType;
      this.indexer = indexer;
      this.encryptValues = encryptValues;
      this.cryptoSuite = cryptoSuite;
    }
    Set<ByteString> indexEntry(Entry entry)
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/DefaultIndex.java
@@ -53,7 +53,6 @@
  private int indexEntryLimit;
  private EntryIDSetCodec codec;
  protected boolean encryptValues;
  protected CryptoSuite cryptoSuite;
  /**
@@ -79,16 +78,18 @@
   *          The configured limit on the number of entry IDs that may be indexed by one key.
   * @param entryContainer
   *          The entryContainer holding this index.
   * @param cryptoSuite
   * @throws StorageRuntimeException
   *           If an error occurs in the storage.
   */
  DefaultIndex(TreeName name, State state, int indexEntryLimit, EntryContainer entryContainer)
  DefaultIndex(TreeName name, State state, int indexEntryLimit, EntryContainer entryContainer, CryptoSuite cryptoSuite)
      throws StorageRuntimeException
  {
    super(name);
    this.indexEntryLimit = indexEntryLimit;
    this.state = state;
    this.entryContainer = entryContainer;
    this.cryptoSuite = cryptoSuite;
  }
  @Override
@@ -96,7 +97,7 @@
  {
    final EnumSet<IndexFlag> flags = state.getIndexFlags(txn, getName());
    codec = flags.contains(COMPACTED) ? CODEC_V2 : CODEC_V1;
    if (encryptValues)
    if (cryptoSuite.isEncrypted())
    {
      codec = new EntryIDSet.EntryIDSetCodecV3(codec, cryptoSuite);
    }
@@ -154,12 +155,14 @@
  // Keeps temporary values during import encrypted even in on-disk buffers.
  long importDecodeValue(ByteString value)
  {
    return encryptValues ? decodeValue(ByteString.empty(), value).iterator().next().longValue() : value.toLong();
    return cryptoSuite.isEncrypted()
        ? decodeValue(ByteString.empty(), value).iterator().next().longValue()
        : value.toLong();
  }
  ByteString importToValue(EntryID entryID)
  {
    return encryptValues ? toValue(newDefinedSet(entryID.longValue())) : entryID.toByteString();
    return cryptoSuite.isEncrypted() ? toValue(newDefinedSet(entryID.longValue())) : entryID.toByteString();
  }
  @Override
@@ -293,9 +296,7 @@
  @Override
  public boolean setConfidential(boolean indexConfidential)
  {
    final boolean rebuildRequired = !this.encryptValues && indexConfidential;
    this.encryptValues = indexConfidential;
    return rebuildRequired;
    return cryptoSuite.isEncrypted() != indexConfidential;
  }
  @Override
opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/EntryContainer.java
@@ -154,6 +154,8 @@
  /** The set of attribute indexes. */
  private final Map<AttributeType, AttributeIndex> attrIndexMap = new HashMap<>();
  private final Map<AttributeType, CryptoSuite> attrCryptoMap = new HashMap<>();
  /** The set of VLV (Virtual List View) indexes. */
  private final Map<String, VLVIndex> vlvIndexMap = new HashMap<>();
@@ -165,8 +167,6 @@
  private final ServerContext serverContext;
  private CryptoSuite cryptoSuite;
  /**
   * This class is responsible for managing the configuration for attribute
   * indexes used within this entry container.
@@ -180,7 +180,7 @@
    {
      try
      {
        newAttributeIndex(cfg);
        newAttributeIndex(cfg, null);
        return true;
      }
      catch(Exception e)
@@ -196,7 +196,8 @@
      final ConfigChangeResult ccr = new ConfigChangeResult();
      try
      {
        final AttributeIndex index = newAttributeIndex(cfg);
        final CryptoSuite cryptoSuite = newCryptoSuite(cfg.isConfidentialityEnabled());
        final AttributeIndex index = newAttributeIndex(cfg, cryptoSuite);
        storage.write(new WriteOperation()
        {
          @Override
@@ -209,6 +210,7 @@
              ccr.addMessage(NOTE_INDEX_ADD_REQUIRES_REBUILD.get(cfg.getAttribute().getNameOrOID()));
            }
            attrIndexMap.put(cfg.getAttribute(), index);
            attrCryptoMap.put(cfg.getAttribute(), cryptoSuite);
          }
        });
      }
@@ -242,6 +244,7 @@
          public void run(WriteableTransaction txn) throws Exception
          {
            attrIndexMap.remove(cfg.getAttribute()).closeAndDelete(txn);
            attrCryptoMap.remove(cfg.getAttribute());
          }
        });
      }
@@ -370,7 +373,13 @@
    config.addBackendVLVIndexDeleteListener(vlvIndexCfgManager);
  }
  private AttributeIndex newAttributeIndex(BackendIndexCfg cfg) throws ConfigException
  private CryptoSuite newCryptoSuite(boolean confidentiality)
  {
    return serverContext.getCryptoManager().newCryptoSuite(config.getCipherTransformation(),
        config.getCipherKeyLength(), confidentiality);
  }
  private AttributeIndex newAttributeIndex(BackendIndexCfg cfg, CryptoSuite cryptoSuite) throws ConfigException
  {
    return new AttributeIndex(cfg, state, this, cryptoSuite);
  }
@@ -381,7 +390,8 @@
        .compress(config.isEntriesCompressed())
        .encode(config.isCompactEncoding())
        .encrypt(config.isConfidentialityEnabled())
        .cryptoSuite(cryptoSuite)
        .cryptoSuite(serverContext.getCryptoManager().newCryptoSuite(config.getCipherTransformation(),
            config.getCipherKeyLength(),config.isConfidentialityEnabled()))
        .schema(rootContainer.getCompressedSchema())
        .build();
  }
@@ -404,8 +414,6 @@
    boolean shouldCreate = accessMode.isWriteable();
    try
    {
      cryptoSuite = serverContext.getCryptoManager().newCryptoSuite(config.getCipherTransformation(),
          config.getCipherKeyLength());
      id2entry = new ID2Entry(getIndexName(ID2ENTRY_TREE_NAME), newDataConfig(config));
      id2entry.open(txn, shouldCreate);
      id2childrenCount.open(txn, shouldCreate);
@@ -417,13 +425,15 @@
      {
        BackendIndexCfg indexCfg = config.getBackendIndex(idx);
        final AttributeIndex index = newAttributeIndex(indexCfg);
        CryptoSuite cryptoSuite = newCryptoSuite(indexCfg.isConfidentialityEnabled());
        final AttributeIndex index = newAttributeIndex(indexCfg, cryptoSuite);
        index.open(txn, shouldCreate);
        if(!index.isTrusted())
        {
          logger.info(NOTE_INDEX_ADD_REQUIRES_REBUILD, index.getName());
        }
        attrIndexMap.put(indexCfg.getAttribute(), index);
        attrCryptoMap.put(indexCfg.getAttribute(), cryptoSuite);
      }
      for (String idx : config.listBackendVLVIndexes())
@@ -2386,13 +2396,14 @@
        @Override
        public void run(WriteableTransaction txn) throws Exception
        {
          cryptoSuite.setCipherTransformation(cfg.getCipherTransformation());
          cryptoSuite.setCipherKeyLength(cfg.getCipherKeyLength());
          id2entry.setDataConfig(newDataConfig(cfg));
          EntryContainer.this.config = cfg;
        }
      });
      for (CryptoSuite indexCrypto : attrCryptoMap.values())
      {
        indexCrypto.newParameters(cfg.getCipherTransformation(), cfg.getCipherKeyLength(), indexCrypto.isEncrypted());
      }
    }
    catch (Exception e)
    {
opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java
@@ -2752,8 +2752,8 @@
  }
  @Override
  public CryptoSuite newCryptoSuite(String cipherTransformation, int cipherKeyLength)
  public CryptoSuite newCryptoSuite(String cipherTransformation, int cipherKeyLength, boolean encrypt)
  {
    return new CryptoSuite(this, cipherTransformation, cipherKeyLength);
    return new CryptoSuite(this, cipherTransformation, cipherKeyLength, encrypt);
  }
}
opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoSuite.java
@@ -15,6 +15,7 @@
 */
package org.opends.server.crypto;
import net.jcip.annotations.Immutable;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DecodeException;
@@ -33,8 +34,23 @@
/** Defines cipher transformation and hash algorithm for cryptographic related operations. */
public class CryptoSuite
{
  private String cipherTransformation;
  private int cipherKeyLength;
  /** Cipher specific settings that can change at runtime. */
  @Immutable
  private static final class CipherInfo
  {
    private final String cipherTransformation;
    private final int cipherKeyLength;
    private final boolean encrypt;
    CipherInfo(String cipherTransformation, int cipherKeyLength, boolean encrypt)
    {
      this.cipherTransformation = cipherTransformation;
      this.cipherKeyLength = cipherKeyLength;
      this.encrypt = encrypt;
    }
  }
  private volatile CipherInfo cipherInfo;
  private final CryptoManager cryptoManager;
  /**
@@ -42,52 +58,24 @@
   * @param cryptoManager the CryptoManager to use for cryptographic operations
   * @param cipherTransformation the initial cipher transformation
   * @param cipherKeyLength the initial key length for the cipher
   * @param encrypt if the user of the crypto suite needs encryption
   */
  public CryptoSuite(CryptoManager cryptoManager, String cipherTransformation, int cipherKeyLength)
  public CryptoSuite(CryptoManager cryptoManager, String cipherTransformation, int cipherKeyLength, boolean encrypt)
  {
    this.cryptoManager = cryptoManager;
    this.cipherTransformation = cipherTransformation;
    this.cipherKeyLength = cipherKeyLength;
    this.cipherInfo = new CipherInfo(cipherTransformation, cipherKeyLength, encrypt);
  }
  /**
   * Returns the cipher transformation to use.
   *
   * @return the cipher transformation to use
   */
  public String getCipherTransformation()
  {
    return cipherTransformation;
  }
  /**
   * Returns the cipher key length to use.
   *
   * @return the cipher key length to use
   */
  public int getCipherKeyLength()
  {
    return cipherKeyLength;
  }
  /**
   * Sets the cipher transformation for the CryptoSuite.
   * Set new cipher and enable parameters for the crypto suite.
   *
   * @param cipherTransformation the new cipher transformation
   */
  public void setCipherTransformation(String cipherTransformation)
  {
    this.cipherTransformation = cipherTransformation;
  }
  /**
   * Sets the key length for the CryptoSuite.
   *
   * @param cipherKeyLength the new key length
   * @param enabled true if the user of the crypto suite needs encryption
   */
  public void setCipherKeyLength(int cipherKeyLength)
  public void newParameters(String cipherTransformation, int cipherKeyLength, boolean enabled)
  {
    this.cipherKeyLength = cipherKeyLength;
    cipherInfo = new CipherInfo(cipherTransformation, cipherKeyLength, enabled);
  }
  /**
@@ -113,7 +101,8 @@
   */
  public byte[] encrypt(byte[] data) throws GeneralSecurityException, CryptoManagerException
  {
    return cryptoManager.encrypt(cipherTransformation, cipherKeyLength, data);
    CipherInfo currentCipher = cipherInfo;
    return cryptoManager.encrypt(currentCipher.cipherTransformation, currentCipher.cipherKeyLength, data);
  }
  /**
@@ -127,7 +116,8 @@
   */
  public CipherOutputStream getCipherOutputStream(OutputStream os) throws CryptoManagerException
  {
    return cryptoManager.getCipherOutputStream(cipherTransformation, cipherKeyLength, os);
    CipherInfo currentCipher = cipherInfo;
    return cryptoManager.getCipherOutputStream(currentCipher.cipherTransformation, currentCipher.cipherKeyLength, os);
  }
  /**
@@ -162,14 +152,27 @@
    }
  }
  /**
   * Returns whether the user of the crypto suite needs encryption.
   *
   * @return true if the user of the crypto suite needs encryption
   */
  public boolean isEncrypted()
  {
    return cipherInfo.encrypt;
  }
  @Override
  public String toString()
  {
    StringBuilder builder = new StringBuilder();
    CipherInfo currentCipher = cipherInfo;
    builder.append("CryptoSuite(cipherTransformation=");
    builder.append(cipherTransformation);
    builder.append(currentCipher.cipherTransformation);
    builder.append(", keyLength=");
    builder.append(cipherKeyLength);
    builder.append(currentCipher.cipherKeyLength);
    builder.append(", encrypt=");
    builder.append(currentCipher.encrypt);
    builder.append(")");
    return builder.toString();
  }
opendj-server-legacy/src/main/java/org/opends/server/replication/protocol/ReplicationMsg.java
@@ -12,7 +12,7 @@
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2006-2010 Sun Microsystems, Inc.
 * Portions copyright 2013-2014 ForgeRock AS.
 * Portions copyright 2013-2016 ForgeRock AS.
 */
package org.opends.server.replication.protocol;
@@ -27,6 +27,9 @@
 */
public abstract class ReplicationMsg
{
  /** Reserved type for uses other than protocol messages. */
  public static final byte MSG_TYPE_DISK_ENCODING = -1;
  // PDU type values kept for compatibility with replication protocol version 1
  static final byte MSG_TYPE_MODIFY_V1 = 1;
  static final byte MSG_TYPE_ADD_V1 = 2;
opendj-server-legacy/src/main/java/org/opends/server/replication/server/ReplicationServer.java
@@ -53,6 +53,7 @@
import org.opends.server.api.VirtualAttributeProvider;
import org.opends.server.backends.ChangelogBackend;
import org.opends.server.core.DirectoryServer;
import org.opends.server.crypto.CryptoSuite;
import org.opends.server.replication.common.CSN;
import org.opends.server.replication.common.MultiDomainServerState;
import org.opends.server.replication.common.ServerState;
@@ -129,6 +130,8 @@
   */
  private static final List<ReplicationServer> allInstances = new ArrayList<>();
  private final CryptoSuite cryptoSuite;
  /**
   * Creates a new Replication server using the provided configuration entry.
   *
@@ -170,7 +173,10 @@
    this.domainPredicate = predicate;
    enableExternalChangeLog();
    this.changelogDB = new FileChangelogDB(this, config.getReplicationDBDirectory());
    cryptoSuite = DirectoryServer.getInstance().getServerContext().getCryptoManager().
        newCryptoSuite(cfg.getCipherTransformation(), cfg.getCipherKeyLength(), cfg.isConfidentialityEnabled());
    this.changelogDB = new FileChangelogDB(this, config.getReplicationDBDirectory(), cryptoSuite);
    replSessionSecurity = new ReplSessionSecurity();
    initialize();
@@ -871,6 +877,9 @@
      }
    }
    cryptoSuite.newParameters(config.getCipherTransformation(), config.getCipherKeyLength(),
        config.isConfidentialityEnabled());
    // changing the listen port requires to stop the listen thread
    // and restart it.
    if (getReplicationPort() != oldConfig.getReplicationPort())
@@ -1337,6 +1346,15 @@
    return MultimasterReplication.isECLEnabled();
  }
  /**
   * Return whether change-log records should be encrypted.
   * @return trus if change-log records should be encrypted
   */
  public boolean isEncrypted()
  {
    return config.isConfidentialityEnabled();
  }
  @Override
  public String toString()
  {
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/BlockLogWriter.java
@@ -11,7 +11,7 @@
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2014-2015 ForgeRock AS.
 * Copyright 2014-2016 ForgeRock AS.
 */
package org.opends.server.replication.server.changelog.file;
@@ -123,7 +123,7 @@
      write(parser.encodeRecord(record));
      writer.flush();
    }
    catch (IOException e)
    catch (Exception e)
    {
      throw new ChangelogException(ERR_CHANGELOG_UNABLE_TO_ADD_RECORD.get(record.toString(),
          writer.getFile().getPath()), e);
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/FileChangeNumberIndexDB.java
@@ -17,6 +17,7 @@
import static org.opends.messages.ReplicationMessages.*;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -388,7 +389,7 @@
    private static final byte STRING_SEPARATOR = 0;
    @Override
    public ByteString encodeRecord(final Record<Long, ChangeNumberIndexRecord> record)
    public ByteString encodeRecord(final Record<Long, ChangeNumberIndexRecord> record) throws IOException
    {
      final ChangeNumberIndexRecord cnIndexRecord = record.getValue();
      return new ByteStringBuilder()
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/FileChangelogDB.java
@@ -42,6 +42,7 @@
import org.forgerock.util.time.TimeService;
import org.opends.server.api.DirectoryThread;
import org.opends.server.backends.ChangelogBackend;
import org.opends.server.crypto.CryptoSuite;
import org.opends.server.replication.common.CSN;
import org.opends.server.replication.common.MultiDomainServerState;
import org.opends.server.replication.common.ServerState;
@@ -116,6 +117,7 @@
  private static final DBCursor<UpdateMsg> EMPTY_CURSOR_REPLICA_DB =
      new FileReplicaDBCursor(EMPTY_CURSOR, null, AFTER_MATCHING_KEY);
  private final CryptoSuite cryptoSuite;
  /**
   * Creates a new changelog DB.
   *
@@ -123,14 +125,17 @@
   *          the local replication server.
   * @param dbDirectoryPath
   *          the path where the changelog files reside.
   * @param cryptoSuite
   *          the cryptosuite to use for encryption
   * @throws ConfigException
   *           if a problem occurs opening the supplied directory
   */
  public FileChangelogDB(final ReplicationServer replicationServer, String dbDirectoryPath)
  public FileChangelogDB(final ReplicationServer replicationServer, String dbDirectoryPath, CryptoSuite cryptoSuite)
      throws ConfigException
  {
    this.replicationServer = replicationServer;
    this.dbDirectory = makeDir(dbDirectoryPath);
    this.cryptoSuite = cryptoSuite;
  }
  private File makeDir(final String dbDirName) throws ConfigException
@@ -266,7 +271,7 @@
        return null;
      }
      final FileReplicaDB newDB = new FileReplicaDB(serverId, baseDN, server, replicationEnv);
      final FileReplicaDB newDB = new FileReplicaDB(serverId, baseDN, server, cryptoSuite, replicationEnv);
      domainMap.put(serverId, newDB);
      return Pair.of(newDB, true);
    }
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/FileReplicaDB.java
@@ -16,7 +16,10 @@
package org.opends.server.replication.server.changelog.file;
import static org.opends.messages.ReplicationMessages.*;
import static org.opends.server.replication.protocol.ProtocolVersion.REPLICATION_PROTOCOL_V7;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Date;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -24,12 +27,13 @@
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.opends.server.api.MonitorData;
import org.forgerock.opendj.server.config.server.MonitorProviderCfg;
import org.opends.server.api.MonitorProvider;
import org.opends.server.core.DirectoryServer;
import org.opends.server.crypto.CryptoSuite;
import org.opends.server.replication.common.CSN;
import org.opends.server.replication.protocol.ProtocolVersion;
import org.opends.server.replication.protocol.UpdateMsg;
import org.opends.server.replication.server.ReplicationServer;
import org.opends.server.replication.server.ReplicationServerDomain;
@@ -39,6 +43,7 @@
import org.opends.server.replication.server.changelog.api.DBCursor.PositionStrategy;
import org.opends.server.replication.server.changelog.file.Log.RepositionableCursor;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.types.CryptoManagerException;
import org.opends.server.types.InitializationException;
/**
@@ -54,10 +59,6 @@
 */
class FileReplicaDB
{
  /** The parser of records stored in Replica DB. */
  static final RecordParser<CSN, UpdateMsg> RECORD_PARSER = new ReplicaDBParser();
  /** Class that allows atomically setting oldest and newest CSNs without synchronization. */
  @Immutable
  private static final class CSNLimits
@@ -105,13 +106,13 @@
   *           If a database problem happened
   */
  FileReplicaDB(final int serverId, final DN baseDN, final ReplicationServer replicationServer,
      final ReplicationEnvironment replicationEnv) throws ChangelogException
      final CryptoSuite cryptoSuite, final ReplicationEnvironment replicationEnv) throws ChangelogException
  {
    this.serverId = serverId;
    this.baseDN = baseDN;
    this.replicationServer = replicationServer;
    this.replicationEnv = replicationEnv;
    this.log = createLog(replicationEnv);
    this.log = createLog(replicationEnv, cryptoSuite);
    this.csnLimits = new CSNLimits(readOldestCSN(), readNewestCSN());
    DirectoryServer.deregisterMonitorProvider(dbMonitor);
@@ -130,10 +131,11 @@
    return record == null ? null : record.getKey();
  }
  private Log<CSN, UpdateMsg> createLog(final ReplicationEnvironment replicationEnv) throws ChangelogException
  private Log<CSN, UpdateMsg> createLog(final ReplicationEnvironment replicationEnv, final CryptoSuite cryptoSuite)
      throws ChangelogException
  {
    final ReplicationServerDomain domain = replicationServer.getReplicationServerDomain(baseDN, true);
    return replicationEnv.getOrCreateReplicaDB(baseDN, serverId, domain.getGenerationId());
    return replicationEnv.getOrCreateReplicaDB(baseDN, serverId, domain.getGenerationId(), cryptoSuite);
  }
  /**
@@ -336,14 +338,49 @@
    log.dumpAsTextFile(log.getPath());
  }
  static ReplicaDBParser newReplicaDBParser(final CryptoSuite cryptoSuite)
  {
    return new ReplicaDBParser(cryptoSuite);
  }
  /** Parser of records persisted in the ReplicaDB log. */
  private static class ReplicaDBParser implements RecordParser<CSN, UpdateMsg>
  {
    private static final byte RECORD_VERSION = 0x01;
    private final CryptoSuite cryptoSuite;
    /** Adjusts the ByteStringBuilder capacity to avoid capacity increases (and copies) when encoding records. */
    private int encryptionOverhead;
    ReplicaDBParser(CryptoSuite cryptoSuite)
    {
      this.cryptoSuite = cryptoSuite;
    }
    @Override
    public ByteString encodeRecord(final Record<CSN, UpdateMsg> record)
    public ByteString encodeRecord(final Record<CSN, UpdateMsg> record) throws IOException
    {
      final UpdateMsg message = record.getValue();
      if (cryptoSuite.isEncrypted())
      {
        try
        {
          byte[] messageBytes = message.getBytes();
          ByteStringBuilder builder = new ByteStringBuilder(messageBytes.length + encryptionOverhead);
          builder.appendByte(UpdateMsg.MSG_TYPE_DISK_ENCODING);
          builder.appendByte(RECORD_VERSION);
          builder.appendBytes(cryptoSuite.encrypt(messageBytes));
          final int overhead = builder.length() - messageBytes.length;
          if (encryptionOverhead < overhead)
          {
            encryptionOverhead = overhead;
          }
          return builder.toByteString();
        }
        catch (GeneralSecurityException | CryptoManagerException e)
        {
          throw new IOException(e);
        }
      }
      return ByteString.wrap(message.getBytes());
    }
@@ -352,8 +389,21 @@
    {
      try
      {
        final UpdateMsg msg =
            (UpdateMsg) UpdateMsg.generateMsg(data.toByteArray(), ProtocolVersion.REPLICATION_PROTOCOL_V7);
        byte[] recordBytes;
        if (data.byteAt(0) == UpdateMsg.MSG_TYPE_DISK_ENCODING)
        {
          final int version = data.byteAt(1);
          if (version != RECORD_VERSION)
          {
            throw new DecodingException(ERR_UNRECOGNIZED_RECORD_VERSION.get(version));
          }
          recordBytes = cryptoSuite.decrypt(data.subSequence(2, data.length()).toByteArray());
        }
        else
        {
          recordBytes = data.toByteArray();
        }
        final UpdateMsg msg = (UpdateMsg) UpdateMsg.generateMsg(recordBytes, REPLICATION_PROTOCOL_V7);
        return Record.from(msg.getCSN(), msg);
      }
      catch (Exception e)
@@ -362,26 +412,22 @@
      }
    }
    /** {@inheritDoc} */
    @Override
    public CSN decodeKeyFromString(String key) throws ChangelogException
    {
      return new CSN(key);
    }
    /** {@inheritDoc} */
    @Override
    public String encodeKeyToString(CSN key)
    {
      return key.toString();
    }
    /** {@inheritDoc} */
    @Override
    public CSN getMaxKey()
    {
      return CSN.MAX_CSN_VALUE;
    }
  }
}
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/RecordParser.java
@@ -11,13 +11,15 @@
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2014 ForgeRock AS.
 * Copyright 2014-2016 ForgeRock AS.
 */
package org.opends.server.replication.server.changelog.file;
import org.forgerock.opendj.ldap.ByteString;
import org.opends.server.replication.server.changelog.api.ChangelogException;
import java.io.IOException;
/**
 * Parser of a log record.
 * <p>
@@ -55,7 +57,7 @@
   *          The record to encode.
   * @return the bytes array representing the (key,value) record
   */
  ByteString encodeRecord(Record<K, V> record);
  ByteString encodeRecord(Record<K, V> record) throws IOException;
  /**
   * Read the key from the provided string.
opendj-server-legacy/src/main/java/org/opends/server/replication/server/changelog/file/ReplicationEnvironment.java
@@ -48,6 +48,7 @@
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.util.time.TimeService;
import org.opends.server.crypto.CryptoSuite;
import org.opends.server.replication.common.CSN;
import org.opends.server.replication.protocol.UpdateMsg;
import org.opends.server.replication.server.ChangelogState;
@@ -338,8 +339,8 @@
   * @throws ChangelogException
   *           if an error occurs.
   */
  Log<CSN, UpdateMsg> getOrCreateReplicaDB(final DN domainDN, final int serverId, final long generationId)
      throws ChangelogException
  Log<CSN, UpdateMsg> getOrCreateReplicaDB(final DN domainDN, final int serverId, final long generationId,
      final CryptoSuite cryptoSuite) throws ChangelogException
  {
    if (logger.isTraceEnabled())
    {
@@ -367,7 +368,7 @@
        ensureGenerationIdFileExists(generationIdPath);
        changelogState.setDomainGenerationId(domainDN, generationId);
        return openLog(serverIdPath, FileReplicaDB.RECORD_PARSER,
        return openLog(serverIdPath, FileReplicaDB.newReplicaDBParser(cryptoSuite),
            new LogRotationParameters(REPLICA_DB_MAX_LOG_FILE_SIZE_IN_BYTES, 0, 0), logsReplicaDB);
      }
    }
opendj-server-legacy/src/main/java/org/opends/server/types/CryptoManager.java
@@ -430,6 +430,7 @@
   * @return a new {@link CryptoSuite} for the cipher and key
   * @param cipherTransformation cipher transformation string specification
   * @param cipherKeyLength length of key in bits
   * @param encrypt true if the user of the crypto suite needs encryption
   */
  CryptoSuite newCryptoSuite(String cipherTransformation, int cipherKeyLength);
  CryptoSuite newCryptoSuite(String cipherTransformation, int cipherKeyLength, boolean encrypt);
}
opendj-server-legacy/src/messages/org/opends/messages/replication.properties
@@ -589,5 +589,6 @@
ERR_CHANGELOG_RESET_CHANGE_NUMBER_CSN_TOO_OLD_294=The change number could not be reset to %d because the associated \
  change with CSN '%s' has already been purged from the change log. Try resetting to a more recent change
ERR_REPLICATION_CHANGE_NUMBER_DISABLED_295=Change number indexing is disabled for replication domain '%s'
INFO_CHANGELOG_FILTER_OUT_RECORD_BREAKING_ORDER_296=Filtering out from log file '%s' the record '%s'\
 because it would break ordering. Last key appended is '%s'.
INFO_CHANGELOG_FILTER_OUT_RECORD_BREAKING_ORDER_296=Filtering out from log file '%s' the record '%s' \
 because it would break ordering. Last key appended is '%s'.
ERR_UNRECOGNIZED_RECORD_VERSION_297=Cannot decode change-log record with version %x
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/DefaultIndexTest.java
@@ -11,7 +11,7 @@
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2015 ForgeRock AS.
 * Copyright 2015-2016 ForgeRock AS.
 */
package org.opends.server.backends.pluggable;
@@ -41,6 +41,7 @@
import org.opends.server.backends.pluggable.spi.TreeName;
import org.opends.server.backends.pluggable.spi.UpdateFunction;
import org.opends.server.backends.pluggable.spi.WriteableTransaction;
import org.opends.server.crypto.CryptoSuite;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -118,7 +119,10 @@
  {
    final State state = mock(State.class);
    when(state.getIndexFlags(any(ReadableTransaction.class), any(TreeName.class))).thenReturn(indexFlags);
    return new DefaultIndex(new TreeName("dc=example,dc=com", name), state, indexLimit, mock(EntryContainer.class));
    final CryptoSuite cryptoSuite = mock(CryptoSuite.class);
    when(cryptoSuite.isEncrypted()).thenReturn(false);
    return new DefaultIndex(new TreeName("dc=example,dc=com", name), state, indexLimit, mock(EntryContainer.class),
        cryptoSuite);
  }
  static final class DummyWriteableTransaction implements WriteableTransaction {
opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/OnDiskMergeImporterTest.java
@@ -61,6 +61,7 @@
import org.opends.server.backends.pluggable.spi.StorageRuntimeException;
import org.opends.server.backends.pluggable.spi.TreeName;
import org.opends.server.backends.pluggable.spi.WriteableTransaction;
import org.opends.server.crypto.CryptoSuite;
import org.testng.annotations.Test;
import com.forgerock.opendj.util.PackedLong;
@@ -483,6 +484,7 @@
  {
    private static final State state;
    private static final EntryContainer entryContainer;
    private static final CryptoSuite cryptoSuite;
    static
    {
@@ -493,11 +495,14 @@
      state = Mockito.mock(State.class);
      Mockito.when(state.getIndexFlags(Mockito.any(ReadableTransaction.class), Mockito.any(TreeName.class))).thenReturn(
          EnumSet.of(State.IndexFlag.COMPACTED));
      cryptoSuite = Mockito.mock(CryptoSuite.class);
      Mockito.when(cryptoSuite.isEncrypted()).thenReturn(false);
    };
    DummyIndex(int indexEntryLimit) throws StorageRuntimeException
    {
      super(TreeName.valueOf("/dumy/dummy"), state, indexEntryLimit, entryContainer);
      super(TreeName.valueOf("/dummy/dummy"), state, indexEntryLimit, entryContainer, cryptoSuite);
      open(Mockito.mock(WriteableTransaction.class), false);
    }
  }
opendj-server-legacy/src/test/java/org/opends/server/replication/server/ReplServerFakeConfiguration.java
@@ -40,6 +40,7 @@
  private int queueSize;
  private int windowSize;
  private SortedSet<String> servers;
  private boolean confidentialityEnabled;
  /*
   * Assured mode properties
@@ -203,12 +204,30 @@
  }
  @Override
  public boolean isConfidentialityEnabled()
  {
    return confidentialityEnabled;
  }
  @Override
  public long getAssuredTimeout()
  {
    return assuredTimeout;
  }
  @Override
  public int getCipherKeyLength()
  {
    return 128;
  }
  @Override
  public String getCipherTransformation()
  {
    return "AES/CBC/PKCS5Padding";
  }
  @Override
  public int getDegradedStatusThreshold()
  {
    return degradedStatusThreshold;
@@ -246,4 +265,9 @@
  {
    this.computeChangenumber = computeChangenumber;
  }
  public void setConfidentialityEnabled(boolean confidentialityEnabled)
  {
    this.confidentialityEnabled = confidentialityEnabled;
  }
}
opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/BlockLogReaderWriterTest.java
@@ -11,7 +11,7 @@
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2014-2015 ForgeRock AS.
 * Copyright 2014-2016 ForgeRock AS.
 */
package org.opends.server.replication.server.changelog.file;
@@ -22,6 +22,7 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collections;
@@ -494,7 +495,7 @@
    }
    @Override
    public ByteString encodeRecord(Record<Integer, Integer> record)
    public ByteString encodeRecord(Record<Integer, Integer> record) throws IOException
    {
      return new ByteStringBuilder().appendInt(record.getKey()).appendInt(record.getValue()).toByteString();
    }
opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/FileReplicaDBTest.java
@@ -25,6 +25,8 @@
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.util.time.TimeService;
import org.opends.server.TestCaseUtils;
import org.opends.server.core.DirectoryServer;
import org.opends.server.crypto.CryptoSuite;
import org.opends.server.replication.ReplicationTestCase;
import org.opends.server.replication.common.CSN;
import org.opends.server.replication.common.CSNGenerator;
@@ -54,6 +56,8 @@
public class FileReplicaDBTest extends ReplicationTestCase
{
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  private final String cipherTransformation = "AES/CBC/PKCS5Padding";
  private final int keyLength = 128;
  private DN TEST_ROOT_DN;
  /**
@@ -76,16 +80,17 @@
  {
    CSN[] csns = generateCSNs(1, 0, 2);
    return new Object[][] {
      { new DeleteMsg(TEST_ROOT_DN, csns[0], "uid") },
      { new DeleteMsg(TEST_ROOT_DN, csns[1], "uid") },
      { new DeleteMsg(TEST_ROOT_DN, csns[0], "uid"), false },
      { new DeleteMsg(TEST_ROOT_DN, csns[1], "uid"), false },
      { new DeleteMsg(TEST_ROOT_DN, csns[0], "uid"), true },
      { new DeleteMsg(TEST_ROOT_DN, csns[1], "uid"), true },
    };
  }
  @Test(dataProvider="messages")
  public void testRecordParser(UpdateMsg msg) throws Exception
  public void testRecordParser(UpdateMsg msg, boolean confidential) throws Exception
  {
    RecordParser<CSN, UpdateMsg> parser = FileReplicaDB.RECORD_PARSER;
    RecordParser<CSN, UpdateMsg> parser = FileReplicaDB.newReplicaDBParser(createCryptoSuite(confidential));
    ByteString data = parser.encodeRecord(Record.from(msg.getCSN(), msg));
    Record<CSN, UpdateMsg> record = parser.decodeRecord(data);
@@ -94,6 +99,23 @@
    assertThat(record.getValue()).isEqualTo(msg);
  }
  @Test(dataProvider="messages")
  public void testRecordEncodingWithAndWithoutConfidentiality(UpdateMsg msg, boolean confidential) throws Exception
  {
    CryptoSuite cryptoSuite = createCryptoSuite(confidential);
    RecordParser<CSN, UpdateMsg> parser = FileReplicaDB.newReplicaDBParser(cryptoSuite);
    ByteString data1 = parser.encodeRecord(Record.from(msg.getCSN(), msg));
    cryptoSuite.newParameters(cipherTransformation, keyLength, !confidential);
    ByteString data2 = parser.encodeRecord(Record.from(msg.getCSN(), msg));
    assertFalse(data1.equals(data2));
  }
  private CryptoSuite createCryptoSuite(boolean confidential)
  {
    return DirectoryServer.getCryptoManager().newCryptoSuite(cipherTransformation, keyLength, confidential);
  }
  @Test
  public void testDomainDNWithForwardSlashes() throws Exception
  {
@@ -407,7 +429,7 @@
      testRoot = createCleanDir();
      dbEnv = new ReplicationEnvironment(testRoot.getPath(), replicationServer, TimeService.SYSTEM);
      replicaDB = new FileReplicaDB(1, TEST_ROOT_DN, replicationServer, dbEnv);
      replicaDB = new FileReplicaDB(1, TEST_ROOT_DN, replicationServer, createCryptoSuite(false), dbEnv);
      // Populate the db with 'max' msg
      int mySeqnum = 1;
@@ -428,7 +450,7 @@
      debugInfo(tn, "SHUTDOWN replicaDB and recreate");
      replicaDB.shutdown();
      replicaDB = new FileReplicaDB(1, TEST_ROOT_DN, replicationServer, dbEnv);
      replicaDB = new FileReplicaDB(1, TEST_ROOT_DN, replicationServer, createCryptoSuite(false), dbEnv);
      assertLimits(replicaDB, csns[1], csns[max]);
      // Populate the db with 'max' msg
@@ -521,8 +543,9 @@
      throws IOException, ConfigException
  {
    final int changelogPort = findFreePort();
    return new ReplicationServer(
        new ReplServerFakeConfiguration(changelogPort, null, 0, 2, queueSize, windowSize, null));
    ReplServerFakeConfiguration replServerFakeCfg =
        new ReplServerFakeConfiguration(changelogPort, null, 0, 2, queueSize, windowSize, null);
    return new ReplicationServer(replServerFakeCfg);
  }
  private FileReplicaDB newReplicaDB(ReplicationServer rs) throws Exception
opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/LogFileTest.java
@@ -16,6 +16,7 @@
package org.opends.server.replication.server.changelog.file;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import org.forgerock.i18n.LocalizableMessage;
@@ -327,7 +328,7 @@
    }
    @Override
    public ByteString encodeRecord(Record<String, String> record)
    public ByteString encodeRecord(Record<String, String> record) throws IOException
    {
      return new ByteStringBuilder()
        .appendUtf8(record.getKey()).appendByte(STRING_SEPARATOR)
opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/ReplicationEnvironmentTest.java
@@ -24,6 +24,7 @@
import org.forgerock.util.time.TimeService;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.crypto.CryptoSuite;
import org.opends.server.replication.common.CSN;
import org.opends.server.replication.common.CSNGenerator;
import org.opends.server.replication.protocol.UpdateMsg;
@@ -52,14 +53,16 @@
  private static final String DN1_AS_STRING = "cn=test1,dc=company.com";
  private static final String DN2_AS_STRING = "cn=te::st2,dc=company.com";
  private static final String DN3_AS_STRING = "cn=test3,dc=company.com";
  private static final String TEST_DIRECTORY_CHANGELOG = "test-output/changelog";
  private static final CryptoSuite cryptoSuite = mock(CryptoSuite.class);
  @BeforeClass
  public void setUp() throws Exception
  {
    // This test suite depends on having the schema available for DN decoding.
    TestCaseUtils.startFakeServer();
    when(cryptoSuite.isEncrypted()).thenReturn(false);
  }
  @AfterClass
@@ -89,8 +92,8 @@
      final DN domainDN = DN.valueOf(DN1_AS_STRING);
      ReplicationEnvironment environment = createReplicationEnv(rootPath);
      cnDB = environment.getOrCreateCNIndexDB();
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1);
      replicaDB2 = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_2, 1);
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite);
      replicaDB2 = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_2, 1, cryptoSuite);
      final ChangelogState state = environment.readOnDiskChangelogState();
@@ -123,7 +126,7 @@
        for (int j = 1; j <= 10; j++)
        {
          // 3 domains, 10 server id each, generation id is different for each domain
          replicaDBs.add(environment.getOrCreateReplicaDB(domainDNs.get(i), j, i+1));
          replicaDBs.add(environment.getOrCreateReplicaDB(domainDNs.get(i), j, i+1, cryptoSuite));
        }
      }
@@ -159,7 +162,7 @@
      final DN domainDN = DN.valueOf(DN1_AS_STRING);
      ReplicationEnvironment environment = createReplicationEnv(rootPath);
      cnDB = environment.getOrCreateCNIndexDB();
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1);
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite);
      // put server id 1 offline
      CSN offlineCSN = new CSN(TimeThread.getTime(), 0, SERVER_ID_1);
@@ -189,7 +192,7 @@
      final DN domainDN = DN.valueOf(DN1_AS_STRING);
      ReplicationEnvironment environment = createReplicationEnv(rootPath);
      cnDB = environment.getOrCreateCNIndexDB();
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1);
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite);
      File offlineStateFile = new File(environment.getServerIdPath("1", 1), REPLICA_OFFLINE_STATE_FILENAME);
      offlineStateFile.createNewFile();
@@ -214,7 +217,7 @@
      ReplicationEnvironment environment = createReplicationEnv(rootPath);
      cnDB = environment.getOrCreateCNIndexDB();
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1);
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite);
      // put server id 1 offline twice
      CSNGenerator csnGenerator = new CSNGenerator(SERVER_ID_1, 100);
@@ -245,7 +248,7 @@
      ReplicationEnvironment environment = createReplicationEnv(rootPath);
      cnDB = environment.getOrCreateCNIndexDB();
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1);
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite);
      // put server id 1 offline
      environment.notifyReplicaOffline(domainDN, new CSN(TimeThread.getTime(), 0, SERVER_ID_1));
@@ -274,7 +277,7 @@
      ReplicationEnvironment environment = createReplicationEnv(rootPath);
      cnDB = environment.getOrCreateCNIndexDB();
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1);
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite);
      CSN offlineCSN = new CSN(TimeThread.getTime(), 0, SERVER_ID_1);
      environment.notifyReplicaOffline(domainDN, offlineCSN);
@@ -304,8 +307,8 @@
      File rootPath = new File(TEST_DIRECTORY_CHANGELOG);
      DN domainDN = DN.valueOf(DN1_AS_STRING);
      ReplicationEnvironment environment = createReplicationEnv(rootPath);
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1);
      replicaDB2 = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_2, 1);
      replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite);
      replicaDB2 = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_2, 1, cryptoSuite);
      // delete the domain directory created for the 2 replica DBs to break the
      // consistency with domain state file