public static byte[] buildUserMetadata(Map<String, Object> args) throws RestServiceException {
    ByteBuffer userMetadata;
    if (args.containsKey(MultipartPost.USER_METADATA_PART)) {
      userMetadata = (ByteBuffer) args.get(MultipartPost.USER_METADATA_PART);
    } else {
      Map<String, String> userMetadataMap = new HashMap<String, String>();
      int sizeToAllocate = 0;
      for (Map.Entry<String, Object> entry : args.entrySet()) {
        String key = entry.getKey();
        if (key.toLowerCase().startsWith(Headers.USER_META_DATA_HEADER_PREFIX)) {
          // key size
          sizeToAllocate += 4;
          String keyToStore = key.substring(Headers.USER_META_DATA_HEADER_PREFIX.length());
          sizeToAllocate += keyToStore.getBytes(CHARSET).length;
          String value = getHeader(args, key, true);
          userMetadataMap.put(keyToStore, value);
          // value size
          sizeToAllocate += 4;
          sizeToAllocate += value.getBytes(CHARSET).length;
        } else if (key.toLowerCase().startsWith(Headers.USER_META_DATA_ENCODED_HEADER_PREFIX)) {
          // key size
          sizeToAllocate += 4;
          String keyToStore = key.substring(Headers.USER_META_DATA_ENCODED_HEADER_PREFIX.length());
          sizeToAllocate += keyToStore.getBytes(CHARSET).length;
          String urlEncodedValue = getHeader(args, key, true);
          String value;
          try {
            value = URLDecoder.decode(urlEncodedValue, CHARSET.name());
          } catch (UnsupportedEncodingException e) {
            throw new RestServiceException(
                "Fail to decode the header value: " + urlEncodedValue + " for header: " + keyToStore,
                RestServiceErrorCode.UnsupportedEncoding);
          }
          userMetadataMap.put(keyToStore, value);
          // value size
          sizeToAllocate += 4;
          sizeToAllocate += value.getBytes(CHARSET).length;
        }
      }
      if (sizeToAllocate == 0) {
        userMetadata = ByteBuffer.allocate(0);
      } else {
        // version
        sizeToAllocate += 2;
        // size excluding version and crc
        sizeToAllocate += 4;
        // total number of entries
        sizeToAllocate += 4;
        // crc size
        sizeToAllocate += CRC_SIZE;
        userMetadata = ByteBuffer.allocate(sizeToAllocate);
        userMetadata.putShort(USER_METADATA_VERSION_V1);
        // total size = sizeToAllocate - version size - sizeToAllocate size - crc size
        userMetadata.putInt(sizeToAllocate - 6 - CRC_SIZE);
        userMetadata.putInt(userMetadataMap.size());
        for (Map.Entry<String, String> entry : userMetadataMap.entrySet()) {
          String key = entry.getKey();
          Utils.serializeString(userMetadata, key, CHARSET);
          Utils.serializeString(userMetadata, entry.getValue(), CHARSET);
        }
        CRC32 crc = new CRC32();
        crc.update(userMetadata.array(), 0, sizeToAllocate - CRC_SIZE);
        userMetadata.putLong(crc.getValue());
      }
    }
    return userMetadata.array();
  }
Builds user metadata given the arguments associated with a request.
<p>
The following binary format will be used:
<pre>
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|         |   size    |  total   |           |          |             |            |            |            |            |
| version | excluding |  no of   | key1 size |   key1   | value1 size |  value 1   |  key2 size |     ...    |     Crc    |
|(2 bytes)| ver & crc | entries  | (4 bytes) |(key1 size| (4 bytes)   |(value1 size|  (4 bytes) |     ...    |  (8 bytes) |
|         | (4 bytes) | (4 bytes)|           |   bytes) |             |   bytes)   |            |            |            |
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 version        - The version of the user metadata record

 size exluding
 ver & CRC      - The size of the user metadata content excluding the version and the CRC

 total no of
 entries        - Total number of entries in user metadata

 key1 size      - Size of 1st key

 key1           - Content of key1

 value1 size    - Size of 1st value

 value1         - Content of value1

 key2 size      - Size of 2nd key

 crc        - The crc of the user metadata record
</pre>

@param args the arguments associated with the request.
@return the user metadata extracted from arguments.
@throws RestServiceException if usermetadata arguments have null values.
ambry-api/src/main/java/com/github/ambry/rest/RestUtils.java