private void handleMetadataBlob(BlobData blobData, byte[] userMetadata, ByteBuffer encryptionKey)
        throws IOException, MessageFormatException {
      ByteBuf serializedMetadataContent = blobData.content();
      compositeBlobInfo =
          MetadataContentSerDe.deserializeMetadataContentRecord(serializedMetadataContent.nioBuffer(), blobIdFactory);
      totalSize = compositeBlobInfo.getTotalSize();
      chunkMetadataList = compositeBlobInfo.getChunkMetadataList();
      boolean rangeResolutionFailure = false;
      try {
        long rangeTotalSize = totalSize;
        if (options.getBlobOptions.hasBlobSegmentIdx()) {
          int requestedSegment = options.getBlobOptions.getBlobSegmentIdx();
          if (requestedSegment < 0 || requestedSegment >= chunkMetadataList.size()) {
            throw new IllegalArgumentException(
                "Bad segment number: " + requestedSegment + ", num of keys: " + chunkMetadataList.size());
          }
          chunkMetadataList = chunkMetadataList.subList(requestedSegment, requestedSegment + 1);
          rangeTotalSize = chunkMetadataList.get(0).getSize();
        }
        if (options.getBlobOptions.getRange() != null) {
          resolvedByteRange = options.getBlobOptions.getRange().toResolvedByteRange(rangeTotalSize);
          // Get only the chunks within the range.
          if (!options.getBlobOptions.hasBlobSegmentIdx()) {
            chunkMetadataList = compositeBlobInfo.getStoreKeysInByteRange(resolvedByteRange.getStartOffset(),
                resolvedByteRange.getEndOffset());
          }
        }
      } catch (IllegalArgumentException e) {
        onInvalidRange(e);
        rangeResolutionFailure = true;
      }
      if (!rangeResolutionFailure) {
        if (options.getChunkIdsOnly || getOperationFlag() == MessageFormatFlags.Blob || encryptionKey == null) {
          initializeDataChunks();
        } else {
          // if blob is encrypted, then decryption is required only in case of GetBlobInfo and GetBlobAll (since user-metadata
          // is expected to be encrypted). In case of GetBlob, Metadata blob does not need any decryption even if BlobProperties says so
          decryptCallbackResultInfo = new DecryptCallBackResultInfo();
          progressTracker.initializeCryptoJobTracker(CryptoJobType.DECRYPTION);
          decryptJobMetricsTracker.onJobSubmission();
          logger.trace("Submitting decrypt job for Metadata chunk {}", blobId);
          long startTimeMs = System.currentTimeMillis();
          cryptoJobHandler.submitJob(
              new DecryptJob(blobId, encryptionKey, null, ByteBuffer.wrap(userMetadata), cryptoService, kms,
                  options.getBlobOptions, decryptJobMetricsTracker,
                  (DecryptJob.DecryptJobResult result, Exception exception) -> {
                    routerMetrics.decryptTimeMs.update(System.currentTimeMillis() - startTimeMs);
                    if (isOperationComplete() || operationException.get() != null) {
                      if (result != null && result.getDecryptedBlobContent() != null) {
                        ReferenceCountUtil.safeRelease(result.getDecryptedBlobContent());
                      }
                    } else {
                      decryptJobMetricsTracker.onJobCallbackProcessingStart();
                      logger.trace(
                          "Handling decrypt job call back for Metadata chunk {} to set decrypt callback results",
                          blobId);
                      decryptCallbackResultInfo.setResultAndException(result, exception);
                      routerCallback.onPollReady();
                      decryptJobMetricsTracker.onJobCallbackProcessingComplete();
                    }
                  }));
        }
      }
    }
Process a metadata blob to find the data chunks that need to be fetched.
@param blobData the metadata blob's data.
@param userMetadata userMetadata of the blob
@param encryptionKey blob encryption key. Could be null for un-encrypted blobs
@throws IOException
@throws MessageFormatException
ambry-router/src/main/java/com/github/ambry/router/GetBlobOperation.java