private BlobId unwrapChunkInfo(ChunkInfo chunkInfo, long intermediateChunkSize, boolean lastChunk)
      throws RouterException {
    long chunkSize = chunkInfo.getChunkSizeInBytes();
    long chunkExpirationTimeInMs = chunkInfo.getExpirationTimeInMs();

    if (chunkSize == 0 || chunkSize > intermediateChunkSize || (
        routerConfig.routerMetadataContentVersion == MessageFormatRecord.Metadata_Content_Version_V2 && !lastChunk
            && chunkSize < intermediateChunkSize)) {
      throw new RouterException(
          "Invalid chunkSize for " + (lastChunk ? "last" : "intermediate") + " chunk: " + chunkSize
              + "; intermediateChunkSize: " + intermediateChunkSize, RouterErrorCode.InvalidPutArgument);
    }

    long metadataExpirationTimeInMs = Utils.addSecondsToEpochTime(passedInBlobProperties.getCreationTimeInMs(),
        passedInBlobProperties.getTimeToLiveInSeconds());
    // the chunk TTL must be at least as long as the metadata blob to ensure that they do not become inaccessible before
    // the metadata chunk does.
    if (Utils.compareTimes(chunkExpirationTimeInMs, metadataExpirationTimeInMs) < 0) {
      throw new RouterException("Chunk expiration time (" + chunkExpirationTimeInMs + ") < metadata expiration time ("
          + metadataExpirationTimeInMs + ")", RouterErrorCode.InvalidPutArgument);
    }

    BlobId blobId = RouterUtils.getBlobIdFromString(chunkInfo.getBlobId(), clusterMap);
    // the metadata blob format does not support nested composite blobs.
    if (blobId.getBlobDataType() != BlobDataType.DATACHUNK) {
      throw new RouterException("Cannot stitch together multiple composite blobs", RouterErrorCode.InvalidBlobId);
    }

    return blobId;
  }
Extract the {@link BlobId} from a {@link ChunkInfo} object and check the metadata in the object to ensure that this
is a valid chunk for stitching.
@param chunkInfo the {@link ChunkInfo} containing the blob ID and extra metadata.
@param intermediateChunkSize the size in bytes that intermediate chunks must be with the current metadata format.
@param lastChunk {@code true} if this is the last chunk in the list of blobs to stitch.
@return the {@link BlobId}.
@throws RouterException if validation of the chunk or blob ID parsing failed.
ambry-router/src/main/java/com/github/ambry/router/PutOperation.java