public static MessageDispatch createMessageDispatch(
      MessageReference reference, ServerMessage message, AMQConsumer consumer)
      throws IOException, JMSException {
    ActiveMQMessage amqMessage =
        toAMQMessage(
            reference, message, consumer.getMarshaller(), consumer.getOpenwireDestination());

    // we can use core message id for sequenceId
    amqMessage.getMessageId().setBrokerSequenceId(message.getMessageID());
    MessageDispatch md = new MessageDispatch();
    md.setConsumerId(consumer.getId());
    md.setRedeliveryCounter(reference.getDeliveryCount() - 1);
    md.setDeliverySequenceId(amqMessage.getMessageId().getBrokerSequenceId());
    md.setMessage(amqMessage);
    ActiveMQDestination destination = amqMessage.getDestination();
    md.setDestination(destination);

    return md;
  }
  private static ActiveMQMessage toAMQMessage(
      MessageReference reference,
      ServerMessage coreMessage,
      WireFormat marshaller,
      ActiveMQDestination actualDestination)
      throws IOException {
    ActiveMQMessage amqMsg = null;
    byte coreType = coreMessage.getType();
    switch (coreType) {
      case org.apache.activemq.artemis.api.core.Message.BYTES_TYPE:
        amqMsg = new ActiveMQBytesMessage();
        break;
      case org.apache.activemq.artemis.api.core.Message.MAP_TYPE:
        amqMsg = new ActiveMQMapMessage();
        break;
      case org.apache.activemq.artemis.api.core.Message.OBJECT_TYPE:
        amqMsg = new ActiveMQObjectMessage();
        break;
      case org.apache.activemq.artemis.api.core.Message.STREAM_TYPE:
        amqMsg = new ActiveMQStreamMessage();
        break;
      case org.apache.activemq.artemis.api.core.Message.TEXT_TYPE:
        amqMsg = new ActiveMQTextMessage();
        break;
      case org.apache.activemq.artemis.api.core.Message.DEFAULT_TYPE:
        amqMsg = new ActiveMQMessage();
        break;
      default:
        throw new IllegalStateException("Unknown message type: " + coreMessage.getType());
    }

    String type = coreMessage.getStringProperty(new SimpleString("JMSType"));
    if (type != null) {
      amqMsg.setJMSType(type);
    }
    amqMsg.setPersistent(coreMessage.isDurable());
    amqMsg.setExpiration(coreMessage.getExpiration());
    amqMsg.setPriority(coreMessage.getPriority());
    amqMsg.setTimestamp(coreMessage.getTimestamp());

    Long brokerInTime = (Long) coreMessage.getObjectProperty(AMQ_MSG_BROKER_IN_TIME);
    if (brokerInTime == null) {
      brokerInTime = 0L;
    }
    amqMsg.setBrokerInTime(brokerInTime);

    ActiveMQBuffer buffer = coreMessage.getBodyBufferDuplicate();
    Boolean compressProp = (Boolean) coreMessage.getObjectProperty(AMQ_MSG_COMPRESSED);
    boolean isCompressed = compressProp == null ? false : compressProp.booleanValue();
    amqMsg.setCompressed(isCompressed);

    if (buffer != null) {
      buffer.resetReaderIndex();
      byte[] bytes = null;
      synchronized (buffer) {
        if (coreType == org.apache.activemq.artemis.api.core.Message.TEXT_TYPE) {
          SimpleString text = buffer.readNullableSimpleString();
          if (text != null) {
            ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(text.length() + 4);
            OutputStream out = bytesOut;
            if (isCompressed) {
              out = new DeflaterOutputStream(out);
            }
            try (DataOutputStream dataOut = new DataOutputStream(out)) {
              MarshallingSupport.writeUTF8(dataOut, text.toString());
              bytes = bytesOut.toByteArray();
            }
          }
        } else if (coreType == org.apache.activemq.artemis.api.core.Message.MAP_TYPE) {
          TypedProperties mapData = new TypedProperties();
          // it could be a null map
          if (buffer.readableBytes() > 0) {
            mapData.decode(buffer);
            Map<String, Object> map = mapData.getMap();
            ByteArrayOutputStream out = new ByteArrayOutputStream(mapData.getEncodeSize());
            OutputStream os = out;
            if (isCompressed) {
              os = new DeflaterOutputStream(os);
            }
            try (DataOutputStream dataOut = new DataOutputStream(os)) {
              MarshallingSupport.marshalPrimitiveMap(map, dataOut);
            }
            bytes = out.toByteArray();
          }

        } else if (coreType == org.apache.activemq.artemis.api.core.Message.OBJECT_TYPE) {
          int len = buffer.readInt();
          bytes = new byte[len];
          buffer.readBytes(bytes);
          if (isCompressed) {
            ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
            try (DeflaterOutputStream out = new DeflaterOutputStream(bytesOut)) {
              out.write(bytes);
            }
            bytes = bytesOut.toByteArray();
          }
        } else if (coreType == org.apache.activemq.artemis.api.core.Message.STREAM_TYPE) {
          org.apache.activemq.util.ByteArrayOutputStream bytesOut =
              new org.apache.activemq.util.ByteArrayOutputStream();
          OutputStream out = bytesOut;
          if (isCompressed) {
            out = new DeflaterOutputStream(bytesOut);
          }
          try (DataOutputStream dataOut = new DataOutputStream(out)) {

            boolean stop = false;
            while (!stop && buffer.readable()) {
              byte primitiveType = buffer.readByte();
              switch (primitiveType) {
                case DataConstants.BOOLEAN:
                  MarshallingSupport.marshalBoolean(dataOut, buffer.readBoolean());
                  break;
                case DataConstants.BYTE:
                  MarshallingSupport.marshalByte(dataOut, buffer.readByte());
                  break;
                case DataConstants.BYTES:
                  int len = buffer.readInt();
                  byte[] bytesData = new byte[len];
                  buffer.readBytes(bytesData);
                  MarshallingSupport.marshalByteArray(dataOut, bytesData);
                  break;
                case DataConstants.CHAR:
                  char ch = (char) buffer.readShort();
                  MarshallingSupport.marshalChar(dataOut, ch);
                  break;
                case DataConstants.DOUBLE:
                  double doubleVal = Double.longBitsToDouble(buffer.readLong());
                  MarshallingSupport.marshalDouble(dataOut, doubleVal);
                  break;
                case DataConstants.FLOAT:
                  Float floatVal = Float.intBitsToFloat(buffer.readInt());
                  MarshallingSupport.marshalFloat(dataOut, floatVal);
                  break;
                case DataConstants.INT:
                  MarshallingSupport.marshalInt(dataOut, buffer.readInt());
                  break;
                case DataConstants.LONG:
                  MarshallingSupport.marshalLong(dataOut, buffer.readLong());
                  break;
                case DataConstants.SHORT:
                  MarshallingSupport.marshalShort(dataOut, buffer.readShort());
                  break;
                case DataConstants.STRING:
                  String string = buffer.readNullableString();
                  if (string == null) {
                    MarshallingSupport.marshalNull(dataOut);
                  } else {
                    MarshallingSupport.marshalString(dataOut, string);
                  }
                  break;
                default:
                  // now we stop
                  stop = true;
                  break;
              }
            }
          }
          bytes = bytesOut.toByteArray();
        } else if (coreType == org.apache.activemq.artemis.api.core.Message.BYTES_TYPE) {
          int n = buffer.readableBytes();
          bytes = new byte[n];
          buffer.readBytes(bytes);
          if (isCompressed) {
            int length = bytes.length;
            Deflater deflater = new Deflater();
            try (org.apache.activemq.util.ByteArrayOutputStream compressed =
                new org.apache.activemq.util.ByteArrayOutputStream()) {
              compressed.write(new byte[4]);
              deflater.setInput(bytes);
              deflater.finish();
              byte[] bytesBuf = new byte[1024];
              while (!deflater.finished()) {
                int count = deflater.deflate(bytesBuf);
                compressed.write(bytesBuf, 0, count);
              }
              ByteSequence byteSeq = compressed.toByteSequence();
              ByteSequenceData.writeIntBig(byteSeq, length);
              bytes = Arrays.copyOfRange(byteSeq.data, 0, byteSeq.length);
            } finally {
              deflater.end();
            }
          }
        } else {
          int n = buffer.readableBytes();
          bytes = new byte[n];
          buffer.readBytes(bytes);
          if (isCompressed) {
            try (ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
                DeflaterOutputStream out = new DeflaterOutputStream(bytesOut)) {
              out.write(bytes);
              bytes = bytesOut.toByteArray();
            }
          }
        }

        buffer.resetReaderIndex(); // this is important for topics as the buffer
        // may be read multiple times
      }

      if (bytes != null) {
        ByteSequence content = new ByteSequence(bytes);
        amqMsg.setContent(content);
      }
    }

    // we need check null because messages may come from other clients
    // and those amq specific attribute may not be set.
    Long arrival = (Long) coreMessage.getObjectProperty(AMQ_MSG_ARRIVAL);
    if (arrival == null) {
      // messages from other sources (like core client) may not set this prop
      arrival = 0L;
    }
    amqMsg.setArrival(arrival);

    String brokerPath = (String) coreMessage.getObjectProperty(AMQ_MSG_BROKER_PATH);
    if (brokerPath != null && brokerPath.isEmpty()) {
      String[] brokers = brokerPath.split(",");
      BrokerId[] bids = new BrokerId[brokers.length];
      for (int i = 0; i < bids.length; i++) {
        bids[i] = new BrokerId(brokers[i]);
      }
      amqMsg.setBrokerPath(bids);
    }

    String clusterPath = (String) coreMessage.getObjectProperty(AMQ_MSG_CLUSTER);
    if (clusterPath != null && clusterPath.isEmpty()) {
      String[] cluster = clusterPath.split(",");
      BrokerId[] bids = new BrokerId[cluster.length];
      for (int i = 0; i < bids.length; i++) {
        bids[i] = new BrokerId(cluster[i]);
      }
      amqMsg.setCluster(bids);
    }

    Integer commandId = (Integer) coreMessage.getObjectProperty(AMQ_MSG_COMMAND_ID);
    if (commandId == null) {
      commandId = -1;
    }
    amqMsg.setCommandId(commandId);

    SimpleString corrId = (SimpleString) coreMessage.getObjectProperty("JMSCorrelationID");
    if (corrId != null) {
      amqMsg.setCorrelationId(corrId.toString());
    }

    byte[] dsBytes = (byte[]) coreMessage.getObjectProperty(AMQ_MSG_DATASTRUCTURE);
    if (dsBytes != null) {
      ByteSequence seq = new ByteSequence(dsBytes);
      DataStructure ds = (DataStructure) marshaller.unmarshal(seq);
      amqMsg.setDataStructure(ds);
    }

    amqMsg.setDestination(OpenWireUtil.toAMQAddress(coreMessage, actualDestination));

    Object value = coreMessage.getObjectProperty(AMQ_MSG_GROUP_ID);
    if (value != null) {
      String groupId = value.toString();
      amqMsg.setGroupID(groupId);
    }

    Integer groupSequence = (Integer) coreMessage.getObjectProperty(AMQ_MSG_GROUP_SEQUENCE);
    if (groupSequence == null) {
      groupSequence = -1;
    }
    amqMsg.setGroupSequence(groupSequence);

    MessageId mid = null;
    byte[] midBytes = (byte[]) coreMessage.getObjectProperty(AMQ_MSG_MESSAGE_ID);
    if (midBytes != null) {
      ByteSequence midSeq = new ByteSequence(midBytes);
      mid = (MessageId) marshaller.unmarshal(midSeq);
    } else {
      mid = new MessageId(UUIDGenerator.getInstance().generateStringUUID() + ":-1");
    }

    amqMsg.setMessageId(mid);

    byte[] origDestBytes = (byte[]) coreMessage.getObjectProperty(AMQ_MSG_ORIG_DESTINATION);
    if (origDestBytes != null) {
      ActiveMQDestination origDest =
          (ActiveMQDestination) marshaller.unmarshal(new ByteSequence(origDestBytes));
      amqMsg.setOriginalDestination(origDest);
    }

    byte[] origTxIdBytes = (byte[]) coreMessage.getObjectProperty(AMQ_MSG_ORIG_TXID);
    if (origTxIdBytes != null) {
      TransactionId origTxId =
          (TransactionId) marshaller.unmarshal(new ByteSequence(origTxIdBytes));
      amqMsg.setOriginalTransactionId(origTxId);
    }

    byte[] producerIdBytes = (byte[]) coreMessage.getObjectProperty(AMQ_MSG_PRODUCER_ID);
    if (producerIdBytes != null) {
      ProducerId producerId = (ProducerId) marshaller.unmarshal(new ByteSequence(producerIdBytes));
      amqMsg.setProducerId(producerId);
    }

    byte[] marshalledBytes = (byte[]) coreMessage.getObjectProperty(AMQ_MSG_MARSHALL_PROP);
    if (marshalledBytes != null) {
      amqMsg.setMarshalledProperties(new ByteSequence(marshalledBytes));
    }

    amqMsg.setRedeliveryCounter(reference.getDeliveryCount() - 1);

    byte[] replyToBytes = (byte[]) coreMessage.getObjectProperty(AMQ_MSG_REPLY_TO);
    if (replyToBytes != null) {
      ActiveMQDestination replyTo =
          (ActiveMQDestination) marshaller.unmarshal(new ByteSequence(replyToBytes));
      amqMsg.setReplyTo(replyTo);
    }

    String userId = (String) coreMessage.getObjectProperty(AMQ_MSG_USER_ID);
    if (userId != null) {
      amqMsg.setUserID(userId);
    }

    Boolean isDroppable = (Boolean) coreMessage.getObjectProperty(AMQ_MSG_DROPPABLE);
    if (isDroppable != null) {
      amqMsg.setDroppable(isDroppable);
    }

    SimpleString dlqCause =
        (SimpleString) coreMessage.getObjectProperty(AMQ_MSG_DLQ_DELIVERY_FAILURE_CAUSE_PROPERTY);
    if (dlqCause != null) {
      try {
        amqMsg.setStringProperty(
            ActiveMQMessage.DLQ_DELIVERY_FAILURE_CAUSE_PROPERTY, dlqCause.toString());
      } catch (JMSException e) {
        throw new IOException("failure to set dlq property " + dlqCause, e);
      }
    }

    Set<SimpleString> props = coreMessage.getPropertyNames();
    if (props != null) {
      for (SimpleString s : props) {
        String keyStr = s.toString();
        if (keyStr.startsWith("_AMQ") || keyStr.startsWith("__HDR_")) {
          continue;
        }
        Object prop = coreMessage.getObjectProperty(s);
        try {
          if (prop instanceof SimpleString) {
            amqMsg.setObjectProperty(s.toString(), prop.toString());
          } else {
            amqMsg.setObjectProperty(s.toString(), prop);
          }
        } catch (JMSException e) {
          throw new IOException("exception setting property " + s + " : " + prop, e);
        }
      }
    }
    try {
      amqMsg.onSend();
      amqMsg.setCompressed(isCompressed);
    } catch (JMSException e) {
      throw new IOException("Failed to covert to Openwire message", e);
    }
    return amqMsg;
  }
 @Override
 public int getDeliveryCount() {
   return ref.getDeliveryCount();
 }