@Override
  public TransformationOutput transform(Message message) {
    ByteBuffer encryptionKey;
    BlobProperties props;
    ByteBuffer metadata;
    BlobData blobData;
    MessageInfo msgInfo = message.getMessageInfo();
    InputStream msgStream = message.getStream();
    TransformationOutput transformationOutput;
    try {
      // Read header
      ByteBuffer headerVersion = ByteBuffer.allocate(Version_Field_Size_In_Bytes);
      msgStream.read(headerVersion.array());
      short version = headerVersion.getShort();
      if (!isValidHeaderVersion(version)) {
        throw new MessageFormatException("Header version not supported " + version,
            MessageFormatErrorCodes.DataCorrupt);
      }
      int headerSize = getHeaderSizeForVersion(version);
      ByteBuffer headerBuffer = ByteBuffer.allocate(headerSize);
      headerBuffer.put(headerVersion.array());
      msgStream.read(headerBuffer.array(), Version_Field_Size_In_Bytes, headerSize - Version_Field_Size_In_Bytes);
      headerBuffer.rewind();
      MessageHeader_Format header = getMessageHeader(version, headerBuffer);
      header.verifyHeader();
      StoreKey originalKey = storeKeyFactory.getStoreKey(new DataInputStream(msgStream));
      if (header.isPutRecord()) {
        encryptionKey = header.hasEncryptionKeyRecord() ? deserializeBlobEncryptionKey(msgStream) : null;
        props = deserializeBlobProperties(msgStream);
        metadata = deserializeUserMetadata(msgStream);
        blobData = deserializeBlob(msgStream);
      } else {
        throw new IllegalArgumentException("Message cannot be a deleted record ");
      }
      if (msgInfo.getStoreKey().equals(originalKey)) {
        StoreKey newKey = storeKeyConverter.convert(Collections.singletonList(originalKey)).get(originalKey);
        if (newKey == null) {
          System.out.println("No mapping for the given key, transformed message will be null");
          transformationOutput = new TransformationOutput((Message) null);
        } else {
          MessageInfo transformedMsgInfo;
          PutMessageFormatInputStream transformedStream =
              new PutMessageFormatInputStream(newKey, encryptionKey, props, metadata,
                  new ByteBufInputStream(blobData.content(), true), blobData.getSize(), blobData.getBlobType(),
                  msgInfo.getLifeVersion());
          transformedMsgInfo = new MessageInfo.Builder(msgInfo).storeKey(newKey)
              .size(transformedStream.getSize())
              .isUndeleted(false)
              .build();
          transformationOutput = new TransformationOutput(new Message(transformedMsgInfo, transformedStream));
        }
      } else {
        throw new IllegalStateException(
            "StoreKey in log " + originalKey + " failed to match store key from Index " + msgInfo.getStoreKey());
      }
    } catch (Exception e) {
      transformationOutput = new TransformationOutput(e);
    }
    return transformationOutput;
  }
A {@link Transformer} implementation that does random key conversions, used for testing.
ambry-messageformat/src/test/java/com/github/ambry/messageformat/MessageSievingInputStreamTest.java