@Override
  public RequestMessage deserializeRequest(final ByteBuf msg) throws SerializationException {
    try {
      final Kryo kryo = kryoThreadLocal.get();
      final byte[] payload = new byte[msg.readableBytes()];
      msg.readBytes(payload);
      try (final Input input = new Input(payload)) {
        // by the time the message gets here, the mime length/type have been already read, so this
        // part just
        // needs to process the payload.
        final UUID id = kryo.readObject(input, UUID.class);
        final String processor = input.readString();
        final String op = input.readString();

        final RequestMessage.Builder builder =
            RequestMessage.build(op).overrideRequestId(id).processor(processor);

        final Map<String, Object> args = kryo.readObject(input, HashMap.class);
        args.forEach(builder::addArg);
        return builder.create();
      }
    } catch (Exception ex) {
      logger.warn(
          "Request [{}] could not be deserialized by {}.",
          msg,
          GryoMessageSerializerV1d0.class.getName());
      throw new SerializationException(ex);
    }
  }
  @Override
  public ResponseMessage deserializeResponse(final ByteBuf msg) throws SerializationException {
    try {
      final Kryo kryo = kryoThreadLocal.get();
      final byte[] payload = new byte[msg.readableBytes()];
      msg.readBytes(payload);
      try (final Input input = new Input(payload)) {
        final UUID requestId = kryo.readObjectOrNull(input, UUID.class);
        final int status = input.readShort();
        final String statusMsg = input.readString();
        final Map<String, Object> statusAttributes =
            (Map<String, Object>) kryo.readClassAndObject(input);
        final Object result = kryo.readClassAndObject(input);
        final Map<String, Object> metaAttributes =
            (Map<String, Object>) kryo.readClassAndObject(input);

        return ResponseMessage.build(requestId)
            .code(ResponseStatusCode.getFromValue(status))
            .statusMessage(statusMsg.intern())
            .statusAttributes(statusAttributes)
            .result(result)
            .responseMetaData(metaAttributes)
            .create();
      }
    } catch (Exception ex) {
      logger.warn(
          "Response [{}] could not be deserialized by {}.",
          msg,
          GryoMessageSerializerV1d0.class.getName());
      throw new SerializationException(ex);
    }
  }
  @Override
  public ByteBuf serializeResponseAsBinary(
      final ResponseMessage responseMessage, final ByteBufAllocator allocator)
      throws SerializationException {
    ByteBuf encodedMessage = null;
    try {
      final Kryo kryo = kryoThreadLocal.get();
      try (final OutputStream baos = new ByteArrayOutputStream()) {
        final Output output = new Output(baos);

        // request id - if present
        kryo.writeObjectOrNull(
            output,
            responseMessage.getRequestId() != null ? responseMessage.getRequestId() : null,
            UUID.class);

        // status
        output.writeShort(responseMessage.getStatus().getCode().getValue());
        output.writeString(responseMessage.getStatus().getMessage());
        kryo.writeClassAndObject(output, responseMessage.getStatus().getAttributes());

        // result
        kryo.writeClassAndObject(
            output,
            serializeToString
                ? serializeResultToString(responseMessage)
                : responseMessage.getResult().getData());
        kryo.writeClassAndObject(output, responseMessage.getResult().getMeta());

        final long size = output.total();
        if (size > Integer.MAX_VALUE)
          throw new SerializationException(
              String.format("Message size of %s exceeds allocatable space", size));

        encodedMessage = allocator.buffer((int) output.total());
        encodedMessage.writeBytes(output.toBytes());
      }

      return encodedMessage;
    } catch (Exception ex) {
      if (encodedMessage != null) ReferenceCountUtil.release(encodedMessage);

      logger.warn(
          "Response [{}] could not be serialized by {}.",
          responseMessage.toString(),
          GryoMessageSerializerV1d0.class.getName());
      throw new SerializationException(ex);
    }
  }
  @Override
  public ByteBuf serializeRequestAsBinary(
      final RequestMessage requestMessage, final ByteBufAllocator allocator)
      throws SerializationException {
    ByteBuf encodedMessage = null;
    try {
      final Kryo kryo = kryoThreadLocal.get();
      try (final OutputStream baos = new ByteArrayOutputStream()) {
        final Output output = new Output(baos);
        final String mimeType = serializeToString ? MIME_TYPE_STRINGD : MIME_TYPE;
        output.writeByte(mimeType.length());
        output.write(mimeType.getBytes(UTF8));

        kryo.writeObject(output, requestMessage.getRequestId());
        output.writeString(requestMessage.getProcessor());
        output.writeString(requestMessage.getOp());
        kryo.writeObject(output, requestMessage.getArgs());

        final long size = output.total();
        if (size > Integer.MAX_VALUE)
          throw new SerializationException(
              String.format("Message size of %s exceeds allocatable space", size));

        encodedMessage = allocator.buffer((int) size);
        encodedMessage.writeBytes(output.toBytes());
      }

      return encodedMessage;
    } catch (Exception ex) {
      if (encodedMessage != null) ReferenceCountUtil.release(encodedMessage);

      logger.warn(
          "Request [{}] could not be serialized by {}.",
          requestMessage.toString(),
          GryoMessageSerializerV1d0.class.getName());
      throw new SerializationException(ex);
    }
  }
  @Override
  public Kryo createMapper() {
    final Kryo kryo =
        new Kryo(new GryoClassResolver(), new MapReferenceResolver(), new DefaultStreamFactory());
    kryo.addDefaultSerializer(Map.Entry.class, new EntrySerializer());
    kryo.setRegistrationRequired(registrationRequired);
    kryo.setReferences(referenceTracking);

    serializationList.forEach(
        p -> {
          final Function<Kryo, Serializer> serializer = p.getValue1();
          if (null == serializer)
            kryo.register(p.getValue0(), kryo.getDefaultSerializer(p.getValue0()), p.getValue2());
          else kryo.register(p.getValue0(), serializer.apply(kryo), p.getValue2());
        });
    return kryo;
  }