public static Message transferInputStream(List<Transformer> transformers, InputStream inStream, MessageInfo msgInfo)
      throws IOException {
    // Read the entire stream out so the underlying transformer won't increase the ref counter for the GetResponse ByteBuf.
    Message msg =
        new Message(msgInfo, new ByteArrayInputStream(Utils.readBytesFromStream(inStream, (int) msgInfo.getSize())));
    if (transformers == null || transformers.isEmpty()) {
      // Write the message without any transformations.
      return msg;
    }

    logger.trace("transferInputStream for message {}", msgInfo);
    TransformationOutput output = null;
    for (Transformer transformer : transformers) {
      // Warms up transformer with message infos representing messages it will transform later.
      // It may convert the key with transformer.converter and cache them.
      try {
        transformer.warmup(Collections.singletonList(msgInfo), true);
      } catch (Exception e) {
        throw new IOException("Encountered exception during transformer.warmup", e);
      }

      // transform the message. It may use the converted key in the warmup stage.
      output = transformer.transform(msg);
      if (output.getException() != null || output.getMsg() == null) {
        break;
      }
      msg = output.getMsg();
    }

    if (output.getException() != null) {
      if (output.getException() instanceof MessageFormatException) {
        logger.error("Error validating/transforming the message {} and hence skipping the message", msgInfo,
            output.getException());
        return null;
      } else {
        throw new IOException("Encountered exception during transformation", output.getException());
      }
    }
    if (output.getMsg() == null) {
      logger.info("Transformation is on, and the message with id {} does not have a replacement and was discarded.",
          msgInfo.getStoreKey());
      return null;
    }
    logger.trace("Original message length {}, transformed bytes read {}", msgInfo.getSize(),
        output.getMsg().getMessageInfo().getSize());
    return output.getMsg();
  }
Transfer one message with a list of {@link Transformer}
@param inStream The stream from which bytes need to be read.
@param msgInfo MessageInfo which contains details about the message in the stream
@param transformers the list of the {@link Transformer} to use (in order).
@throws java.io.IOException
ambry-messageformat/src/main/java/com/github/ambry/messageformat/MessageSievingInputStream.java