From 39cfb739395cca91589c958fda4790c87ce0c9c8 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

---
 opendj3-server-dev/src/server/org/opends/server/backends/jeb/DN2URI.java |   78 ++++++++++++++++++++++++++++-----------
 1 files changed, 56 insertions(+), 22 deletions(-)

diff --git a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/DN2URI.java b/opendj3-server-dev/src/server/org/opends/server/backends/jeb/DN2URI.java
index 3b709e9..a4b4fed 100644
--- a/opendj3-server-dev/src/server/org/opends/server/backends/jeb/DN2URI.java
+++ b/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;
   }

--
Gitblit v1.10.0