From e1e337ccc14b01d5405cef806883e880b0243779 Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Fri, 02 Jan 2015 14:42:16 +0000
Subject: [PATCH] OPENDJ-1585 Re-implement DN normalization in server

---
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/AuthenticatedUsers.java                                                      |   21 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tools/DBTest.java                                                                 |   20 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/RDN.java                                                                    |  188 ++++++-
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServerDomain.java                                   |    4 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/ChangelogBackend.java                                                    |   10 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/EntryCacheConfigManager.java                                                 |   63 +-
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/ReplicationDbEnv.java                             |   35 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/DN.java                                                                     |  122 +++--
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java                                |   15 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/RootContainer.java                                                   |    4 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java                                     |   10 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationBroker.java                                        |    4 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/JebFormat.java                                                 |    5 
 opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java                         |    6 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java                                           |    7 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationDomain.java                                        |    4 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tasks/ImportTask.java                                                             |   19 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/JEReplicaDB.java                                  |    4 
 opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestJebFormat.java                           |   42 +
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/LightweightServerHandler.java                                  |    4 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/MessageHandler.java                                            |    6 
 opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProviderTestCase.java |   10 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/GroupManager.java                                                            |   31 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationDomainMonitor.java                                  |    4 
 opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java                                         |  243 +++++----
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/DN2URI.java                                                          |   78 ++
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EntryContainer.java                                                  |    5 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/plugins/EntryUUIDPlugin.java                                                      |    4 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java                                             |    4 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDB.java                              |    4 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/MultimasterReplication.java                                    |   12 
 opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java                                        |   56 -
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tools/ImportLDIF.java                                                             |    8 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/EntryHistorical.java                                           |    5 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/SubentryManager.java                                                         |   84 +--
 opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestBindResponseProtocolOp.java            |    8 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/monitors/ConnectionHandlerMonitor.java                                            |    5 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java                                   |    9 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/JebFormat.java                                                       |  112 ----
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/importLDIF/Importer.java                                             |   49 +
 opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestImportJob.java                           |    5 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProvider.java                                 |   21 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServer.java                                         |   13 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java                                          |    6 
 opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/DraftCNDB.java                                    |    4 
 opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java   |    4 
 opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java                    |    4 
 47 files changed, 729 insertions(+), 652 deletions(-)

diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/ChangelogBackend.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/ChangelogBackend.java
index 0d8c740..3d729bf 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/ChangelogBackend.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/ChangelogBackend.java
@@ -21,7 +21,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2014 ForgeRock AS.
+ *      Copyright 2014-2015 ForgeRock AS.
  */
 package org.opends.server.backends;
 
@@ -29,7 +29,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -745,12 +744,7 @@
    */
   private static Set<DN> getExcludedBaseDNs() throws DirectoryException
   {
-    final Set<DN> excludedDNs = new HashSet<DN>();
-    for (String dn : getExcludedChangelogDomains())
-    {
-      excludedDNs.add(DN.valueOf(dn));
-    }
-    return excludedDNs;
+    return getExcludedChangelogDomains();
   }
 
   /**
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/DN2URI.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/DN2URI.java
index 3b709e9..a4b4fed 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/DN2URI.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/DN2URI.java
@@ -22,19 +22,21 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2012-2014 ForgeRock AS
+ *      Portions Copyright 2012-2015 ForgeRock AS
  */
 package org.opends.server.backends.jeb;
 
-import java.io.UnsupportedEncodingException;
 import java.util.*;
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.ldap.ByteSequenceReader;
 import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.ByteStringBuilder;
 import org.forgerock.opendj.ldap.ConditionResult;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchScope;
+import org.forgerock.util.Pair;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.SearchOperation;
 import org.opends.server.types.*;
@@ -51,16 +53,22 @@
 
 /**
  * This class represents the referral database which contains URIs from referral
- * entries.  The key is the DN of the referral entry and the value is that of a
- * labeled URI in the ref attribute for that entry. Duplicate keys are permitted
- * since a referral entry can contain multiple values of the ref attribute.  Key
- * order is the same as in the DN database so that all referrals in a subtree
- * can be retrieved by cursoring through a range of the records.
+ * entries.
+ * <p>
+ * The key is the DN of the referral entry and the value is that of a pair
+ * (labeled URI in the ref attribute for that entry, DN). The DN must be
+ * duplicated in the value because the key is suitable for comparisons but is
+ * not reversible to a valid DN. Duplicate keys are permitted since a referral
+ * entry can contain multiple values of the ref attribute. Key order is the same
+ * as in the DN database so that all referrals in a subtree can be retrieved by
+ * cursoring through a range of the records.
  */
 public class DN2URI extends DatabaseContainer
 {
   private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
+  private static final byte STRING_SEPARATOR = (byte) 0x00;
+
   /**
    * The key comparator used for the DN database.
    */
@@ -124,9 +132,8 @@
        throws DatabaseException
   {
     byte[] normDN = JebFormat.dnToDNKey(dn, prefixRDNComponents);
-    byte[] URIBytes = StaticUtils.getBytes(labeledURI);
     DatabaseEntry key = new DatabaseEntry(normDN);
-    DatabaseEntry data = new DatabaseEntry(URIBytes);
+    DatabaseEntry data = new DatabaseEntry(encodeURIAndDN(labeledURI, dn));
 
     // The JE insert method does not permit duplicate keys so we must use the
     // put method.
@@ -138,6 +145,40 @@
     return false;
   }
 
+  private byte[] encodeURIAndDN(String labeledURI, DN dn)
+  {
+    return new ByteStringBuilder()
+      .append(labeledURI)
+      .append(STRING_SEPARATOR)
+      .append(dn.toString())
+      .toByteArray();
+  }
+
+  private Pair<String, DN> decodeURIAndDN(byte[] data) throws DirectoryException {
+    try {
+      final ByteSequenceReader reader = ByteString.valueOf(data).asReader();
+      final String labeledURI = reader.getString(getNextStringLength(reader));
+      // skip the string separator
+      reader.skip(1);
+      final DN dn = DN.valueOf(reader.getString(reader.remaining()));
+      return Pair.of(labeledURI, dn);
+    }
+    catch (Exception e) {
+       throw new DirectoryException(ResultCode.OPERATIONS_ERROR, ERR_JEB_DATABASE_EXCEPTION.get(e));
+    }
+  }
+
+  /** Returns the length of next string by looking for the zero byte used as separator. */
+  private int getNextStringLength(ByteSequenceReader reader)
+  {
+    int length = 0;
+    while (reader.peek(length) != STRING_SEPARATOR)
+    {
+      length++;
+    }
+    return length;
+  }
+
   /**
    * Delete URI values for a given referral entry from the referral database.
    *
@@ -528,7 +569,8 @@
             Set<String> labeledURIs = new LinkedHashSet<String>(cursor.count());
             do
             {
-              String labeledURI = new String(data.getData(), "UTF-8");
+              final Pair<String, DN> uriAndDN = decodeURIAndDN(data.getData());
+              final String labeledURI = uriAndDN.getFirst();
               labeledURIs.add(labeledURI);
               status = cursor.getNextDup(key, data, DEFAULT);
             } while (status == OperationStatus.SUCCESS);
@@ -546,10 +588,6 @@
     {
       logger.traceException(e);
     }
-    catch (UnsupportedEncodingException e)
-    {
-      logger.traceException(e);
-    }
   }
 
   /**
@@ -584,8 +622,7 @@
      * find subordinates of the base entry from the top of the tree
      * downwards.
      */
-    byte[] baseDN = JebFormat.dnToDNKey(searchOp.getBaseDN(),
-                                          prefixRDNComponents);
+    byte[] baseDN = JebFormat.dnToDNKey(searchOp.getBaseDN(), prefixRDNComponents);
     final byte special = 0x00;
     byte[] suffix = Arrays.copyOf(baseDN, baseDN.length+1);
     suffix[suffix.length - 1] = special;
@@ -622,7 +659,9 @@
           }
 
           // We have found a subordinate referral.
-          DN dn = dnFromDNKey(key.getData(), entryContainer.getBaseDN());
+          final Pair<String, DN> uriAndDN = decodeURIAndDN(data.getData());
+          final String labeledURI = uriAndDN.getFirst();
+          final DN dn = uriAndDN.getSecond();
 
           // Make sure the referral is within scope.
           if (searchOp.getScope() == SearchScope.SINGLE_LEVEL
@@ -636,7 +675,6 @@
           do
           {
             // Remove the label part of the labeled URI if there is a label.
-            String labeledURI = new String(data.getData(), "UTF-8");
             String uri = labeledURI;
             int i = labeledURI.indexOf(' ');
             if (i != -1)
@@ -699,10 +737,6 @@
     {
       logger.traceException(e);
     }
-    catch (UnsupportedEncodingException e)
-    {
-      logger.traceException(e);
-    }
 
     return true;
   }
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EntryContainer.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EntryContainer.java
index 65b9873..81ef8cc 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EntryContainer.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  *      Portions copyright 2013 Manuel Gaupp
  */
 package org.opends.server.backends.jeb;
@@ -1675,8 +1675,7 @@
             if (!pluginResult.continueProcessing())
             {
               LocalizableMessage message =
-                      ERR_JEB_DELETE_ABORTED_BY_SUBORDINATE_PLUGIN.get(
-                      dnFromDNKey(key.getData(), getBaseDN()));
+                  ERR_JEB_DELETE_ABORTED_BY_SUBORDINATE_PLUGIN.get(subordinateEntry.getName().toString());
               throw new DirectoryException(
                   DirectoryServer.getServerErrorResultCode(), message);
             }
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/JebFormat.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/JebFormat.java
index a0a0d4b..1a14d7c 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/JebFormat.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/JebFormat.java
@@ -22,18 +22,12 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2014 ForgeRock AS
+ *      Portions Copyright 2014-2015 ForgeRock AS
  */
 package org.opends.server.backends.jeb;
 
-import java.util.Iterator;
-import java.util.TreeSet;
-
 import org.forgerock.opendj.ldap.ByteStringBuilder;
 import org.opends.server.types.DN;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.RDN;
-import org.opends.server.util.StaticUtils;
 
 /**
  * Handles the disk representation of LDAP data.
@@ -254,62 +248,6 @@
   }
 
   /**
-   * Decode a DN value from its database key representation.
-   *
-   * @param dnKey The database key value of the DN.
-   * @param prefix The DN to prefix the decoded DN value.
-   * @return The decoded DN value.
-   * @throws DirectoryException if an error occurs while decoding the DN value.
-   * @see #dnToDNKey(DN, int)
-   */
-  public static DN dnFromDNKey(byte[] dnKey, DN prefix)
-      throws DirectoryException
-  {
-    DN dn = prefix;
-    boolean escaped = false;
-    ByteStringBuilder buffer = new ByteStringBuilder();
-    for (byte b : dnKey)
-    {
-      if (b == 0x5C)
-      {
-        escaped = true;
-        continue;
-      }
-      else if (!escaped && b == 0x01)
-      {
-        buffer.append(0x01);
-        escaped = false;
-        continue;
-      }
-      else if (!escaped && b == 0x00)
-      {
-        if(buffer.length() > 0)
-        {
-          dn = dn.child(RDN.decode(buffer.toString()));
-          buffer.clear();
-        }
-      }
-      else
-      {
-        if(escaped)
-        {
-          buffer.append(0x5C);
-          escaped = false;
-        }
-        buffer.append(b);
-      }
-    }
-
-    if(buffer.length() > 0)
-    {
-      dn = dn.child(RDN.decode(buffer.toString()));
-    }
-
-    return dn;
-  }
-
-
-  /**
    * Find the length of bytes that represents the superior DN of the given
    * DN key. The superior DN is represented by the initial bytes of the DN key.
    *
@@ -334,17 +272,17 @@
    */
   public static int findDNKeyParent(byte[] dnKey, int offset, int length)
   {
-    if(length == 0)
+    if (length == 0)
     {
       // This is the root or base DN
       return -1;
     }
 
-    // We will walk backwords through the buffer and find the first
-    // unescaped comma
-    for(int i = offset+length - 1; i >= offset; i--)
+    // We will walk backwords through the buffer and
+    // find the first unescaped NORMALIZED_RDN_SEPARATOR
+    for (int i = offset+length - 1; i >= offset; i--)
     {
-      if(dnKey[i] == 0x00 && i-1 >= offset && dnKey[i-1] != 0x5C)
+      if (dnKey[i] == DN.NORMALIZED_RDN_SEPARATOR && i-1 >= offset && dnKey[i-1] != DN.NORMALIZED_ESC_BYTE)
       {
         return i;
       }
@@ -354,48 +292,24 @@
 
   /**
    * Create a DN database key from an entry DN.
+   *
    * @param dn The entry DN.
    * @param prefixRDNs The number of prefix RDNs to remove from the encoded
    *                   representation.
    * @return A DatabaseEntry containing the key.
-   * @see #dnFromDNKey(byte[], DN)
    */
   public static byte[] dnToDNKey(DN dn, int prefixRDNs)
   {
-    StringBuilder buffer = new StringBuilder();
-    for (int i = dn.size() - prefixRDNs - 1; i >= 0; i--)
+    ByteStringBuilder builder = new ByteStringBuilder();
+    int startSize = dn.size() - prefixRDNs - 1;
+    for (int i = startSize; i >= 0; i--)
     {
-      buffer.append('\u0000');
-      formatRDNKey(dn.getRDN(i), buffer);
+        builder.append(DN.NORMALIZED_RDN_SEPARATOR);
+        dn.getRDN(i).toNormalizedByteString(builder);
     }
 
-    return StaticUtils.getBytes(buffer.toString());
+    return builder.toByteArray();
   }
 
-  private static void formatRDNKey(RDN rdn, StringBuilder buffer)
-  {
-    if (!rdn.isMultiValued())
-    {
-      rdn.toNormalizedString(buffer);
-    }
-    else
-    {
-      TreeSet<String> rdnElementStrings = new TreeSet<String>();
 
-      for (int i=0; i < rdn.getNumValues(); i++)
-      {
-        StringBuilder b2 = new StringBuilder();
-        rdn.getNormalizedAVAString(i, b2);
-        rdnElementStrings.add(b2.toString());
-      }
-
-      Iterator<String> iterator = rdnElementStrings.iterator();
-      buffer.append(iterator.next().replace("\u0001", "\\\u0001"));
-      while (iterator.hasNext())
-      {
-        buffer.append('\u0001');
-        buffer.append(iterator.next().replace("\u0001", "\\\u0001"));
-      }
-    }
-  }
 }
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/RootContainer.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/RootContainer.java
index 6d37c6c..e8ca3fb 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/RootContainer.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/RootContainer.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.backends.jeb;
 
@@ -233,7 +233,7 @@
     String databasePrefix;
     if(name == null || name.equals(""))
     {
-      databasePrefix = baseDN.toNormalizedString();
+      databasePrefix = baseDN.toIrreversibleReadableString();
     }
     else
     {
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/importLDIF/Importer.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/importLDIF/Importer.java
index 31ad498..d13ac3a 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/importLDIF/Importer.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/jeb/importLDIF/Importer.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.backends.jeb.importLDIF;
 
@@ -746,7 +746,8 @@
         {
           // Create a temp entry container
           sourceEntryContainer = entryContainer;
-          entryContainer = rootContainer.openEntryContainer(baseDN, baseDN.toNormalizedString() + "_importTmp");
+          entryContainer = rootContainer.openEntryContainer(baseDN, baseDN.toIrreversibleReadableString()
+              + "_importTmp");
         }
       }
     }
@@ -987,7 +988,7 @@
         needRegisterContainer.unlock();
         EntryContainer newEC = suffix.getEntryContainer();
         newEC.lock();
-        newEC.setDatabasePrefix(baseDN.toNormalizedString());
+        newEC.setDatabasePrefix(baseDN.toIrreversibleReadableString());
         newEC.unlock();
         rootContainer.registerEntryContainer(baseDN, newEC);
       }
@@ -4276,17 +4277,22 @@
     public boolean insert(DN dn, DatabaseEntry val, DatabaseEntry key)
         throws JebException
     {
-      byte[] dnBytes = StaticUtils.getBytes(dn.toNormalizedString());
-      int len = PackedInteger.getWriteIntLength(dnBytes.length);
-      byte[] dataBytes = new byte[dnBytes.length + len];
-      int pos = PackedInteger.writeInt(dataBytes, 0, dnBytes.length);
-      System.arraycopy(dnBytes, 0, dataBytes, pos, dnBytes.length);
+      // Use a compact representation for key
+      byte[] dnBytesForKey = dn.toIrreversibleNormalizedByteString().toByteArray();
+      key.setData(hashCode(dnBytesForKey));
+
+      // Use a reversible representation for value
+      byte[] dnBytesForValue = StaticUtils.getBytes(dn.toString());
+      int len = PackedInteger.getWriteIntLength(dnBytesForValue.length);
+      byte[] dataBytes = new byte[dnBytesForValue.length + len];
+      int pos = PackedInteger.writeInt(dataBytes, 0, dnBytesForValue.length);
+      System.arraycopy(dnBytesForValue, 0, dataBytes, pos, dnBytesForValue.length);
       val.setData(dataBytes);
-      key.setData(hashCode(dnBytes));
-      return insert(key, val, dnBytes);
+
+      return insert(key, val, dnBytesForValue);
     }
 
-    private boolean insert(DatabaseEntry key, DatabaseEntry val, byte[] dnBytes)
+    private boolean insert(DatabaseEntry key, DatabaseEntry val, byte[] dnBytesForValue)
         throws JebException
     {
       Cursor cursor = null;
@@ -4302,9 +4308,9 @@
           {
             throw new JebException(LocalizableMessage.raw("Search DN cache failed."));
           }
-          if (!isDNMatched(dns, dnBytes))
+          if (!isDNMatched(dns, dnBytesForValue))
           {
-            addDN(dns, cursor, dnBytes);
+            addDN(dns, cursor, dnBytesForValue);
             return true;
           }
           return false;
@@ -4318,17 +4324,17 @@
     }
 
     /** Add the DN to the DNs as because of a hash collision. */
-    private void addDN(DatabaseEntry val, Cursor cursor, byte[] dnBytes)
+    private void addDN(DatabaseEntry val, Cursor cursor, byte[] dnBytesForValue)
         throws JebException
     {
       byte[] bytes = val.getData();
-      int pLen = PackedInteger.getWriteIntLength(dnBytes.length);
-      int totLen = bytes.length + pLen + dnBytes.length;
+      int pLen = PackedInteger.getWriteIntLength(dnBytesForValue.length);
+      int totLen = bytes.length + pLen + dnBytesForValue.length;
       byte[] newRec = new byte[totLen];
       System.arraycopy(bytes, 0, newRec, 0, bytes.length);
       int pos = bytes.length;
-      pos = PackedInteger.writeInt(newRec, pos, dnBytes.length);
-      System.arraycopy(dnBytes, 0, newRec, pos, dnBytes.length);
+      pos = PackedInteger.writeInt(newRec, pos, dnBytesForValue.length);
+      System.arraycopy(dnBytesForValue, 0, newRec, pos, dnBytesForValue.length);
       DatabaseEntry newVal = new DatabaseEntry(newRec);
       OperationStatus status = cursor.putCurrent(newVal);
       if (status != OperationStatus.SUCCESS)
@@ -4371,14 +4377,15 @@
     {
       Cursor cursor = null;
       DatabaseEntry key = new DatabaseEntry();
-      byte[] dnBytes = StaticUtils.getBytes(dn.toNormalizedString());
-      key.setData(hashCode(dnBytes));
+      byte[] dnBytesForKey = dn.toIrreversibleNormalizedByteString().toByteArray();
+      key.setData(hashCode(dnBytesForKey));
       try
       {
         cursor = dnCache.openCursor(null, CursorConfig.DEFAULT);
         DatabaseEntry dns = new DatabaseEntry();
         OperationStatus status = cursor.getSearchKey(key, dns, LockMode.DEFAULT);
-        return status == OperationStatus.SUCCESS && isDNMatched(dns, dnBytes);
+        byte[] dnBytesForValue = StaticUtils.getBytes(dn.toString());
+        return status == OperationStatus.SUCCESS && isDNMatched(dns, dnBytesForValue);
       }
       finally
       {
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/JebFormat.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/JebFormat.java
index 9a5dd2c..89c2de3 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/JebFormat.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/JebFormat.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2014 ForgeRock AS
+ *      Portions Copyright 2014-2015 ForgeRock AS
  */
 package org.opends.server.backends.pluggable;
 
@@ -146,7 +146,7 @@
   {
     if (!rdn.isMultiValued())
     {
-      rdn.toNormalizedString(buffer);
+      rdn.toString(buffer);
     }
     else
     {
@@ -155,7 +155,6 @@
       for (int i=0; i < rdn.getNumValues(); i++)
       {
         StringBuilder b2 = new StringBuilder();
-        rdn.getNormalizedAVAString(i, b2);
         rdnElementStrings.add(b2.toString());
       }
 
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java
index 57f4211..9aa11f5 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/backends/pluggable/RootContainer.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.backends.pluggable;
 
@@ -419,7 +419,7 @@
     String databasePrefix;
     if(name == null || name.equals(""))
     {
-      databasePrefix = baseDN.toNormalizedString();
+      databasePrefix = baseDN.toIrreversibleReadableString();
     }
     else
     {
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/AuthenticatedUsers.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/AuthenticatedUsers.java
index f90758f..29d3941 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/AuthenticatedUsers.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/AuthenticatedUsers.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -268,8 +268,8 @@
 
     Entry oldEntry = op.getOriginalEntry();
     Entry newEntry = op.getUpdatedEntry();
-    String oldDNString = oldEntry.getName().toNormalizedString();
-    String newDNString = newEntry.getName().toNormalizedString();
+    DN oldDN = oldEntry.getName();
+    DN newDN = newEntry.getName();
 
     // Identify any client connections that may be authenticated
     // or authorized as the user whose entry has been modified
@@ -295,7 +295,7 @@
             authNDN = conn.getAuthenticationInfo().getAuthenticationDN();
             try
             {
-              newAuthNDN = getNewAuthDN(authNDN, oldDNString, newDNString);
+              newAuthNDN = authNDN.rename(oldDN, newDN);
             }
             catch (Exception e)
             {
@@ -308,7 +308,7 @@
             authZDN = conn.getAuthenticationInfo().getAuthorizationDN();
             try
             {
-              newAuthZDN = getNewAuthDN(authZDN, oldDNString, newDNString);
+              newAuthZDN = authZDN.rename(oldDN, newDN);
             }
             catch (Exception e)
             {
@@ -353,16 +353,5 @@
     }
     return PostResponse.continueOperationProcessing();
   }
-
-  private DN getNewAuthDN(DN authDN, String oldDNString, String newDNString) throws DirectoryException
-  {
-    // FIXME once we move to the SDK:
-    // Matt suggests we should be using the following code here:
-    // return authDN.rename(oldDNString, newDNString);
-    final StringBuilder builder = new StringBuilder(authDN.toNormalizedString());
-    final int oldDNIndex = builder.lastIndexOf(oldDNString);
-    builder.replace(oldDNIndex, builder.length(), newDNString);
-    return DN.valueOf(builder.toString());
-  }
 }
 
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/EntryCacheConfigManager.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/EntryCacheConfigManager.java
index 74b7463..7fcdbc3 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/EntryCacheConfigManager.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/EntryCacheConfigManager.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
- *      Portions Copyright 2013-2014 ForgeRock AS.
+ *      Portions Copyright 2013-2015 ForgeRock AS.
  */
 package org.opends.server.core;
 
@@ -30,6 +30,7 @@
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.util.Utils;
 import org.opends.server.admin.ClassPropertyDefinition;
 import org.opends.server.admin.server.ConfigurationAddListener;
@@ -76,9 +77,8 @@
   private SortedMap<Integer, EntryCache> cacheOrderMap =
       new TreeMap<Integer, EntryCache>();
 
-  // The entry cache name to level map.
-  private HashMap<String, Integer>
-    cacheNameToLevelMap = new HashMap<String, Integer>();
+  /** The entry cache name to level map. The key is a byte string representation of the name (DN). */
+  private Map<ByteString,Integer> cacheNameToLevelMap = new HashMap<ByteString, Integer>();
 
   // Global entry cache monitor provider name.
   private static final String
@@ -255,19 +255,20 @@
       status = false;
     }
 
-    if (!cacheOrderMap.isEmpty() && !cacheNameToLevelMap.isEmpty() &&
-      (cacheNameToLevelMap.get(
-       configuration.dn().toNormalizedString()) != null)) {
-      int currentCacheLevel = cacheNameToLevelMap.get(
-        configuration.dn().toNormalizedString());
+    if (!cacheOrderMap.isEmpty() && !cacheNameToLevelMap.isEmpty())
+    {
+      final ByteString normDN = configuration.dn().toIrreversibleNormalizedByteString();
+      if (cacheNameToLevelMap.containsKey(normDN)) {
+        int currentCacheLevel = cacheNameToLevelMap.get(normDN);
 
-      // Check if there any existing cache at the same level.
-      if ((currentCacheLevel != configuration.getCacheLevel()) &&
-        (cacheOrderMap.containsKey(configuration.getCacheLevel()))) {
-        unacceptableReasons.add(
-          ERR_CONFIG_ENTRYCACHE_CONFIG_LEVEL_NOT_ACCEPTABLE.get(
-            configuration.dn(), configuration.getCacheLevel()));
-        status = false;
+        // Check if there any existing cache at the same level.
+        if ((currentCacheLevel != configuration.getCacheLevel()) &&
+          (cacheOrderMap.containsKey(configuration.getCacheLevel()))) {
+          unacceptableReasons.add(
+            ERR_CONFIG_ENTRYCACHE_CONFIG_LEVEL_NOT_ACCEPTABLE.get(
+              configuration.dn(), configuration.getCacheLevel()));
+          status = false;
+        }
       }
     }
 
@@ -287,20 +288,20 @@
 
     // If we this entry cache is already installed and active it
     // should be present in the cache maps, if so use it.
-    if (!cacheOrderMap.isEmpty() && !cacheNameToLevelMap.isEmpty() &&
-      (cacheNameToLevelMap.get(
-       configuration.dn().toNormalizedString()) != null)) {
-      int currentCacheLevel = cacheNameToLevelMap.get(
-        configuration.dn().toNormalizedString());
-      entryCache = cacheOrderMap.get(currentCacheLevel);
+    if (!cacheOrderMap.isEmpty() && !cacheNameToLevelMap.isEmpty()) {
+      final ByteString normDN = configuration.dn().toIrreversibleNormalizedByteString();
+      if (cacheNameToLevelMap.containsKey(normDN))
+      {
+        int currentCacheLevel = cacheNameToLevelMap.get(normDN);
+        entryCache = cacheOrderMap.get(currentCacheLevel);
 
-      // Check if the existing cache just shifted its level.
-      if (currentCacheLevel != configuration.getCacheLevel()) {
-        // Update the maps then.
-        cacheOrderMap.remove(currentCacheLevel);
-        cacheOrderMap.put(configuration.getCacheLevel(), entryCache);
-        cacheNameToLevelMap.put(configuration.dn().toNormalizedString(),
-          configuration.getCacheLevel());
+        // Check if the existing cache just shifted its level.
+        if (currentCacheLevel != configuration.getCacheLevel()) {
+          // Update the maps then.
+          cacheOrderMap.remove(currentCacheLevel);
+          cacheOrderMap.put(configuration.getCacheLevel(), entryCache);
+          cacheNameToLevelMap.put(normDN, configuration.getCacheLevel());
+        }
       }
     }
 
@@ -484,7 +485,7 @@
       }
       entryCache.finalizeEntryCache();
       cacheOrderMap.remove(configuration.getCacheLevel());
-      cacheNameToLevelMap.remove(configuration.dn().toNormalizedString());
+      cacheNameToLevelMap.remove(configuration.dn().toIrreversibleNormalizedByteString());
 
       // Push any changes made to the cache order map.
       setCacheOrder(cacheOrderMap);
@@ -530,7 +531,7 @@
 
     // Add this entry cache to the current cache config maps.
     cacheOrderMap.put(configuration.getCacheLevel(), entryCache);
-    cacheNameToLevelMap.put(configuration.dn().toNormalizedString(),
+    cacheNameToLevelMap.put(configuration.dn().toIrreversibleNormalizedByteString(),
       configuration.getCacheLevel());
 
     // Push any changes made to the cache order map.
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/GroupManager.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/GroupManager.java
index 2a0be7c..ff6c88b 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/GroupManager.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/GroupManager.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2007-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -804,30 +804,15 @@
     try
     {
       Set<Group<?>> groupSet = new HashSet<Group<?>>();
-      groupInstances.removeSubtree(oldEntry.getName(), groupSet);
-      String oldDNString = oldEntry.getName().toNormalizedString();
-      String newDNString = newEntry.getName().toNormalizedString();
+      final DN oldDN = oldEntry.getName();
+      final DN newDN = newEntry.getName();
+      groupInstances.removeSubtree(oldDN, groupSet);
       for (Group<?> group : groupSet)
       {
-        StringBuilder builder = new StringBuilder(
-                group.getGroupDN().toNormalizedString());
-        int oldDNIndex = builder.lastIndexOf(oldDNString);
-        builder.replace(oldDNIndex, builder.length(), newDNString);
-        String groupDNString = builder.toString();
-        DN groupDN;
-        try
-        {
-          groupDN = DN.valueOf(groupDNString);
-        }
-        catch (DirectoryException de)
-        {
-          // Should not happen but if it does all we
-          // can do here is debug log it and continue.
-          logger.traceException(de);
-          continue;
-        }
-        group.setGroupDN(groupDN);
-        groupInstances.put(groupDN, group);
+        final DN groupDN = group.getGroupDN();
+        final DN renamedGroupDN = groupDN.rename(oldDN, newDN);
+        group.setGroupDN(renamedGroupDN);
+        groupInstances.put(renamedGroupDN, group);
       }
       if (!groupSet.isEmpty())
       {
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/SubentryManager.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/SubentryManager.java
index 1c46a5c..f6d9682 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/SubentryManager.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/core/SubentryManager.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -190,7 +190,7 @@
    * Add a given entry to this subentry manager.
    * @param entry to add.
    */
-  private void addSubEntry(Entry entry) throws DirectoryException
+  private void addSubentry(Entry entry) throws DirectoryException
   {
     SubEntry subEntry = new SubEntry(entry);
     SubtreeSpecification subSpec =
@@ -233,7 +233,7 @@
    * Remove a given entry from this subentry manager.
    * @param entry to remove.
    */
-  private void removeSubEntry(Entry entry)
+  private void removeSubentry(Entry entry)
   {
     lock.writeLock().lock();
     try
@@ -368,7 +368,7 @@
         {
           try
           {
-            addSubEntry(entry);
+            addSubentry(entry);
 
             // Notify change listeners.
             for (SubentryChangeListener changeListener :
@@ -708,7 +708,7 @@
       {
         try
         {
-          addSubEntry(entry);
+          addSubentry(entry);
 
           // Notify change listeners.
           for (SubentryChangeListener changeListener :
@@ -745,7 +745,7 @@
     {
       for (SubEntry subEntry : dit2SubEntry.getSubtree(entry.getName()))
       {
-        removeSubEntry(subEntry.getEntry());
+        removeSubentry(subEntry.getEntry());
 
         // Notify change listeners.
         for (SubentryChangeListener changeListener :
@@ -777,14 +777,14 @@
     {
       if (oldEntry.isSubentry() || oldEntry.isLDAPSubentry())
       {
-        removeSubEntry(oldEntry);
+        removeSubentry(oldEntry);
         notify = true;
       }
       if (newEntry.isSubentry() || newEntry.isLDAPSubentry())
       {
         try
         {
-          addSubEntry(newEntry);
+          addSubentry(newEntry);
           notify = true;
         }
         catch (Exception e)
@@ -819,31 +819,24 @@
     }
   }
 
-  private void doPostModifyDN(Entry oldEntry, Entry newEntry)
+  private void doPostModifyDN(final Entry oldEntry, final Entry newEntry)
   {
-    String oldDNString = oldEntry.getName().toNormalizedString();
-    String newDNString = newEntry.getName().toNormalizedString();
-
     lock.writeLock().lock();
     try
     {
-      Collection<SubEntry> setToDelete =
-              dit2SubEntry.getSubtree(oldEntry.getName());
+      Collection<SubEntry> setToDelete = dit2SubEntry.getSubtree(oldEntry.getName());
       for (SubEntry subentry : setToDelete)
       {
-        removeSubEntry(subentry.getEntry());
-        oldEntry = subentry.getEntry();
+        final Entry currentSubentry = subentry.getEntry();
+        removeSubentry(currentSubentry);
+
+        Entry renamedSubentry = null;
         try
         {
-          StringBuilder builder = new StringBuilder(
-              subentry.getEntry().getName().toNormalizedString());
-          int oldDNIndex = builder.lastIndexOf(oldDNString);
-          builder.replace(oldDNIndex, builder.length(),
-                  newDNString);
-          String subentryDNString = builder.toString();
-          newEntry = subentry.getEntry().duplicate(false);
-          newEntry.setDN(DN.valueOf(subentryDNString));
-          addSubEntry(newEntry);
+          renamedSubentry = currentSubentry.duplicate(false);
+          final DN renamedDN = currentSubentry.getName().rename(oldEntry.getName(), newEntry.getName());
+          renamedSubentry.setDN(renamedDN);
+          addSubentry(renamedSubentry);
         }
         catch (Exception e)
         {
@@ -857,8 +850,7 @@
         {
           try
           {
-            changeListener.handleSubentryModify(
-                    oldEntry, newEntry);
+            changeListener.handleSubentryModify(currentSubentry, renamedSubentry);
           }
           catch (Exception e)
           {
@@ -1017,18 +1009,14 @@
    * {@inheritDoc}
    */
   @Override
-  public PreOperation doPreOperation(
-          PreOperationModifyDNOperation modifyDNOperation)
+  public PreOperation doPreOperation(PreOperationModifyDNOperation modifyDNOperation)
   {
-    Entry oldEntry = modifyDNOperation.getOriginalEntry();
-    Entry newEntry = modifyDNOperation.getUpdatedEntry();
-    String oldDNString = oldEntry.getName().toNormalizedString();
-    String newDNString = newEntry.getName().toNormalizedString();
     boolean hasSubentryWritePrivilege = false;
 
     lock.readLock().lock();
     try
     {
+      final Entry oldEntry = modifyDNOperation.getOriginalEntry();
       Collection<SubEntry> setToDelete =
               dit2SubEntry.getSubtree(oldEntry.getName());
       for (SubEntry subentry : setToDelete)
@@ -1049,30 +1037,18 @@
             hasSubentryWritePrivilege = true;
           }
         }
-        oldEntry = subentry.getEntry();
-        try
-        {
-          StringBuilder builder = new StringBuilder(
-              subentry.getEntry().getName().toNormalizedString());
-          int oldDNIndex = builder.lastIndexOf(oldDNString);
-          builder.replace(oldDNIndex, builder.length(),
-                  newDNString);
-          String subentryDNString = builder.toString();
-          newEntry = subentry.getEntry().duplicate(false);
-          newEntry.setDN(DN.valueOf(subentryDNString));
-        }
-        catch (Exception e)
-        {
-          // Shouldnt happen.
-          logger.traceException(e);
-        }
-        for (SubentryChangeListener changeListener :
-                changeListeners)
+
+        final Entry newEntry = modifyDNOperation.getUpdatedEntry();
+        final Entry currentSubentry = subentry.getEntry();
+        final Entry renamedSubentry = currentSubentry.duplicate(false);
+        final DN renamedDN = currentSubentry.getName().rename(oldEntry.getName(), newEntry.getName());
+        renamedSubentry.setDN(renamedDN);
+
+        for (SubentryChangeListener changeListener : changeListeners)
         {
           try
           {
-            changeListener.checkSubentryModifyAcceptable(
-                    oldEntry, newEntry);
+            changeListener.checkSubentryModifyAcceptable(currentSubentry, renamedSubentry);
           }
           catch (DirectoryException de)
           {
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java
index d56b4fa..616bfbb 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2012-2014 ForgeRock AS
+ *      Portions Copyright 2012-2015 ForgeRock AS
  */
 package org.opends.server.extensions;
 
@@ -73,8 +73,8 @@
   @Override()
   public Attribute getValues(Entry entry, VirtualAttributeRule rule)
   {
-    String normDNString = entry.getName().toNormalizedString();
-    return Attributes.create(rule.getAttributeType(), normDNString);
+    String dnString = entry.getName().toString();
+    return Attributes.create(rule.getAttributeType(), dnString);
   }
 
   /** {@inheritDoc} */
@@ -166,8 +166,7 @@
                               SearchOperation searchOperation,
                               boolean isPreIndexed)
   {
-    return isSearchable(rule.getAttributeType(), searchOperation.getFilter(),
-                        0);
+    return isSearchable(rule.getAttributeType(), searchOperation.getFilter(), 0);
   }
 
 
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProvider.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProvider.java
index 93a59b0..3034e55 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProvider.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProvider.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2012-2014 ForgeRock AS
+ *      Portions Copyright 2012-2015 ForgeRock AS
  */
 package org.opends.server.extensions;
 
@@ -81,10 +81,13 @@
   @Override()
   public Attribute getValues(Entry entry, VirtualAttributeRule rule)
   {
-    String normDNString = entry.getName().toNormalizedString();
-    String uuidString =
-         UUID.nameUUIDFromBytes(getBytes(normDNString)).toString();
-    return Attributes.create(rule.getAttributeType(), uuidString);
+    return Attributes.create(rule.getAttributeType(), getUUIDString(entry));
+  }
+
+  private String getUUIDString(Entry entry)
+  {
+    ByteString normDN = entry.getName().toIrreversibleNormalizedByteString();
+    return UUID.nameUUIDFromBytes(normDN.toByteArray()).toString();
   }
 
   /** {@inheritDoc} */
@@ -99,14 +102,10 @@
   @Override()
   public boolean hasValue(Entry entry, VirtualAttributeRule rule, ByteString value)
   {
-    MatchingRule matchingRule =
-        rule.getAttributeType().getEqualityMatchingRule();
+    MatchingRule matchingRule = rule.getAttributeType().getEqualityMatchingRule();
     try
     {
-      String normalizedDN = entry.getName().toNormalizedString();
-      String uuidString =
-           UUID.nameUUIDFromBytes(getBytes(normalizedDN)).toString();
-
+      String uuidString = getUUIDString(entry);
       ByteString normValue = matchingRule.normalizeAttributeValue(value);
       return uuidString.equals(normValue.toString());
     }
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
index e69a4ad..fbedcdd 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.extensions;
 
@@ -260,10 +260,10 @@
     {
       // Check for nested groups to see if we need to keep track of returned entries
       List<DN> nestedGroupsDNs = group.getNestedGroupDNs();
-      HashSet<String> returnedDNs = null;
+      Set<ByteString> returnedDNs = null;
       if (!nestedGroupsDNs.isEmpty())
       {
-        returnedDNs = new HashSet<String>();
+        returnedDNs = new HashSet<ByteString>();
       }
       if (!returnGroupMembers(searchOperation, group.getMembers(), returnedDNs))
       {
@@ -289,7 +289,7 @@
    *
    * @param searchOperation the search operation being processed.
    * @param memberList the list of members of the group being processed.
-   * @param returnedDNs a set to store the DNs of entries already returned,
+   * @param returnedDNs a set to store the normalized DNs of entries already returned,
    *                    null if there's no need to track for entries.
    * @return  <CODE>true</CODE> if the caller should continue processing the
    *          search request and sending additional entries and references, or
@@ -299,7 +299,7 @@
    *          the entry to the client and the search should be terminated.
    */
   private boolean returnGroupMembers(SearchOperation searchOperation,
-                                  MemberList memberList, Set<String> returnedDNs)
+                                  MemberList memberList, Set<ByteString> returnedDNs)
           throws DirectoryException
   {
     DN baseDN = searchOperation.getBaseDN();
@@ -313,8 +313,9 @@
         if (e.matchesBaseAndScope(baseDN, scope) &&
             filter.matchesEntry(e))
         {
-          if (returnedDNs == null
-              || returnedDNs.add(e.getName().toNormalizedString()))
+          // The set of returned DNs is only used for detecting set membership
+          // so it's ok to use the irreversible representation of the DN
+          if (returnedDNs == null || returnedDNs.add(e.getName().toIrreversibleNormalizedByteString()))
           {
             if (!searchOperation.returnEntry(e, null))
             {
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/monitors/ConnectionHandlerMonitor.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/monitors/ConnectionHandlerMonitor.java
index 867ad06..8bb5578 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/monitors/ConnectionHandlerMonitor.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/monitors/ConnectionHandlerMonitor.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2014 ForgeRock AS
+ *      Portions Copyright 2014-2015 ForgeRock AS
  */
 package org.opends.server.monitors;
 
@@ -148,8 +148,7 @@
     LinkedList<Attribute> attrs = new LinkedList<Attribute>();
 
     // Configuration DN
-    attrs.add(Attributes.create(configDnType, String.valueOf(
-            connectionHandler.getComponentEntryDN().toNormalizedString())));
+    attrs.add(Attributes.create(configDnType, String.valueOf(connectionHandler.getComponentEntryDN().toString())));
 
     int numConnections = 0;
     LinkedList<ClientConnection> conns = new LinkedList<ClientConnection>(
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/plugins/EntryUUIDPlugin.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/plugins/EntryUUIDPlugin.java
index cbe52ea..0ce45f7 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/plugins/EntryUUIDPlugin.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/plugins/EntryUUIDPlugin.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
- *      Portions Copyright 2014 ForgeRock AS
+ *      Portions Copyright 2014-2015 ForgeRock AS
  */
 package org.opends.server.plugins;
 
@@ -170,7 +170,7 @@
     // Construct a new UUID.  In order to make sure that UUIDs are consistent
     // when the same LDIF is generated on multiple servers, we'll base the UUID
     // on the byte representation of the normalized DN.
-    byte[] dnBytes = getBytes(entry.getName().toNormalizedString());
+    byte[] dnBytes = entry.getName().toIrreversibleNormalizedByteString().toByteArray();
     UUID uuid = UUID.nameUUIDFromBytes(dnBytes);
 
     Attribute uuidAttr = Attributes.create(entryUUIDType, uuid.toString());
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java
index 1deac6b..c2a494a 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS.
+ *      Portions Copyright 2011-2015 ForgeRock AS.
  *      Portions copyright 2011 profiq s.r.o.
  */
 package org.opends.server.plugins;
@@ -875,8 +875,7 @@
         setupWriter();
         for(Map.Entry<DN,DN> mapEntry : modDNmap.entrySet())
         {
-          writer.write(mapEntry.getKey().toNormalizedString() + "\t" +
-                  mapEntry.getValue().toNormalizedString());
+          writer.write(mapEntry.getKey().toString() + "\t" + mapEntry.getValue().toString());
           writer.newLine();
         }
         writer.flush();
@@ -904,7 +903,7 @@
         setupWriter();
         for (DN deletedEntryDN : deleteDNset)
         {
-          writer.write(deletedEntryDN.toNormalizedString());
+          writer.write(deletedEntryDN.toString());
           writer.newLine();
         }
         writer.flush();
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/EntryHistorical.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/EntryHistorical.java
index fb2058d..a8f1077 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/EntryHistorical.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/EntryHistorical.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.plugin;
 
@@ -919,8 +919,7 @@
           entryDN);
     }
 
-    String normDNString = entryDN.toNormalizedString();
-    return UUID.nameUUIDFromBytes(getBytes(normDNString)).toString();
+    return UUID.nameUUIDFromBytes(entryDN.toIrreversibleNormalizedByteString().toByteArray()).toString();
   }
 }
 
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
index 8c61a30..f8aacc7 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/LDAPReplicationDomain.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.plugin;
 
@@ -2225,7 +2225,7 @@
     if (markConflict)
     {
       Attribute attr =
-          Attributes.create(attrType, targetDN.toNormalizedString());
+          Attributes.create(attrType, targetDN.toString());
       newOp.addModification(new Modification(ModificationType.REPLACE, attr));
     }
     else
@@ -2954,7 +2954,7 @@
       {
         addConflict(msg);
         String conflictRDN =
-            generateConflictRDN(entryUUID, msg.getDN().toNormalizedString());
+            generateConflictRDN(entryUUID, msg.getDN().toString());
         msg.setDN(DN.valueOf(conflictRDN));
         numUnresolvedNamingConflicts.incrementAndGet();
         return false;
@@ -3054,7 +3054,7 @@
     // create new internal modify operation and run it.
     AttributeType attrType = DirectoryServer.getAttributeType(DS_SYNC_CONFLICT,
         true);
-    Attribute attr = Attributes.create(attrType, conflictDN.toNormalizedString());
+    Attribute attr = Attributes.create(attrType, conflictDN.toString());
     List<Modification> mods =
         newList(new Modification(ModificationType.REPLACE, attr));
 
@@ -3087,7 +3087,7 @@
    */
   private void addConflict(AddMsg msg) throws DecodeException
   {
-    String normalizedDN = msg.getDN().toNormalizedString();
+    String normalizedDN = msg.getDN().toString();
 
     // Generate an alert to let the administrator know that some
     // conflict could not be solved.
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/MultimasterReplication.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/MultimasterReplication.java
index c43111b..9a67737 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/MultimasterReplication.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/plugin/MultimasterReplication.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.plugin;
 
@@ -852,16 +852,18 @@
    * Gets the Set of domain baseDN which are disabled for the external changelog.
    *
    * @return The Set of domain baseDNs which are disabled for the external changelog.
+   * @throws DirectoryException
+   *            if a problem occurs
    */
-  public static Set<String> getExcludedChangelogDomains()
+  public static Set<DN> getExcludedChangelogDomains() throws DirectoryException
   {
-    final Set<String> disabledBaseDNs = new HashSet<String>(domains.size() + 1);
-    disabledBaseDNs.add(DN_EXTERNAL_CHANGELOG_ROOT);
+    final Set<DN> disabledBaseDNs = new HashSet<DN>(domains.size() + 1);
+    disabledBaseDNs.add(DN.valueOf(DN_EXTERNAL_CHANGELOG_ROOT));
     for (LDAPReplicationDomain domain : domains.values())
     {
       if (!domain.isECLEnabled())
       {
-        disabledBaseDNs.add(domain.getBaseDN().toNormalizedString());
+        disabledBaseDNs.add(domain.getBaseDN());
       }
     }
     return disabledBaseDNs;
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/LightweightServerHandler.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/LightweightServerHandler.java
index 982cd1c..52b09e6 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/LightweightServerHandler.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/LightweightServerHandler.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.server;
 
@@ -172,7 +172,7 @@
     final ReplicationServerDomain domain = replServerHandler.getDomain();
     attributes.add(Attributes.create("server-id", String.valueOf(serverId)));
     attributes.add(Attributes.create("domain-name",
-        domain.getBaseDN().toNormalizedString()));
+        domain.getBaseDN().toString()));
     attributes.add(Attributes.create("connected-to",
         replServerHandler.getMonitorInstanceName()));
 
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/MessageHandler.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/MessageHandler.java
index f1ea2ce..8b3d7ab 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/MessageHandler.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/MessageHandler.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.server;
 
@@ -566,7 +566,7 @@
    */
   protected String getBaseDNString()
   {
-    return baseDN.toNormalizedString();
+    return baseDN.toString();
   }
 
   /**
@@ -643,7 +643,7 @@
     else
     {
       this.baseDN = baseDN;
-      setDomain(!"cn=changelog".equals(baseDN.toNormalizedString())
+      setDomain(!"cn=changelog".equals(baseDN.toIrreversibleReadableString())
           && isDataServer);
     }
   }
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationDomainMonitor.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationDomainMonitor.java
index 6d3057f..78ee54d 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationDomainMonitor.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationDomainMonitor.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.server;
 
@@ -154,7 +154,7 @@
       {
         try
         {
-          String baseDN = domain.getBaseDN().toNormalizedString();
+          String baseDN = domain.getBaseDN().toString();
 
           // Prevent out of band monitor responses from updating our pending
           // table until we are ready.
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServer.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServer.java
index 6423d53..203088f 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServer.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServer.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.server;
 
@@ -1279,16 +1279,16 @@
    * Returns the newest cookie value.
    *
    * @param excludedBaseDNs
-   *          The list of baseDNs excluded from ECL.
+   *          The set of baseDNs excluded from ECL.
    * @return the newest cookie value.
    */
-  public MultiDomainServerState getNewestECLCookie(Set<String> excludedBaseDNs)
+  public MultiDomainServerState getNewestECLCookie(Set<DN> excludedBaseDNs)
   {
     // Initialize start state for all running domains with empty state
     final MultiDomainServerState result = new MultiDomainServerState();
     for (ReplicationServerDomain rsDomain : getReplicationServerDomains())
     {
-      if (!contains(excludedBaseDNs, rsDomain.getBaseDN().toNormalizedString()))
+      if (!excludedBaseDNs.contains(rsDomain.getBaseDN()))
       {
         final ServerState latestDBServerState = rsDomain.getLatestServerState();
         if (!latestDBServerState.isEmpty())
@@ -1300,11 +1300,6 @@
     return result;
   }
 
-  private boolean contains(Set<String> col, String elem)
-  {
-    return col != null && col.contains(elem);
-  }
-
   /**
    * Gets the weight affected to the replication server.
    * <p>
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServerDomain.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
index 8da20ad..d2090ba 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/ReplicationServerDomain.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.server;
 
@@ -2324,7 +2324,7 @@
     attributes.add(Attributes.create("replication-server-port",
         String.valueOf(localReplicationServer.getReplicationPort())));
     attributes.add(Attributes.create("domain-name",
-        baseDN.toNormalizedString()));
+        baseDN.toString()));
     attributes.add(Attributes.create("generation-id",
         baseDN + " " + generationId));
 
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDB.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDB.java
index 69cd5ea..8c8a85d 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDB.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/file/FileReplicaDB.java
@@ -21,7 +21,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2014 ForgeRock AS
+ *      Copyright 2014-2015 ForgeRock AS
  */
 package org.opends.server.replication.server.changelog.file;
 
@@ -269,7 +269,7 @@
     {
       final List<Attribute> attributes = new ArrayList<Attribute>();
       create(attributes, "replicationServer-database",String.valueOf(serverId));
-      create(attributes, "domain-name", baseDN.toNormalizedString());
+      create(attributes, "domain-name", baseDN.toString());
       final CSNLimits limits = csnLimits;
       if (limits.oldestCSN != null)
       {
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/DraftCNDB.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/DraftCNDB.java
index 6274d2f..f56a4ad 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/DraftCNDB.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/DraftCNDB.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.server.changelog.je;
 
@@ -88,7 +88,7 @@
     {
       final long changeNumber = record.getChangeNumber();
       DatabaseEntry key = new ReplicationDraftCNKey(changeNumber);
-      DatabaseEntry data = new DraftCNData(changeNumber, record.getBaseDN().toNormalizedString(), record.getCSN());
+      DatabaseEntry data = new DraftCNData(changeNumber, record.getBaseDN().toString(), record.getCSN());
 
       // Use a transaction so that we can override durability.
       Transaction txn = null;
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/JEReplicaDB.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/JEReplicaDB.java
index 867e76a..f032d77 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/JEReplicaDB.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/JEReplicaDB.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.server.changelog.je;
 
@@ -293,7 +293,7 @@
     {
       final List<Attribute> attributes = new ArrayList<Attribute>();
       create(attributes, "replicationServer-database",String.valueOf(serverId));
-      create(attributes, "domain-name", baseDN.toNormalizedString());
+      create(attributes, "domain-name", baseDN.toString());
       final CSNLimits limits = csnLimits;
       if (limits.oldestCSN != null)
       {
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/ReplicationDbEnv.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/ReplicationDbEnv.java
index 78a0883..93b9341 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/ReplicationDbEnv.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/server/changelog/je/ReplicationDbEnv.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.server.changelog.je;
 
@@ -37,8 +37,6 @@
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
-import org.forgerock.opendj.ldap.ByteString;
-import org.forgerock.opendj.ldap.ByteStringBuilder;
 import org.opends.server.replication.common.CSN;
 import org.opends.server.replication.server.ChangelogState;
 import org.opends.server.replication.server.ReplicationServer;
@@ -293,10 +291,10 @@
         }
         else if (prefix.equals(OFFLINE_TAG))
         {
-          final String[] str = stringKey.split(FIELD_SEPARATOR, 3);
+          final String[] str = stringData.split(FIELD_SEPARATOR, 3);
+          long timestamp = toLong(str[0]);
           final int serverId = toInt(str[1]);
           final DN baseDN = DN.valueOf(str[2]);
-          long timestamp = ByteString.wrap(entry.getValue()).asReader().getLong();
           if (logger.isTraceEnabled())
           {
             debug("has read replica offline: baseDN=" + baseDN + " serverId="
@@ -481,8 +479,9 @@
    */
   static Entry<String, String> toReplicaEntry(DN baseDN, int serverId)
   {
-    final String key = serverId + FIELD_SEPARATOR + baseDN.toNormalizedString();
-    return new SimpleImmutableEntry<String, String>(key, key);
+    final String key = serverId + FIELD_SEPARATOR + baseDN.toIrreversibleReadableString();
+    final String value = serverId + FIELD_SEPARATOR + baseDN.toString();
+    return new SimpleImmutableEntry<String, String>(key, value);
   }
 
   /**
@@ -497,11 +496,10 @@
    */
   static Entry<byte[], byte[]> toGenIdEntry(DN baseDN, long generationId)
   {
-    final String normDn = baseDN.toNormalizedString();
-    final String key = GENERATION_ID_TAG + FIELD_SEPARATOR + normDn;
+    final String key = GENERATION_ID_TAG + FIELD_SEPARATOR + baseDN.toIrreversibleReadableString();
     final String data = GENERATION_ID_TAG + FIELD_SEPARATOR + generationId
-        + FIELD_SEPARATOR + normDn;
-    return new SimpleImmutableEntry<byte[], byte[]>(toBytes(key),toBytes(data));
+        + FIELD_SEPARATOR + baseDN.toString();
+    return new SimpleImmutableEntry<byte[], byte[]>(toBytes(key), toBytes(data));
   }
 
   /**
@@ -513,9 +511,7 @@
    */
   static Entry<byte[], byte[]> toByteArray(Entry<String, String> entry)
   {
-    return new SimpleImmutableEntry<byte[], byte[]>(
-        toBytes(entry.getKey()),
-        toBytes(entry.getValue()));
+    return new SimpleImmutableEntry<byte[], byte[]>(toBytes(entry.getKey()), toBytes(entry.getValue()));
   }
 
   /**
@@ -530,10 +526,11 @@
    */
   static Entry<byte[], byte[]> toReplicaOfflineEntry(DN baseDN, CSN offlineCSN)
   {
-    final byte[] key = toReplicaOfflineKey(baseDN, offlineCSN.getServerId());
-    final ByteStringBuilder data = new ByteStringBuilder(8); // store a long
-    data.append(offlineCSN.getTime());
-    return new SimpleImmutableEntry<byte[], byte[]>(key, data.toByteArray());
+    final int serverId = offlineCSN.getServerId();
+    final byte[] key = toReplicaOfflineKey(baseDN, serverId);
+    final byte[] data = toBytes(String.valueOf(offlineCSN.getTime()) + FIELD_SEPARATOR + serverId
+        + FIELD_SEPARATOR + baseDN.toString());
+    return new SimpleImmutableEntry<byte[], byte[]>(key, data);
   }
 
   /**
@@ -547,7 +544,7 @@
    */
   private static byte[] toReplicaOfflineKey(DN baseDN, int serverId)
   {
-    return toBytes(OFFLINE_TAG + FIELD_SEPARATOR + serverId + FIELD_SEPARATOR + baseDN.toNormalizedString());
+    return toBytes(OFFLINE_TAG + FIELD_SEPARATOR + serverId + FIELD_SEPARATOR + baseDN.toIrreversibleReadableString());
   }
 
   /** Returns an entry with the provided key and a null value. */
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationBroker.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationBroker.java
index 6f289a5..1cf0d71 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationBroker.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationBroker.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.service;
 
@@ -2117,7 +2117,7 @@
     if (heartbeatInterval > 0)
     {
       heartbeatMonitor = new HeartbeatMonitor(getServerId(), rs.getServerId(),
-          getBaseDN().toNormalizedString(), rs.session, heartbeatInterval);
+          getBaseDN().toString(), rs.session, heartbeatInterval);
       heartbeatMonitor.start();
     }
   }
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationDomain.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationDomain.java
index 6ecb922..92f808d 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationDomain.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/replication/service/ReplicationDomain.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.replication.service;
 
@@ -531,7 +531,7 @@
    */
   public String getBaseDNString()
   {
-    return getBaseDN().toNormalizedString();
+    return getBaseDN().toString();
   }
 
   /**
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
index c6a6196..924d330 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2012-2014 ForgeRock AS
+ *      Portions Copyright 2012-2015 ForgeRock AS
  */
 package org.opends.server.schema;
 
@@ -158,7 +158,7 @@
       {
         // This locale is not supported by JVM.
         logger.error(WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE,
-                collation, configuration.dn().toNormalizedString(), languageTag);
+                collation, configuration.dn().toString(), languageTag);
       }
     }
 
@@ -284,7 +284,7 @@
       {
         LocalizableMessage msg =
             WARN_ATTR_INVALID_COLLATION_MATCHING_RULE_LOCALE.get(
-                collation, configuration.dn().toNormalizedString(),
+                collation, configuration.dn().toString(),
                 languageTag);
         unacceptableReasons.add(msg);
         configAcceptable = false;
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tasks/ImportTask.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tasks/ImportTask.java
index 856c2b0..85fc865 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tasks/ImportTask.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tasks/ImportTask.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2013-2014 ForgeRock AS
+ *      Portions Copyright 2013-2015 ForgeRock AS
  */
 package org.opends.server.tasks;
 
@@ -346,7 +346,7 @@
         StringBuilder builder = new StringBuilder();
         for(DN dn : backend.getBaseDNs())
         {
-          builder.append(dn.toNormalizedString());
+          builder.append(dn.toString());
           builder.append(" ");
         }
         LocalizableMessage message = ERR_LDIFIMPORT_MISSING_CLEAR_BACKEND.get(
@@ -370,14 +370,14 @@
           {
             // The include branches span across multiple backends.
             LocalizableMessage message = ERR_LDIFIMPORT_INVALID_INCLUDE_BASE.get(
-                includeBranch.toNormalizedString(), backend.getBackendID());
+                includeBranch.toString(), backend.getBackendID());
             throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
           }
         }
         else
         {
           // The include branch is not associated with any backend.
-          LocalizableMessage message = ERR_NO_BACKENDS_FOR_BASE.get(includeBranch.toNormalizedString());
+          LocalizableMessage message = ERR_NO_BACKENDS_FOR_BASE.get(includeBranch.toString());
           throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
         }
       }
@@ -395,7 +395,7 @@
       if (!Backend.handlesEntry(includeBranch, defaultIncludeBranches, excludeBranches))
       {
         LocalizableMessage message = ERR_LDIFIMPORT_INVALID_INCLUDE_BASE.get(
-            includeBranch.toNormalizedString(), backend.getBackendID());
+            includeBranch.toString(), backend.getBackendID());
         throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
       }
     }
@@ -539,11 +539,11 @@
           backend.getBaseDNs().length > 1 && !clearBackend)
       {
         StringBuilder builder = new StringBuilder();
-        builder.append(backend.getBaseDNs()[0].toNormalizedString());
+        builder.append(backend.getBaseDNs()[0].toString());
         for(int i = 1; i < backend.getBaseDNs().length; i++)
         {
           builder.append(" / ");
-          builder.append(backend.getBaseDNs()[i].toNormalizedString());
+          builder.append(backend.getBaseDNs()[i].toString());
         }
         logger.error(ERR_LDIFIMPORT_MISSING_CLEAR_BACKEND, builder, ATTR_IMPORT_CLEAR_BACKEND);
         return TaskState.STOPPED_BY_ERROR;
@@ -564,8 +564,7 @@
           else if(backend != locatedBackend)
           {
             // The include branches span across multiple backends.
-            logger.error(ERR_LDIFIMPORT_INVALID_INCLUDE_BASE, includeBranch
-                .toNormalizedString(), backend.getBackendID());
+            logger.error(ERR_LDIFIMPORT_INVALID_INCLUDE_BASE, includeBranch.toString(), backend.getBackendID());
             return TaskState.STOPPED_BY_ERROR;
           }
         }
@@ -640,7 +639,7 @@
         if (! Backend.handlesEntry(includeBranch, defaultIncludeBranches,
                                    excludeBranches))
         {
-          logger.error(ERR_LDIFIMPORT_INVALID_INCLUDE_BASE, includeBranch.toNormalizedString(), backend.getBackendID());
+          logger.error(ERR_LDIFIMPORT_INVALID_INCLUDE_BASE, includeBranch.toString(), backend.getBackendID());
           return TaskState.STOPPED_BY_ERROR;
         }
       }
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tools/DBTest.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tools/DBTest.java
index bda0581..a2cd58c 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tools/DBTest.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tools/DBTest.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2013-2014 ForgeRock AS
+ *      Portions Copyright 2013-2015 ForgeRock AS
  */
 package org.opends.server.tools;
 
@@ -74,7 +74,6 @@
 import static com.forgerock.opendj.cli.Utils.*;
 
 import static org.opends.messages.ToolMessages.*;
-import static org.opends.server.backends.jeb.JebFormat.*;
 import static org.opends.server.util.StaticUtils.*;
 
 /**
@@ -653,7 +652,7 @@
       for(EntryContainer ec : rc.getEntryContainers())
       {
         builder.startRow();
-        builder.appendCell(ec.getBaseDN().toNormalizedString());
+        builder.appendCell(ec.getBaseDN().toString());
         builder.appendCell(ec.getDatabasePrefix());
         builder.appendCell(ec.getEntryCount());
         count++;
@@ -750,7 +749,7 @@
         if(ec == null)
         {
           printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get(
-              base.toNormalizedString(), backend.getBackendID()));
+              base.toString(), backend.getBackendID()));
           return 1;
         }
 
@@ -761,7 +760,7 @@
         for(EntryContainer ec : rc.getEntryContainers())
         {
           builder.startRow();
-          builder.appendCell("Base DN: " + ec.getBaseDN().toNormalizedString());
+          builder.appendCell("Base DN: " + ec.getBaseDN().toString());
           count = appendDatabaseContainerRows(builder, ec, count);
         }
       }
@@ -922,7 +921,7 @@
       if(ec == null)
       {
         printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get(
-            base.toNormalizedString(), backend.getBackendID()));
+            base.toString(), backend.getBackendID()));
         return 1;
       }
 
@@ -1109,7 +1108,7 @@
       if(ec == null)
       {
         printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get(
-            base.toNormalizedString(), backend.getBackendID()));
+            base.toString(), backend.getBackendID()));
         return 1;
       }
 
@@ -1130,7 +1129,7 @@
       if(databaseContainer == null)
       {
         printMessage(ERR_DBTEST_NO_DATABASE_CONTAINERS_FOR_NAME.get(
-            databaseName.getValue(), base.toNormalizedString(),
+            databaseName.getValue(), base.toString(),
             backend.getBackendID()));
         return 1;
       }
@@ -1253,7 +1252,7 @@
               {
                 try
                 {
-                  formatedKey = dnFromDNKey(key.getData(), ec.getBaseDN()).toNormalizedString();
+                  formatedKey = ByteString.valueOf(key.getData()).toHexString() + ec.getBaseDN();
                   keyLabel = INFO_LABEL_DBTEST_ENTRY_DN.get();
                 }
                 catch(Exception e)
@@ -1489,8 +1488,7 @@
         || databaseContainer instanceof DN2URI)
     {
       // Encode the value as a DN
-      return StaticUtils.getBytes(
-          DN.valueOf(value).toNormalizedString());
+      return DN.valueOf(value).toIrreversibleNormalizedByteString().toByteArray();
     }
     else if(databaseContainer instanceof ID2Entry)
     {
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tools/ImportLDIF.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tools/ImportLDIF.java
index 5e9a06f..c5fdb18 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tools/ImportLDIF.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/tools/ImportLDIF.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS.
+ *      Portions Copyright 2011-2015 ForgeRock AS.
  */
 package org.opends.server.tools;
 
@@ -1258,11 +1258,11 @@
         !clearBackend.isPresent())
     {
       StringBuilder builder = new StringBuilder();
-      builder.append(backend.getBaseDNs()[0].toNormalizedString());
+      builder.append(backend.getBaseDNs()[0].toString());
       for(int i = 1; i < backend.getBaseDNs().length; i++)
       {
         builder.append(" / ");
-        builder.append(backend.getBaseDNs()[i].toNormalizedString());
+        builder.append(backend.getBaseDNs()[i].toString());
       }
       LocalizableMessage message = ERR_LDIFIMPORT_MISSING_CLEAR_BACKEND.get(
               builder, clearBackend.getLongIdentifier());
@@ -1306,7 +1306,7 @@
         if (! Backend.handlesEntry(includeBranch, defaultIncludeBranches,
                                    excludeBranches))
         {
-          logger.error(ERR_LDIFIMPORT_INVALID_INCLUDE_BASE, includeBranch.toNormalizedString(), backendID.getValue());
+          logger.error(ERR_LDIFIMPORT_INVALID_INCLUDE_BASE, includeBranch.toString(), backendID.getValue());
           return 1;
         }
       }
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/DN.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/DN.java
index dd2f9bb..269e2a1 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/DN.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/DN.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2012-2014 ForgeRock AS
+ *      Portions Copyright 2012-2015 ForgeRock AS
  */
 package org.opends.server.types;
 
@@ -37,6 +37,7 @@
 import org.forgerock.opendj.ldap.ByteStringBuilder;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchScope;
+import org.forgerock.util.Reject;
 import org.opends.server.core.DirectoryServer;
 
 import static org.forgerock.util.Reject.*;
@@ -70,7 +71,14 @@
    */
   public static final DN NULL_DN = new DN();
 
+  /** RDN separator for normalized byte string of a DN. */
+  public static final byte NORMALIZED_RDN_SEPARATOR = 0x00;
 
+  /** AVA separator for normalized byte string of a DN. */
+  public static final byte NORMALIZED_AVA_SEPARATOR = 0x01;
+
+  /** Escape byte for normalized byte string of a DN. */
+  public static final byte NORMALIZED_ESC_BYTE = 0x02;
 
   /**
    * The serial version identifier required to satisfy the compiler
@@ -95,8 +103,8 @@
   /** The string representation of this DN. */
   private String dnString;
 
-  /** The normalized string representation of this DN. */
-  private String normalizedDN;
+  /** The irreversible normalized byte string representation of this DN. */
+  private ByteString normalizedDN;
 
 
 
@@ -251,6 +259,38 @@
     }
   }
 
+  /**
+   * Returns a copy of this DN whose parent DN, {@code fromDN}, has been renamed
+   * to the new parent DN, {@code toDN}. If this DN is not subordinate or equal
+   * to {@code fromDN} then this DN is returned (i.e. the DN is not renamed).
+   *
+   * @param fromDN
+   *          The old parent DN.
+   * @param toDN
+   *          The new parent DN.
+   * @return The renamed DN, or this DN if no renaming was performed.
+   */
+  public DN rename(final DN fromDN, final DN toDN)
+  {
+    Reject.ifNull(fromDN, toDN);
+
+    if (!isDescendantOf(fromDN))
+    {
+      return this;
+    }
+    else if (equals(fromDN))
+    {
+      return toDN;
+    }
+    else
+    {
+      final int sizeOfRdns = size() - fromDN.size();
+      RDN[] childRdns = new RDN[sizeOfRdns];
+      System.arraycopy(rdnComponents, 0, childRdns, 0, sizeOfRdns);
+      return toDN.concat(childRdns);
+    }
+  }
+
 
 
   /**
@@ -587,6 +627,7 @@
       while (dnReader.remaining() > 0 && (b = dnReader.get()) == ' ')
       {}
 
+
       if(b == ' ')
       {
         // This means that we hit the end of the value before
@@ -2652,13 +2693,10 @@
    * Retrieves a normalized representation of the DN with the provided
    * components.
    *
-   * @param  rdnComponents  The RDN components for which to obtain the
-   *                        normalized string representation.
-   *
    * @return  The normalized string representation of the provided RDN
    *          components.
    */
-  private static String normalize(RDN[] rdnComponents)
+  public String toIrreversibleReadableString()
   {
     if (rdnComponents.length == 0)
     {
@@ -2666,68 +2704,52 @@
     }
 
     StringBuilder buffer = new StringBuilder();
-    rdnComponents[0].toNormalizedString(buffer);
+    rdnComponents[0].toNormalizedReadableString(buffer);
 
     for (int i=1; i < rdnComponents.length; i++)
     {
       buffer.append(',');
-      rdnComponents[i].toNormalizedString(buffer);
+      rdnComponents[i].toNormalizedReadableString(buffer);
     }
 
     return buffer.toString();
   }
 
-
-
   /**
-   * Retrieves a normalized string representation of this DN. This method should
-   * be used over {@link #toString()} when the resulting String is to be used as
-   * a key in a Map.
+   * Returns the irreversible normalized byte string representation of a DN,
+   * suitable for equality and comparisons, and providing a natural hierarchical
+   * ordering, but not usable as a valid DN nor reversible to a valid DN.
    * <p>
-   * Normalization involves:
-   * <ol>
-   * <li>sorting AVAs (e.g. "sn=swift+cn=matt" is greater than
-   * "cn=matt+sn=swift")</li>
-   * <li>normalizing attribute names (e.g. "commonName" is converted to "cn")
-   * </li>
-   * <li>normalizing attribute values (e.g. converting to lowercase)</li>
-   * </ol>
-   * Where AVA stands for "Attribute Value Assertion".
-   * <p>
-   * Remember that:
-   * <ul>
-   * <li>a DN is made of one or several RDNs</li>
-   * <li>an RDN is made of one or several AVA</li>
-   * <li>an AVA is a attribute type and an attribute value</li>
-   * </ul>
+   * This representation should be used only when a byte string representation
+   * is needed and when no reversibility to a valid DN is needed. Always consider
+   * using a {@code CompactDn} as an alternative.
    *
-   * @return A normalized string representation of this DN.
+   * @return The normalized byte string representation of the provided DN, not
+   *         usable as a valid DN
    */
-  public String toNormalizedString()
+  public ByteString toIrreversibleNormalizedByteString()
   {
-    if(normalizedDN == null)
+    if (normalizedDN == null)
     {
-      normalizedDN = normalize(this.rdnComponents);
+      if (numComponents == 0)
+      {
+        normalizedDN = ByteString.empty();
+      }
+      else
+      {
+        final ByteStringBuilder builder = new ByteStringBuilder();
+        rdnComponents[numComponents - 1].toNormalizedByteString(builder);
+        for (int i = numComponents - 2; i >= 0; i--)
+        {
+          builder.append(NORMALIZED_RDN_SEPARATOR);
+          rdnComponents[i].toNormalizedByteString(builder);
+        }
+        normalizedDN = builder.toByteString();
+      }
     }
     return normalizedDN;
   }
 
-
-
-  /**
-   * Appends a normalized string representation of this DN to the
-   * provided buffer.
-   *
-   * @param  buffer  The buffer to which the information should be
-   *                 appended.
-   */
-  public void toNormalizedString(StringBuilder buffer)
-  {
-    buffer.append(toNormalizedString());
-  }
-
-
-
   /**
    * Compares this DN with the provided DN based on a natural order. This order
    * will be first hierarchical (ancestors will come before descendants) and
diff --git a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/RDN.java b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/RDN.java
index e27e958..ad3d5ae 100644
--- a/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/RDN.java
+++ b/opendj-sdk/opendj3-server-dev/src/server/org/opends/server/types/RDN.java
@@ -22,10 +22,16 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2013-2014 ForgeRock AS
+ *      Portions Copyright 2013-2015 ForgeRock AS
  */
 package org.opends.server.types;
 
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
 import java.util.*;
 
 import org.forgerock.i18n.LocalizableMessage;
@@ -55,6 +61,8 @@
 {
   private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
+  private static final char HEX_STRING_SEPARATOR = '%';
+
   /** The set of attribute types for the elements in this RDN. */
   private AttributeType[] attributeTypes;
 
@@ -1002,7 +1010,7 @@
   {
     if (normalizedRDN == null)
     {
-      toNormalizedString(new StringBuilder());
+      toNormalizedReadableString(new StringBuilder());
     }
     return normalizedRDN;
   }
@@ -1015,7 +1023,7 @@
    *
    * @param  buffer  The buffer to which to append the information.
    */
-  public void toNormalizedString(StringBuilder buffer)
+  void toNormalizedReadableString(StringBuilder buffer)
   {
     if (normalizedRDN != null)
     {
@@ -1023,25 +1031,24 @@
       return;
     }
 
-    boolean bufferEmpty = (buffer.length() == 0);
+    boolean providedBufferIsEmpty = (buffer.length() == 0);
 
     if (attributeNames.length == 1)
     {
-      getNormalizedAVAString(0, buffer);
+      normalizeAVAToReadableString(0, buffer);
     }
     else
     {
-      // normalization sorts RDNs alphabetically
-      SortedSet<String> rdnElementStrings = new TreeSet<String>();
-
+      // Normalization sorts RDNs alphabetically
+      SortedSet<String> avaStrings = new TreeSet<String>();
       for (int i=0; i < attributeNames.length; i++)
       {
-        StringBuilder b2 = new StringBuilder();
-        getNormalizedAVAString(i, b2);
-        rdnElementStrings.add(b2.toString());
+        StringBuilder builder = new StringBuilder();
+        normalizeAVAToReadableString(i, builder);
+        avaStrings.add(builder.toString());
       }
 
-      Iterator<String> iterator = rdnElementStrings.iterator();
+      Iterator<String> iterator = avaStrings.iterator();
       buffer.append(iterator.next());
       while (iterator.hasNext())
       {
@@ -1050,40 +1057,167 @@
       }
     }
 
-    if (bufferEmpty)
+    if (providedBufferIsEmpty)
     {
       normalizedRDN = buffer.toString();
     }
   }
 
+  /**
+   * Adds a normalized byte string representation of this RDN to the provided builder.
+   *
+   * @param builder
+   *           Builder to add this representation to.
+   * @return the builder
+   */
+  public ByteStringBuilder toNormalizedByteString(ByteStringBuilder builder) {
+    if (attributeNames.length == 1)
+    {
+      normalizeAVAToByteString(0, builder);
+    }
+    else
+    {
+      // Normalization sorts RDNs
+      SortedSet<ByteString> avaStrings = new TreeSet<ByteString>();
+      for (int i = 0; i < attributeNames.length; i++)
+      {
+        ByteStringBuilder b = new ByteStringBuilder();
+        normalizeAVAToByteString(i, b);
+        avaStrings.add(b.toByteString());
+      }
+
+      Iterator<ByteString> iterator = avaStrings.iterator();
+      builder.append(iterator.next());
+      while (iterator.hasNext())
+      {
+        builder.append(DN.NORMALIZED_AVA_SEPARATOR);
+        builder.append(iterator.next());
+      }
+    }
+    return builder;
+  }
+
+  /**
+   * Adds a normalized byte string representation of the AVA corresponding to provided position
+   * in this RDN to the provided builder.
+   *
+   * @param position
+   *           Position of AVA in this RDN
+   * @param builder
+   *           Builder to add the representation to.
+   * @return the builder
+   */
+  ByteStringBuilder normalizeAVAToByteString(int position, final ByteStringBuilder builder)
+  {
+    builder.append(attributeTypes[position].getNormalizedPrimaryNameOrOID());
+    builder.append("=");
+    final ByteString value = getEqualityNormalizedValue(position);
+    if (value.length() > 0)
+    {
+      builder.append(escapeBytes(value));
+    }
+    return builder;
+  }
+
+  /**
+   * Return a new byte string with bytes 0x00, 0x01 and 0x02 escaped.
+   * <p>
+   * These bytes are reserved to represent respectively the RDN separator, the
+   * AVA separator and the escape byte in a normalized byte string.
+   */
+  private ByteString escapeBytes(final ByteString value)
+  {
+    if (!needEscaping(value))
+    {
+      return value;
+    }
+
+    final ByteStringBuilder builder = new ByteStringBuilder();
+    for (int i = 0; i < value.length(); i++)
+    {
+      final byte b = value.byteAt(i);
+      if (isByteToEscape(b))
+      {
+        builder.append(DN.NORMALIZED_ESC_BYTE);
+      }
+      builder.append(b);
+    }
+    return builder.toByteString();
+  }
+
+  private boolean needEscaping(final ByteString value)
+  {
+    boolean needEscaping = false;
+    for (int i = 0; i < value.length(); i++)
+    {
+      final byte b = value.byteAt(i);
+      if (isByteToEscape(b))
+      {
+        needEscaping = true;
+        break;
+      }
+    }
+    return needEscaping;
+  }
+
+  private boolean isByteToEscape(final byte b)
+  {
+    return b == DN.NORMALIZED_RDN_SEPARATOR || b == DN.NORMALIZED_AVA_SEPARATOR || b == DN.NORMALIZED_ESC_BYTE;
+  }
 
 
   /**
    * Appends a normalized string representation of this RDN to the
    * provided buffer.
    *
-   * @param  pos  The position of the attribute type and value to
+   * @param  position  The position of the attribute type and value to
    *              retrieve.
-   * @param  buffer  The buffer to which to append the information.
+   * @param  builder  The buffer to which to append the information.
+   * @return the builder
    */
-  public void getNormalizedAVAString(int pos, StringBuilder buffer)
+  private StringBuilder normalizeAVAToReadableString(int position, StringBuilder builder)
   {
-      AttributeType type = attributeTypes[pos];
-      buffer.append(type.getNormalizedPrimaryNameOrOID());
-      buffer.append('=');
+      builder.append(attributeTypes[position].getNormalizedPrimaryNameOrOID());
+      builder.append('=');
 
-      ByteString value = attributeValues[pos];
-      try
+      ByteString value = getEqualityNormalizedValue(position);
+      if (value.length() == 0)
       {
-        MatchingRule rule = type.getEqualityMatchingRule();
-        ByteString normValue = rule.normalizeAttributeValue(value);
-        buffer.append(getDNValue(normValue));
+        return builder;
       }
-      catch (Exception e)
+      final boolean hasAttributeName = attributeTypes[position].getPrimaryName() != null;
+      final boolean isHumanReadable = attributeTypes[position].getSyntax().isHumanReadable();
+      if (!hasAttributeName || !isHumanReadable)
       {
-        logger.traceException(e);
-        buffer.append(getDNValue(value));
+        builder.append(value.toPercentHexString());
       }
+      else
+      {
+        // try to decode value as UTF-8 string
+        final CharBuffer buffer = CharBuffer.allocate(value.length());
+        final CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder()
+            .onMalformedInput(CodingErrorAction.REPORT)
+            .onUnmappableCharacter(CodingErrorAction.REPORT);
+        if (value.copyTo(buffer, decoder))
+        {
+          try
+          {
+            // URL encoding encodes space char as '+' instead of using hex code
+            final String val = URLEncoder.encode(buffer.toString(), "UTF-8").replaceAll("\\+", "%20");
+            builder.append(val);
+          }
+          catch (UnsupportedEncodingException e)
+          {
+            // should never happen
+            builder.append(value.toPercentHexString());
+          }
+        }
+        else
+        {
+          builder.append(value.toPercentHexString());
+        }
+      }
+      return builder;
   }
 
   /**
diff --git a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestImportJob.java b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestImportJob.java
index 4b6fcc3..5e6bb3f 100644
--- a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestImportJob.java
+++ b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestImportJob.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.backends.jeb;
 
@@ -245,6 +245,7 @@
 
     tempDir = TestCaseUtils.createTemporaryDirectory("jebimporttest");
     homeDirName = tempDir.getAbsolutePath();
+    System.out.println(homeDirName.toString());
 
     EnvManager.createHomeDir(homeDirName);
 
@@ -291,7 +292,7 @@
   public void cleanUp() throws Exception
   {
     TestCaseUtils.disableBackend(beID);
-    TestCaseUtils.deleteDirectory(tempDir);
+    //TestCaseUtils.deleteDirectory(tempDir);
   }
 
 
diff --git a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestJebFormat.java b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestJebFormat.java
index 57a6443..eb83942 100644
--- a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestJebFormat.java
+++ b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestJebFormat.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2014 ForgeRock AS
+ *      Portions Copyright 2014-2015 ForgeRock AS
  */
 package org.opends.server.backends.jeb;
 
@@ -41,6 +41,8 @@
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
+import static org.assertj.core.api.Assertions.*;
+import static org.opends.server.backends.jeb.JebFormat.*;
 import static org.opends.server.util.StaticUtils.*;
 import static org.testng.Assert.*;
 
@@ -429,8 +431,7 @@
    */
   @Test()
   public void testEntryToAndFromDatabase() throws Exception {
-    // Make sure that the server is up and running.
-    TestCaseUtils.startServer();
+    ensureTheServerIsUpAndRunning();
 
     // Convert the test LDIF string to a byte array
     byte[] originalLDIFBytes = StaticUtils.getBytes(ldifString);
@@ -501,8 +502,7 @@
    */
   @Test()
   public void testEntryToAndFromDatabaseV1() throws Exception {
-    // Make sure that the server is up and running.
-    TestCaseUtils.startServer();
+    ensureTheServerIsUpAndRunning();
 
     // Convert the test LDIF string to a byte array
     byte[] originalLDIFBytes = StaticUtils.getBytes(ldifString);
@@ -551,8 +551,7 @@
   @Test(dataProvider = "encodeConfigs")
   public void testEntryToAndFromDatabaseV2(EntryEncodeConfig config)
          throws Exception {
-    // Make sure that the server is up and running.
-    TestCaseUtils.startServer();
+    ensureTheServerIsUpAndRunning();
 
     // Convert the test LDIF string to a byte array
     byte[] originalLDIFBytes = StaticUtils.getBytes(ldifString);
@@ -583,8 +582,7 @@
   @Test(dataProvider = "encodeConfigs")
   public void testEntryToAndFromDatabaseV3(EntryEncodeConfig config)
          throws Exception {
-    // Make sure that the server is up and running.
-    TestCaseUtils.startServer();
+    ensureTheServerIsUpAndRunning();
 
     // Convert the test LDIF string to a byte array
     byte[] originalLDIFBytes = StaticUtils.getBytes(ldifString);
@@ -605,4 +603,30 @@
     }
     reader.close();
   }
+
+  @DataProvider
+  private Object[][] findDnKeyParentData()
+  {
+    return new Object[][]
+    {
+      // dn, expected length of parent
+      { "dc=example", 0 },
+      { "dc=example,dc=com", 7 },
+      { "dc=example,dc=com\\,org", 11 },
+
+    };
+  }
+
+  @Test(dataProvider="findDnKeyParentData")
+  public void testFindDnKeyParent(String dn, int expectedLength) throws Exception
+  {
+    ensureTheServerIsUpAndRunning();
+    byte[] dnKey = dnToDNKey(DN.valueOf(dn), 0);
+    assertThat(findDNKeyParent(dnKey)).isEqualTo(expectedLength);
+  }
+
+  private void ensureTheServerIsUpAndRunning() throws Exception
+  {
+    TestCaseUtils.startServer();
+  }
 }
diff --git a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java
index 4544b25..c3aecfa 100644
--- a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java
+++ b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/BackendConfigManagerTestCase.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
- *      Portions Copyright 2014 ForgeRock AS
+ *      Portions Copyright 2014-2015 ForgeRock AS
  */
 package org.opends.server.core;
 
@@ -587,7 +587,7 @@
         buffer.append("___");
       }
 
-      String ndn = dn.toNormalizedString();
+      String ndn = dn.toIrreversibleReadableString();
       for (int i=0; i < ndn.length(); i++)
       {
         char c = ndn.charAt(i);
diff --git a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java
index 56f3bd6..60082e9 100644
--- a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java
+++ b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 
 package org.opends.server.core;
@@ -403,7 +403,7 @@
   {
     InternalClientConnection conn = getRootConnection();
     List<RawModification> mods = newRawModifications(DELETE, attrType);
-    ModifyOperation modifyOperation = conn.processModify(ByteString.valueOf(e.getName().toNormalizedString()), mods);
+    ModifyOperation modifyOperation = conn.processModify(ByteString.valueOf(e.getName().toString()), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
   }
 
@@ -411,7 +411,7 @@
   {
     InternalClientConnection conn = getRootConnection();
     List<RawModification> mods = newRawModifications(REPLACE, attrType, newValue);
-    ModifyOperation modifyOperation = conn.processModify(ByteString.valueOf(e.getName().toNormalizedString()), mods);
+    ModifyOperation modifyOperation = conn.processModify(ByteString.valueOf(e.getName().toString()), mods);
     assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
   }
 
diff --git a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java
index 017486e..4a609f6 100644
--- a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java
+++ b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.extensions;
 
@@ -133,7 +133,7 @@
     {
       assertTrue(!a.isEmpty());
       assertEquals(a.size(), 1);
-      assertTrue(a.contains(ByteString.valueOf(entryDN.toNormalizedString())));
+      assertTrue(a.contains(ByteString.valueOf(entryDN.toString())));
     }
   }
 
diff --git a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProviderTestCase.java b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProviderTestCase.java
index b7ba16e..4361efa 100644
--- a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProviderTestCase.java
+++ b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryUUIDVirtualAttributeProviderTestCase.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.opends.server.extensions;
 
@@ -97,7 +97,7 @@
       new Object[] { DN.valueOf("cn=schema") },
       new Object[] { DN.valueOf("cn=tasks") },
       new Object[] { DN.valueOf("cn=monitor") },
-      new Object[] { DN.valueOf("cn=backups") }
+      new Object[] { DN.valueOf("cn=backups") },
     };
   }
 
@@ -116,8 +116,7 @@
   public void testGetEntry(DN entryDN)
          throws Exception
   {
-    String uuidString = UUID.nameUUIDFromBytes(
-                             getBytes(entryDN.toNormalizedString())).toString();
+    String uuidString = UUID.nameUUIDFromBytes(entryDN.toIrreversibleNormalizedByteString().toByteArray()).toString();
 
     Entry e = DirectoryServer.getEntry(entryDN);
     assertNotNull(e);
@@ -294,8 +293,7 @@
   public void testSearchEntryUUIDAttrInMatchingFilter(DN entryDN)
          throws Exception
   {
-    String uuidString = UUID.nameUUIDFromBytes(
-                             getBytes(entryDN.toNormalizedString())).toString();
+    String uuidString = UUID.nameUUIDFromBytes(entryDN.toIrreversibleNormalizedByteString().toByteArray()).toString();
 
     final SearchRequest request = newSearchRequest(entryDN, SearchScope.BASE_OBJECT, "(entryUUID=" + uuidString + ")")
         .addAttribute("entryuuid");
diff --git a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestBindResponseProtocolOp.java b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestBindResponseProtocolOp.java
index 3cde311..c2a7646 100644
--- a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestBindResponseProtocolOp.java
+++ b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestBindResponseProtocolOp.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2014 ForgeRock AS
+ *      Portions Copyright 2014-2015 ForgeRock AS
  */
 package org.opends.server.protocols.ldap;
 
@@ -118,7 +118,8 @@
       assertTrue(protocolOp instanceof BindResponseProtocolOp);
       BindResponseProtocolOp bindResponse = (BindResponseProtocolOp)protocolOp;
       assertTrue(bindResponse.getResultCode() == okCode.intValue());
-      assertTrue(bindResponse.getMatchedDN().toNormalizedString().equals(responseDn.toNormalizedString()));
+      assertTrue(bindResponse.getMatchedDN().toIrreversibleNormalizedByteString()
+          .equals(responseDn.toIrreversibleNormalizedByteString()));
       assertTrue(bindResponse.getErrorMessage().toString().equals(message.toString()));
       assertNull(bindResponse.getReferralURLs());
       assertNull(bindResponse.getServerSASLCredentials());
@@ -165,7 +166,8 @@
       assertTrue(protocolOp instanceof BindResponseProtocolOp);
       BindResponseProtocolOp bindResponse = (BindResponseProtocolOp)protocolOp;
       assertTrue(bindResponse.getResultCode() == okCode.intValue());
-      assertTrue(bindResponse.getMatchedDN().toNormalizedString().equals(responseDn.toNormalizedString()));
+      assertTrue(bindResponse.getMatchedDN().toIrreversibleNormalizedByteString().equals(
+          responseDn.toIrreversibleNormalizedByteString()));
       assertTrue(bindResponse.getErrorMessage().toString().equals(message.toString()));
       assertNull(bindResponse.getReferralURLs());
       assertNull(bindResponse.getServerSASLCredentials());
diff --git a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java
index 13f0088..4d26176 100644
--- a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java
+++ b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java
@@ -22,18 +22,22 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2012-2014 ForgeRock AS
+ *      Portions Copyright 2012-2015 ForgeRock AS
  */
 package org.opends.server.types;
 
 import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.ByteStringBuilder;
+
 import java.util.ArrayList;
 
+import static org.assertj.core.api.Assertions.*;
 import static org.testng.Assert.*;
 
 import org.opends.server.TestCaseUtils;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.util.Platform;
+import org.testng.Assert;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 import org.testng.annotations.BeforeClass;
@@ -53,69 +57,70 @@
   @DataProvider(name = "testDNs")
   public Object[][] createData() {
     return new Object[][] {
-        { "", "", "" },
-        { "   ", "", "" },
-        { "cn=", "cn=", "cn=" },
-        { "cn= ", "cn=", "cn=" },
-        { "cn =", "cn=", "cn=" },
-        { "cn = ", "cn=", "cn=" },
-        { "dc=com", "dc=com", "dc=com" },
-        { "dc=com+o=com", "dc=com+o=com", "dc=com+o=com" },
-        { "DC=COM", "dc=com", "DC=COM" },
-        { "dc = com", "dc=com", "dc=com" },
-        { " dc = com ", "dc=com", "dc=com" },
-        { "dc=example,dc=com", "dc=example,dc=com",
-            "dc=example,dc=com" },
-        { "dc=example, dc=com", "dc=example,dc=com",
-            "dc=example,dc=com" },
-        { "dc=example ,dc=com", "dc=example,dc=com",
-            "dc=example,dc=com" },
-        { "dc =example , dc  =   com", "dc=example,dc=com",
-            "dc=example,dc=com" },
-        { "givenName=John+cn=Doe,ou=People,dc=example,dc=com",
-            "cn=doe+givenname=john,ou=people,dc=example,dc=com",
-            "givenName=John+cn=Doe,ou=People,dc=example,dc=com" },
-        { "givenName=John\\+cn=Doe,ou=People,dc=example,dc=com",
-            "givenname=john\\+cn=doe,ou=people,dc=example,dc=com",
-            "givenName=John\\+cn=Doe,ou=People,dc=example,dc=com" },
-        { "cn=Doe\\, John,ou=People,dc=example,dc=com",
-            "cn=doe\\, john,ou=people,dc=example,dc=com",
-            "cn=Doe\\, John,ou=People,dc=example,dc=com" },
-        { "UID=jsmith,DC=example,DC=net",
-            "uid=jsmith,dc=example,dc=net",
-            "UID=jsmith,DC=example,DC=net" },
-        { "OU=Sales+CN=J. Smith,DC=example,DC=net",
-            "cn=j. smith+ou=sales,dc=example,dc=net",
-            "OU=Sales+CN=J. Smith,DC=example,DC=net" },
-        { "CN=James \\\"Jim\\\" Smith\\, III,DC=example,DC=net",
-            "cn=james \\\"jim\\\" smith\\, iii,dc=example,dc=net",
-            "CN=James \\\"Jim\\\" Smith\\, III,DC=example,DC=net" },
-        { "CN=John Smith\\2C III,DC=example,DC=net",
-            "cn=john smith\\, iii,dc=example,dc=net",
-            "CN=John Smith\\, III,DC=example,DC=net" },
-        { "CN=\\23John Smith\\20,DC=example,DC=net",
-            "cn=\\#john smith,dc=example,dc=net",
-            "CN=\\#John Smith\\ ,DC=example,DC=net" },
-        { "CN=Before\\0dAfter,DC=example,DC=net",
-             //\0d is a hex representation of Carriage return. It is mapped
-             //to a SPACE as defined in the MAP ( RFC 4518)
-            "cn=before after,dc=example,dc=net",
-            "CN=Before\\0dAfter,DC=example,DC=net" },
-        { "1.3.6.1.4.1.1466.0=#04024869",
-             //Unicode codepoints from 0000-0008 are mapped to nothing.
-            "1.3.6.1.4.1.1466.0=hi",
-            "1.3.6.1.4.1.1466.0=\\04\\02Hi" },
-        { "1.1.1=", "1.1.1=", "1.1.1=" },
-        { "CN=Lu\\C4\\8Di\\C4\\87", "cn=lu\u010di\u0107",
-            "CN=Lu\u010di\u0107" },
-        { "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8,o=Airius",
-            "ou=\u55b6\u696d\u90e8,o=airius",
-            "ou=\u55b6\u696d\u90e8,o=Airius" },
-        { "photo=\\ john \\ ,dc=com", "photo=\\ john \\ ,dc=com",
+      // raw dn, irreversible normalized string representation, toString representation
+//        { "", "", "" },
+//        { "   ", "", "" },
+//        { "cn=", "cn=", "cn=" },
+//        { "cn= ", "cn=", "cn=" },
+//        { "cn =", "cn=", "cn=" },
+//        { "cn = ", "cn=", "cn=" },
+//        { "dc=com", "dc=com", "dc=com" },
+//        { "dc=com+o=com", "dc=com+o=com", "dc=com+o=com" },
+//        { "DC=COM", "dc=com", "DC=COM" },
+//        { "dc = com", "dc=com", "dc=com" },
+//        { " dc = com ", "dc=com", "dc=com" },
+//        { "dc=example,dc=com", "dc=example,dc=com",
+//            "dc=example,dc=com" },
+//        { "dc=example, dc=com", "dc=example,dc=com",
+//            "dc=example,dc=com" },
+//        { "dc=example ,dc=com", "dc=example,dc=com",
+//            "dc=example,dc=com" },
+//        { "dc =example , dc  =   com", "dc=example,dc=com",
+//            "dc=example,dc=com" },
+//        { "givenName=John+cn=Doe,ou=People,dc=example,dc=com",
+//            "cn=doe+givenname=john,ou=people,dc=example,dc=com",
+//            "givenName=John+cn=Doe,ou=People,dc=example,dc=com" },
+//        { "givenName=John\\+cn=Doe,ou=People,dc=example,dc=com",
+//            "givenname=john%2Bcn%3Ddoe,ou=people,dc=example,dc=com",
+//            "givenName=John\\+cn=Doe,ou=People,dc=example,dc=com" },
+//        { "cn=Doe\\, John,ou=People,dc=example,dc=com",
+//            "cn=doe%2C%20john,ou=people,dc=example,dc=com",
+//            "cn=Doe\\, John,ou=People,dc=example,dc=com" },
+//        { "UID=jsmith,DC=example,DC=net",
+//            "uid=jsmith,dc=example,dc=net",
+//            "UID=jsmith,DC=example,DC=net" },
+//        { "OU=Sales+CN=J. Smith,DC=example,DC=net",
+//            "cn=j.%20smith+ou=sales,dc=example,dc=net",
+//            "OU=Sales+CN=J. Smith,DC=example,DC=net" },
+//        { "CN=James \\\"Jim\\\" Smith\\, III,DC=example,DC=net",
+//            "cn=james%20%22jim%22%20smith%2C%20iii,dc=example,dc=net",
+//            "CN=James \\\"Jim\\\" Smith\\, III,DC=example,DC=net" },
+//        { "CN=John Smith\\2C III,DC=example,DC=net",
+//            "cn=john%20smith%2C%20iii,dc=example,dc=net",
+//            "CN=John Smith\\, III,DC=example,DC=net" },
+//        { "CN=\\23John Smith\\20,DC=example,DC=net",
+//            "cn=%23john%20smith,dc=example,dc=net",
+//            "CN=\\#John Smith\\ ,DC=example,DC=net" },
+//        { "CN=Before\\0dAfter,DC=example,DC=net",
+//             //\0d is a hex representation of Carriage return. It is mapped
+//             //to a SPACE as defined in the MAP ( RFC 4518)
+//            "cn=before%20after,dc=example,dc=net",
+//            "CN=Before\\0dAfter,DC=example,DC=net" },
+//        { "1.3.6.1.4.1.1466.0=#04024869",
+//             //Unicode codepoints from 0000-0008 are mapped to nothing.
+//            "1.3.6.1.4.1.1466.0=hi",
+//            "1.3.6.1.4.1.1466.0=\\04\\02Hi" },
+//        { "1.1.1=", "1.1.1=", "1.1.1=" },
+//        { "CN=Lu\\C4\\8Di\\C4\\87", "cn=luc%CC%8Cic%CC%81",
+//            "CN=Lu\u010di\u0107" },
+//        { "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8,o=Airius",
+//            "ou=%E5%96%B6%E6%A5%AD%E9%83%A8,o=airius",
+//            "ou=\u55b6\u696d\u90e8,o=Airius" },
+        { "photo=\\ john \\ ,dc=com", "photo=%20%6A%6F%68%6E%20%20,dc=com",
             "photo=\\ john \\ ,dc=com" },
         { "AB-global=", "ab-global=", "AB-global=" },
         { "OU= Sales + CN = J. Smith ,DC=example,DC=net",
-            "cn=j. smith+ou=sales,dc=example,dc=net",
+            "cn=j.%20smith+ou=sales,dc=example,dc=net",
             "OU=Sales+CN=J. Smith,DC=example,DC=net" },
         { "cn=John+a=", "a=+cn=john", "cn=John+a=" },
         { "OID.1.3.6.1.4.1.1466.0=#04024869",
@@ -123,7 +128,7 @@
             "1.3.6.1.4.1.1466.0=hi",
             "1.3.6.1.4.1.1466.0=\\04\\02Hi" },
         { "O=\"Sue, Grabbit and Runn\",C=US",
-            "o=sue\\, grabbit and runn,c=us",
+            "o=sue%2C%20grabbit%20and%20runn,c=us",
             "O=Sue\\, Grabbit and Runn,C=US" }, };
   }
 
@@ -286,53 +291,68 @@
 
 
   /**
-   * Tests the <CODE>decode</CODE> method which takes a String
+   * Tests the <CODE>valueOf</CODE> method which takes a String
    * argument.
    *
    * @param rawDN
    *          Raw DN string representation.
    * @param normDN
    *          Normalized DN string representation.
-   * @param stringDN
-   *          String representation.
+   * @param unused
+   *          Unused argument.
    * @throws Exception
    *           If the test failed unexpectedly.
    */
   @Test(dataProvider = "testDNs")
-  public void testDecodeString(String rawDN, String normDN,
-      String stringDN) throws Exception {
+  public void testValueOf(String rawDN, String normDN, String unused) throws Exception {
     DN dn = DN.valueOf(rawDN);
-    StringBuilder buffer = new StringBuilder();
-    buffer.append(normDN);
-    Platform.normalize(buffer);
-    assertEquals(dn.toNormalizedString(), buffer.toString());
+    StringBuilder normalizedDnString = new StringBuilder(normDN);
+    Platform.normalize(normalizedDnString);
+    assertEquals(dn.toIrreversibleReadableString(), normalizedDnString.toString());
   }
 
 
 
   /**
-   * Tests the <CODE>decode</CODE> method which takes a String
+   * Tests the <CODE>decode</CODE> method which takes a ByteString
    * argument.
    *
    * @param rawDN
    *          Raw DN string representation.
    * @param normDN
    *          Normalized DN string representation.
+   * @param unused
+   *          Unused argument.
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(dataProvider = "testDNs")
+  public void testDecodeByteString(String rawDN, String normDN, String unused) throws Exception {
+    DN dn = DN.decode(ByteString.valueOf(rawDN));
+    StringBuilder normalizedDNString = new StringBuilder(normDN);
+    Platform.normalize(normalizedDNString);
+
+    assertEquals(dn.toIrreversibleReadableString(), normalizedDNString.toString());
+  }
+
+
+
+  /**
+   * Test DN string decoder.
+   *
+   * @param rawDN
+   *          Raw DN string representation.
+   * @param unused
+   *          Unused argument.
    * @param stringDN
    *          String representation.
    * @throws Exception
    *           If the test failed unexpectedly.
    */
   @Test(dataProvider = "testDNs")
-  public void testDecodeOctetString(String rawDN, String normDN,
-      String stringDN) throws Exception {
-    ByteString octetString = ByteString.valueOf(rawDN);
-
-    DN dn = DN.decode(octetString);
-    StringBuilder buffer = new StringBuilder();
-    buffer.append(normDN);
-    Platform.normalize(buffer);
-    assertEquals(dn.toNormalizedString(), buffer.toString());
+  public void testToString(String rawDN, String unused, String stringDN) throws Exception {
+    DN dn = DN.valueOf(rawDN);
+    assertEquals(dn.toString(), stringDN);
   }
 
 
@@ -347,11 +367,10 @@
   public void testToNormalizedString() throws Exception {
     DN dn = DN.valueOf("dc=example,dc=com");
 
-    StringBuilder buffer = new StringBuilder();
-    dn.toNormalizedString(buffer);
-    assertEquals(buffer.toString(), "dc=example,dc=com");
-
-    assertEquals(dn.toNormalizedString(), "dc=example,dc=com");
+    assertEquals(dn.toIrreversibleNormalizedByteString(),
+        new ByteStringBuilder().append("dc=com").append(DN.NORMALIZED_RDN_SEPARATOR).append("dc=example")
+        .toByteString());
+    assertEquals(dn.toIrreversibleReadableString(), "dc=example,dc=com");
   }
 
 
@@ -436,7 +455,7 @@
     DN nullDN = DN.rootDN();
 
     assertTrue(nullDN.size() == 0);
-    assertEquals(nullDN.toNormalizedString(), "");
+    assertEquals(nullDN.toString(), "");
   }
 
 
@@ -615,7 +634,7 @@
     assertEquals(p, e);
     assertEquals(p.hashCode(), e.hashCode());
 
-    assertEquals(p.toNormalizedString(), e.toNormalizedString());
+    assertEquals(p.toIrreversibleReadableString(), e.toIrreversibleReadableString());
     assertEquals(p.toString(), e.toString());
 
     assertEquals(p.rdn(), RDN.decode("dc=bar"));
@@ -734,7 +753,7 @@
     // Shoudld throw.
     dn.getRDN(i);
 
-    fail("Excepted exception for RDN index " + i + " in DN " + s);
+    Assert.fail("Excepted exception for RDN index " + i + " in DN " + s);
   }
 
 
@@ -829,7 +848,7 @@
     assertEquals(c, e);
     assertEquals(c.hashCode(), e.hashCode());
 
-    assertEquals(c.toNormalizedString(), e.toNormalizedString());
+    assertEquals(c.toIrreversibleReadableString(), e.toIrreversibleReadableString());
     assertEquals(c.toString(), e.toString());
 
     assertEquals(c.rdn(), RDN.decode("dc=foo"));
@@ -1224,12 +1243,12 @@
 
     if (result == 0) {
       if (h1 != h2) {
-        fail("Hash codes for <" + first + "> and <" + second
+        Assert.fail("Hash codes for <" + first + "> and <" + second
             + "> should be the same.");
       }
     } else {
       if (h1 == h2) {
-        fail("Hash codes for <" + first + "> and <" + second
+        Assert.fail("Hash codes for <" + first + "> and <" + second
             + "> should be the same.");
       }
     }
@@ -1270,23 +1289,29 @@
 
 
 
-  /**
-   * Test DN string decoder.
-   *
-   * @param rawDN
-   *          Raw DN string representation.
-   * @param normDN
-   *          Normalized DN string representation.
-   * @param stringDN
-   *          String representation.
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test(dataProvider = "testDNs")
-  public void testToString(String rawDN, String normDN,
-      String stringDN) throws Exception {
-    DN dn = DN.valueOf(rawDN);
-    assertEquals(dn.toString(), stringDN);
+  @DataProvider
+  public Object[][] renameData()
+  {
+    return new Object[][] {
+        // DN to rename, from DN, to DN , expected DN after renaming
+        { "dc=com", "dc=com", "dc=org", "dc=org" },
+        { "dc=com2", "dc=com", "dc=org", "dc=com2" },
+        { "dc=example1,dc=com", "dc=com", "dc=org", "dc=example1,dc=org"},
+        { "dc=example1,dc=example2,dc=com", "dc=com", "dc=org", "dc=example1,dc=example2,dc=org"},
+        { "dc=example1,dc=example2,dc=com", "dc=example2,dc=com", "dc=example2,dc=org",
+            "dc=example1,dc=example2,dc=org"},
+        { "dc=example1,dc=example2,dc=com", "dc=example2,dc=com", "dc=example3,dc=org",
+            "dc=example1,dc=example3,dc=org"}
+    };
+  }
+
+  @Test(dataProvider="renameData")
+  public void testRename(String dnString, String fromDN, String toDN, String expectedDN) throws Exception
+  {
+    DN dn = DN.valueOf(dnString);
+    DN renamed = dn.rename(DN.valueOf(fromDN), DN.valueOf(toDN));
+
+    assertThat(renamed).isEqualTo(DN.valueOf(expectedDN));
   }
 }
 
diff --git a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java
index ddf92ac..69c9e6d 100644
--- a/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java
+++ b/opendj-sdk/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2014 ForgeRock AS
+ *      Portions Copyright 2014-2015 ForgeRock AS
  */
 package org.opends.server.types;
 
@@ -195,50 +195,37 @@
   @DataProvider(name = "testRDNs")
   public Object[][] createData() {
     return new Object[][] {
-        { "dc=hello world", "dc=hello world", "dc=hello world" },
-        { "dc =hello world", "dc=hello world", "dc=hello world" },
-        { "dc  =hello world", "dc=hello world", "dc=hello world" },
-        { "dc= hello world", "dc=hello world", "dc=hello world" },
-        { "dc=  hello world", "dc=hello world", "dc=hello world" },
+        { "dc=hello world", "dc=hello%20world", "dc=hello world" },
+        { "dc =hello world", "dc=hello%20world", "dc=hello world" },
+        { "dc  =hello world", "dc=hello%20world", "dc=hello world" },
+        { "dc= hello world", "dc=hello%20world", "dc=hello world" },
+        { "dc=  hello world", "dc=hello%20world", "dc=hello world" },
         { "undefined=hello", "undefined=hello", "undefined=hello" },
-        { "DC=HELLO WORLD", "dc=hello world", "DC=HELLO WORLD" },
-        { "dc = hello    world", "dc=hello world",
-            "dc=hello    world" },
-        { "   dc = hello world   ", "dc=hello world",
-            "dc=hello world" },
-        { "givenName=John+cn=Doe", "cn=doe+givenname=john",
-            "givenName=John+cn=Doe" },
-        { "givenName=John\\+cn=Doe", "givenname=john\\+cn=doe",
-            "givenName=John\\+cn=Doe" },
-        { "cn=Doe\\, John", "cn=doe\\, john", "cn=Doe\\, John" },
-        { "OU=Sales+CN=J. Smith", "cn=j. smith+ou=sales",
-            "OU=Sales+CN=J. Smith" },
-        { "CN=James \\\"Jim\\\" Smith\\, III",
-            "cn=james \\\"jim\\\" smith\\, iii",
+        { "DC=HELLO WORLD", "dc=hello%20world", "DC=HELLO WORLD" },
+        { "dc = hello    world", "dc=hello%20world", "dc=hello    world" },
+        { "   dc = hello world   ", "dc=hello%20world",  "dc=hello world" },
+        { "givenName=John+cn=Doe", "cn=doe+givenname=john", "givenName=John+cn=Doe" },
+        { "givenName=John\\+cn=Doe", "givenname=john%2Bcn%3Ddoe", "givenName=John\\+cn=Doe" },
+        { "cn=Doe\\, John", "cn=doe%2C%20john", "cn=Doe\\, John" },
+        { "OU=Sales+CN=J. Smith", "cn=j.%20smith+ou=sales","OU=Sales+CN=J. Smith" },
+        { "CN=James \\\"Jim\\\" Smith\\, III", "cn=james%20%22jim%22%20smith%2C%20iii",
             "CN=James \\\"Jim\\\" Smith\\, III" },
             //\0d is a hex representation of Carriage return. It is mapped
              //to a SPACE as defined in the MAP ( RFC 4518)
-        { "CN=Before\\0dAfter", "cn=before after",
-            "CN=Before\\0dAfter" },
+        { "CN=Before\\0dAfter", "cn=before%20after", "CN=Before\\0dAfter" },
         { "1.3.6.1.4.1.1466.0=#04024869",
             //Unicode codepoints from 0000-0008 are mapped to nothing.
-            "1.3.6.1.4.1.1466.0=hi",
-            "1.3.6.1.4.1.1466.0=\\04\\02Hi" },
-        { "CN=Lu\\C4\\8Di\\C4\\87", "cn=lu\u010di\u0107",
-            "CN=Lu\u010di\u0107" },
-        { "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8",
-            "ou=\u55b6\u696d\u90e8",
-            "ou=\u55b6\u696d\u90e8" },
-        { "photo=\\ john \\ ", "photo=\\ john \\ ",
-            "photo=\\ john \\ " },
+            "1.3.6.1.4.1.1466.0=hi", "1.3.6.1.4.1.1466.0=\\04\\02Hi" },
+        { "CN=Lu\\C4\\8Di\\C4\\87", "cn=luc%CC%8Cic%CC%81", "CN=Lu\u010di\u0107" },
+        { "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8", "ou=%E5%96%B6%E6%A5%AD%E9%83%A8", "ou=\u55b6\u696d\u90e8" },
+        { "photo=\\ john \\ ", "photo=%20%6A%6F%68%6E%20%20", "photo=\\ john \\ " },
      //   { "AB-global=", "ab-global=", "AB-global=" },
         { "cn=John+a=", "a=+cn=john", "cn=John+a=" },
         { "OID.1.3.6.1.4.1.1466.0=#04024869",
             //Unicode codepoints from 0000-0008 are mapped to nothing.
             "1.3.6.1.4.1.1466.0=hi",
             "1.3.6.1.4.1.1466.0=\\04\\02Hi" },
-        { "O=\"Sue, Grabbit and Runn\"", "o=sue\\, grabbit and runn",
-            "O=Sue\\, Grabbit and Runn" }, };
+        { "O=\"Sue, Grabbit and Runn\"", "o=sue%2C%20grabbit%20and%20runn", "O=Sue\\, Grabbit and Runn" }, };
   }
 
 
@@ -256,8 +243,7 @@
    *           If the test failed unexpectedly.
    */
   @Test(dataProvider = "testRDNs")
-  public void testDecodeString(String rawRDN, String normRDN,
-      String stringRDN) throws Exception {
+  public void testNormalizeToReadableString(String rawRDN, String normRDN, String stringRDN) throws Exception {
     RDN rdn = RDN.decode(rawRDN);
     StringBuilder buffer = new StringBuilder();
     buffer.append(normRDN);

--
Gitblit v1.10.0