From 88cfe5045d77d433ce02b0ef10ee84c9d4fb15e2 Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Fri, 23 May 2014 15:17:15 +0000
Subject: [PATCH] (CR-3599) Convert all protocols message to use ByteArrayBuilder + ByteArrayScanner

---
 opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java |  364 ++++++++++++---------------------------------------
 1 files changed, 90 insertions(+), 274 deletions(-)

diff --git a/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java b/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
index 2efa4b1..a47d015 100644
--- a/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
+++ b/opends/src/server/org/opends/server/replication/protocol/LDAPUpdateMsg.java
@@ -22,11 +22,10 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2013 ForgeRock AS
+ *      Portions Copyright 2011-2014 ForgeRock AS
  */
 package org.opends.server.replication.protocol;
 
-import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.zip.DataFormatException;
@@ -83,7 +82,7 @@
    * @param dn The DN of the entry on which the change
    *           that caused the creation of this object happened
    */
-  public LDAPUpdateMsg(OperationContext ctx, DN dn)
+  LDAPUpdateMsg(OperationContext ctx, DN dn)
   {
     this.protocolVersion = ProtocolVersion.getCurrentVersion();
     this.csn = ctx.getCSN();
@@ -101,7 +100,7 @@
    * @param dn        The DN of the entry on which the change
    *                  that caused the creation of this object happened
    */
-  public LDAPUpdateMsg(CSN csn, String entryUUID, DN dn)
+  LDAPUpdateMsg(CSN csn, String entryUUID, DN dn)
   {
     this.protocolVersion = ProtocolVersion.getCurrentVersion();
     this.csn = csn;
@@ -117,27 +116,19 @@
    */
   public static LDAPUpdateMsg generateMsg(PostOperationOperation op)
   {
-    LDAPUpdateMsg msg = null;
     switch (op.getOperationType())
     {
     case MODIFY :
-      msg = new ModifyMsg((PostOperationModifyOperation) op);
-      break;
-
+      return new ModifyMsg((PostOperationModifyOperation) op);
     case ADD:
-      msg = new AddMsg((PostOperationAddOperation) op);
-      break;
-
+      return new AddMsg((PostOperationAddOperation) op);
     case DELETE :
-      msg = new DeleteMsg((PostOperationDeleteOperation) op);
-      break;
-
+      return new DeleteMsg((PostOperationDeleteOperation) op);
     case MODIFY_DN :
-      msg = new ModifyDNMsg( (PostOperationModifyDNOperation) op);
-      break;
+      return new ModifyDNMsg( (PostOperationModifyDNOperation) op);
+    default:
+      return null;
     }
-
-    return msg;
   }
 
   /**
@@ -210,138 +201,62 @@
    * of a synchronized portion of code.
    *
    * This method is not synchronized and therefore not MT safe.
-   *
-   * @throws UnsupportedEncodingException when encoding fails.
    */
-  public void encode() throws UnsupportedEncodingException
+  public void encode()
   {
     bytes = getBytes(ProtocolVersion.getCurrentVersion());
   }
 
-  /**
-   * Encode the common header for all the UpdateMsg. This uses the current
-   * protocol version.
-   *
-   * @param type the type of UpdateMsg to encode.
-   * @param additionalLength additional length needed to encode the remaining
-   *                         part of the UpdateMsg.
-   * @param version The ProtocolVersion to use when encoding.
-   * @return a byte array containing the common header and enough space to
-   *         encode the remaining bytes of the UpdateMsg as was specified
-   *         by the additionalLength.
-   *         (byte array length = common header length + additionalLength)
-   * @throws UnsupportedEncodingException if UTF-8 is not supported.
-   */
+  /** {@inheritDoc} */
   @Override
-  public byte[] encodeHeader(byte type, int additionalLength, short version)
-    throws UnsupportedEncodingException
+  public ByteArrayBuilder encodeHeader(byte msgType, short protocolVersion)
   {
-    byte[] byteDn = dn.toString().getBytes("UTF-8");
-    byte[] csnByte = getCSN().toString().getBytes("UTF-8");
-    byte[] byteEntryuuid = getEntryUUID().getBytes("UTF-8");
-
     /* The message header is stored in the form :
      * <operation type><protocol version><CSN><dn><entryuuid><assured>
      * <assured mode> <safe data level>
-     * the length of result byte array is therefore :
-     *   1 + 1 + CSN length + 1 + dn length + 1 + uuid length + 1 + 1
-     *   + 1 + 1 + additional_length
      */
-    int length = 8 + csnByte.length + byteDn.length
-                 + byteEntryuuid.length + additionalLength;
-
-    byte[] encodedMsg = new byte[length];
-
-    // put the type of the operation
-    encodedMsg[0] = type;
-
-    // put the protocol version
-    encodedMsg[1] = (byte) version;
-    int pos = 2;
-
-    // Put the CSN
-    pos = addByteArray(csnByte, encodedMsg, pos);
-
-    // Put the DN and a terminating 0
-    pos = addByteArray(byteDn, encodedMsg, pos);
-
-    // Put the entry uuid and a terminating 0
-    pos = addByteArray(byteEntryuuid, encodedMsg, pos);
-
-    // Put the assured flag
-    encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
-
-    // Put the assured mode
-    encodedMsg[pos++] = assuredMode.getValue();
-
-    // Put the safe data level
-    encodedMsg[pos++] = safeDataLevel;
-
-    return encodedMsg;
+    final ByteArrayBuilder builder = new ByteArrayBuilder();
+    builder.append(msgType);
+    builder.append((byte) protocolVersion);
+    builder.appendUTF8(csn);
+    builder.append(dn);
+    builder.append(entryUUID);
+    builder.append(assuredFlag);
+    builder.append(assuredMode.getValue());
+    builder.append(safeDataLevel);
+    return builder;
   }
 
   /**
    * Encode the common header for all the UpdateMessage. This uses the version
    * 1 of the replication protocol (used for compatibility purpose).
    *
-   * @param type the type of UpdateMessage to encode.
-   * @param additionalLength additional length needed to encode the remaining
-   *                         part of the UpdateMessage.
-   * @return a byte array containing the common header and enough space to
-   *         encode the remaining bytes of the UpdateMessage as was specified
-   *         by the additionalLength.
-   *         (byte array length = common header length + additionalLength)
-   * @throws UnsupportedEncodingException if UTF-8 is not supported.
+   * @param msgType the type of UpdateMessage to encode.
+   * @return a byte array builder containing the common header
    */
-  public byte[] encodeHeader_V1(byte type, int additionalLength)
-    throws UnsupportedEncodingException
+  ByteArrayBuilder encodeHeader_V1(byte msgType)
   {
-    byte[] byteDn = dn.toString().getBytes("UTF-8");
-    byte[] csnByte = getCSN().toString().getBytes("UTF-8");
-    byte[] byteEntryuuid = getEntryUUID().getBytes("UTF-8");
-
     /* The message header is stored in the form :
      * <operation type><CSN><dn><assured><entryuuid><change>
-     * the length of result byte array is therefore :
-     *   1 + CSN length + 1 + dn length + 1  + 1 +
-     *   uuid length + 1 + additional_length
      */
-    int length = 5 + csnByte.length + byteDn.length
-                 + byteEntryuuid.length + additionalLength;
-
-    byte[] encodedMsg = new byte[length];
-
-    // put the type of the operation
-    encodedMsg[0] = type;
-    int pos = 1;
-
-    // put the CSN
-    pos = addByteArray(csnByte, encodedMsg, pos);
-
-    // put the assured information
-    encodedMsg[pos++] = (assuredFlag ? (byte) 1 : 0);
-
-    // put the DN and a terminating 0
-    pos = addByteArray(byteDn, encodedMsg, pos);
-
-    // put the entry uuid and a terminating 0
-    pos = addByteArray(byteEntryuuid, encodedMsg, pos);
-
-    return encodedMsg;
+    final ByteArrayBuilder builder = new ByteArrayBuilder();
+    builder.append(msgType);
+    builder.appendUTF8(csn);
+    builder.append(assuredFlag);
+    builder.append(dn);
+    builder.append(entryUUID);
+    return builder;
   }
 
-  /**
-   * {@inheritDoc}
-   */
+  /** {@inheritDoc} */
   @Override
-  public byte[] getBytes(short reqProtocolVersion)
-    throws UnsupportedEncodingException
+  public byte[] getBytes(short protocolVersion)
   {
-    if (reqProtocolVersion == ProtocolVersion.REPLICATION_PROTOCOL_V1)
+    if (protocolVersion == ProtocolVersion.REPLICATION_PROTOCOL_V1)
     {
       return getBytes_V1();
     }
-    else if (reqProtocolVersion <= ProtocolVersion.REPLICATION_PROTOCOL_V3)
+    else if (protocolVersion <= ProtocolVersion.REPLICATION_PROTOCOL_V3)
     {
       return getBytes_V23();
     }
@@ -351,7 +266,7 @@
       if (bytes == null)
       {
         // this is the current version of the protocol
-        bytes = getBytes_V45(reqProtocolVersion);
+        bytes = getBytes_V45(protocolVersion);
       }
       return bytes;
     }
@@ -362,45 +277,35 @@
    * 1 of the replication protocol (used for compatibility purpose).
    *
    * @return The byte array representation of this Message.
-   *
-   * @throws UnsupportedEncodingException  When the encoding of the message
-   *         failed because the UTF-8 encoding is not supported.
    */
-  public abstract byte[] getBytes_V1() throws UnsupportedEncodingException;
+  protected abstract byte[] getBytes_V1();
 
   /**
    * Get the byte array representation of this Message. This uses the version
    * 2 of the replication protocol (used for compatibility purpose).
    *
    * @return The byte array representation of this Message.
-   *
-   * @throws UnsupportedEncodingException  When the encoding of the message
-   *         failed because the UTF-8 encoding is not supported.
    */
-  public abstract byte[] getBytes_V23() throws UnsupportedEncodingException;
-
+  protected abstract byte[] getBytes_V23();
 
   /**
    * Get the byte array representation of this Message. This uses the provided
    * version number which must be version 4 or newer.
-   * @param reqProtocolVersion TODO
    *
+   * @param protocolVersion the actual protocol version to encode into
    * @return The byte array representation of this Message.
-   *
-   * @throws UnsupportedEncodingException  When the encoding of the message
-   *         failed because the UTF-8 encoding is not supported.
    */
-  public abstract byte[] getBytes_V45(short reqProtocolVersion)
-      throws UnsupportedEncodingException;
-
+  protected abstract byte[] getBytes_V45(short protocolVersion);
 
   /**
    * Encode a list of attributes.
    */
-   static private byte[] encodeAttributes(Collection<Attribute> attributes)
+   private static byte[] encodeAttributes(Collection<Attribute> attributes)
    {
      if (attributes==null)
+     {
        return new byte[0];
+     }
      try
      {
        ByteStringBuilder byteBuilder = new ByteStringBuilder();
@@ -424,151 +329,62 @@
   /**
    * Decode the Header part of this Update Message, and check its type.
    *
-   * @param types The allowed types of this Update Message.
-   * @param encodedMsg the encoded form of the UpdateMsg.
-   * @return the position at which the remaining part of the message starts.
+   * @param scanner the encoded form of the UpdateMsg.
+   * @param allowedTypes The allowed types of this Update Message.
    * @throws DataFormatException if the encodedMsg does not contain a valid
    *         common header.
    */
-   public int decodeHeader(byte[] types, byte[] encodedMsg)
-                          throws DataFormatException
-   {
-     // first byte is the type
-     boolean foundMatchingType = false;
-     for (byte type : types)
-     {
-       if (type == encodedMsg[0])
-       {
-         foundMatchingType = true;
-         break;
-       }
-     }
-     if (!foundMatchingType)
-       throw new DataFormatException("byte[] is not a valid update msg: "
-           + encodedMsg[0]);
-
-     /*
-      * For older protocol version PDUs, decode the matching version header
-      * instead.
-      */
-     if ((encodedMsg[0] == MSG_TYPE_ADD_V1) ||
-         (encodedMsg[0] == MSG_TYPE_DELETE_V1) ||
-         (encodedMsg[0] == MSG_TYPE_MODIFYDN_V1) ||
-         (encodedMsg[0] == MSG_TYPE_MODIFY_V1))
-     {
-       return decodeHeader_V1(encodedMsg);
-     }
-
-     // read the protocol version
-     protocolVersion = encodedMsg[1];
-
-     try
-     {
-       // Read the CSN
-       int pos = 2;
-       int length = getNextLength(encodedMsg, pos);
-       String csnStr = new String(encodedMsg, pos, length, "UTF-8");
-       pos += length + 1;
-       csn = new CSN(csnStr);
-
-       // Read the dn
-       length = getNextLength(encodedMsg, pos);
-       dn = DN.decode(new String(encodedMsg, pos, length, "UTF-8"));
-       pos += length + 1;
-
-       // Read the entryuuid
-       length = getNextLength(encodedMsg, pos);
-       entryUUID = new String(encodedMsg, pos, length, "UTF-8");
-       pos += length + 1;
-
-       // Read the assured information
-       assuredFlag = encodedMsg[pos++] == 1;
-
-       // Read the assured mode
-       assuredMode = AssuredMode.valueOf(encodedMsg[pos++]);
-
-       // Read the safe data level
-       safeDataLevel = encodedMsg[pos++];
-
-       return pos;
-     }
-     catch (UnsupportedEncodingException e)
-     {
-       throw new DataFormatException("UTF-8 is not supported by this jvm.");
-     }
-     catch (IllegalArgumentException e)
-     {
-       throw new DataFormatException(e.getLocalizedMessage());
-     }
-     catch (DirectoryException e)
-     {
-       throw new DataFormatException(e.getLocalizedMessage());
-     }
-  }
-
-  /**
-   * Decode the Header part of this Update Message, and check its type. This
-   * uses the version 1 of the replication protocol (used for compatibility
-   * purpose).
-   *
-   * @param encodedMsg the encoded form of the UpdateMessage.
-   * @return the position at which the remaining part of the message starts.
-   * @throws DataFormatException if the encodedMsg does not contain a valid
-   *         common header.
-   */
-  public int decodeHeader_V1(byte[] encodedMsg)
-                          throws DataFormatException
+  void decodeHeader(ByteArrayScanner scanner, byte... allowedTypes)
+      throws DataFormatException
   {
-    if ((encodedMsg[0] != MSG_TYPE_ADD_V1) &&
-      (encodedMsg[0] != MSG_TYPE_DELETE_V1) &&
-      (encodedMsg[0] != MSG_TYPE_MODIFYDN_V1) &&
-      (encodedMsg[0] != MSG_TYPE_MODIFY_V1))
-      throw new DataFormatException("byte[] is not a valid update msg: expected"
-        + " a V1 PDU, received: " + encodedMsg[0]);
-
-    // Force version to V1 (other new parameters take their default values
-    // (assured stuff...))
-    protocolVersion = ProtocolVersion.REPLICATION_PROTOCOL_V1;
-
-    try
+    final byte msgType = scanner.nextByte();
+    if (!isTypeAllowed(msgType, allowedTypes))
     {
-      // read the CSN
-      int pos = 1;
-      int length = getNextLength(encodedMsg, pos);
-      String csnStr = new String(encodedMsg, pos, length, "UTF-8");
-      pos += length + 1;
-      csn = new CSN(csnStr);
-
-      // read the assured information
-      assuredFlag = encodedMsg[pos++] == 1;
-
-      // read the dn
-      length = getNextLength(encodedMsg, pos);
-      dn = DN.decode(new String(encodedMsg, pos, length, "UTF-8"));
-      pos += length + 1;
-
-      // read the entryuuid
-      length = getNextLength(encodedMsg, pos);
-      entryUUID = new String(encodedMsg, pos, length, "UTF-8");
-      pos += length + 1;
-
-      return pos;
+      throw new DataFormatException("byte[] is not a valid update msg: "
+          + msgType);
     }
-    catch (UnsupportedEncodingException e)
+
+    if (msgType == MSG_TYPE_ADD_V1
+        || msgType == MSG_TYPE_DELETE_V1
+        || msgType == MSG_TYPE_MODIFYDN_V1
+        || msgType == MSG_TYPE_MODIFY_V1)
     {
-      throw new DataFormatException("UTF-8 is not supported by this jvm.");
+      /*
+       * For older protocol versions, decode the matching version header instead
+       */
+      // Force version to V1 (other new parameters take their default values
+      // (assured stuff...))
+      protocolVersion = ProtocolVersion.REPLICATION_PROTOCOL_V1;
+      csn = scanner.nextCSNUTF8();
+      assuredFlag = scanner.nextBoolean();
+      dn = scanner.nextDN();
+      entryUUID = scanner.nextString();
     }
-    catch (DirectoryException e)
+    else
     {
-      throw new DataFormatException(e.getLocalizedMessage());
+      protocolVersion = scanner.nextByte();
+      csn = scanner.nextCSNUTF8();
+      dn = scanner.nextDN();
+      entryUUID = scanner.nextString();
+      assuredFlag = scanner.nextBoolean();
+      assuredMode = AssuredMode.valueOf(scanner.nextByte());
+      safeDataLevel = scanner.nextByte();
     }
   }
 
-  /**
-   * Return the number of bytes used by this message.
-   *
-   * @return The number of bytes used by this message.
-   */
+  private boolean isTypeAllowed(final byte msgType, byte... allowedTypes)
+  {
+    for (byte allowedType : allowedTypes)
+    {
+      if (msgType == allowedType)
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /** {@inheritDoc} */
   @Override
   public abstract int size();
 
@@ -613,7 +429,7 @@
    * @throws LDAPException when it occurs.
    * @throws ASN1Exception when it occurs.
    */
-  public ArrayList<RawAttribute> decodeRawAttributes(byte[] in)
+  ArrayList<RawAttribute> decodeRawAttributes(byte[] in)
   throws LDAPException, ASN1Exception
   {
     ArrayList<RawAttribute> rattr = new ArrayList<RawAttribute>();
@@ -642,7 +458,7 @@
    * @throws LDAPException when it occurs.
    * @throws ASN1Exception when it occurs.
    */
-  public ArrayList<Attribute> decodeAttributes(byte[] in)
+  ArrayList<Attribute> decodeAttributes(byte[] in)
   throws LDAPException, ASN1Exception
   {
     ArrayList<Attribute> lattr = new ArrayList<Attribute>();

--
Gitblit v1.10.0