/** * Creates a new {@link AMQPMessage} instance from binary encoded message data. * * @param messageFormat * The Message format tag given the in Transfer that carried this message * @param data * The encoded AMQP message in an {@link ReadableBuffer} wrapper. * @param extraProperties * Broker specific extra properties that should be carried with this message * @param coreMessageObjectPools * Object pool used to accelerate some String operations. */ public AMQPMessage(long messageFormat, ReadableBuffer data, TypedProperties extraProperties, CoreMessageObjectPools coreMessageObjectPools) { this.data = data; this.messageFormat = messageFormat; this.coreMessageObjectPools = coreMessageObjectPools; this.extraProperties = extraProperties == null ? null : new TypedProperties(extraProperties); ensureMessageDataScanned(); }
/** * Retrieves the AMQP Footer encoded in the data of this message by decoding a * fresh copy from the encoded message data. Changes to the returned value are not * reflected in the value encoded in the original message. * * @return the Footer that was encoded into this AMQP Message. */ public Footer getFooter() { ensureMessageDataScanned(); ensureDataIsValid(); return scanForMessageSection(Math.max(0, remainingBodyPosition), Footer.class); }
/** * Retrieves the AMQP Section that composes the body of this message by decoding a * fresh copy from the encoded message data. Changes to the returned value are not * reflected in the value encoded in the original message. * * @return the Section that makes up the body of this message. */ public Section getBody() { ensureMessageDataScanned(); ensureDataIsValid(); // We only handle Sections of AmqpSequence, AmqpValue and Data types so we filter on those. // There could also be a Footer and no body so this will prevent a faulty return type in case // of no body or message type we don't handle. return scanForMessageSection(Math.max(0, remainingBodyPosition), AmqpSequence.class, AmqpValue.class, Data.class); }
/** * Returns a copy of the MessageAnnotations in the message if present or null. Changes to the * returned DeliveryAnnotations instance do not affect the original Message. * * @return a copy of the {@link DeliveryAnnotations} present in the message or null if non present. */ public DeliveryAnnotations getDeliveryAnnotations() { ensureMessageDataScanned(); ensureDataIsValid(); return scanForMessageSection(deliveryAnnotationsPosition, DeliveryAnnotations.class); }
/** * Returns a copy of the message Properties if one is present, changes to the returned * Properties instance do not affect the original Message. * * @return a copy of the Message Properties if one exists or null if none present. */ public Properties getProperties() { ensureMessageDataScanned(); ensureDataIsValid(); return scanForMessageSection(propertiesPosition, Properties.class); }
/** * Returns a copy of the DeliveryAnnotations in the message if present or null. Changes to the * returned MessageAnnotations instance do not affect the original Message. * * @return a copy of the {@link MessageAnnotations} present in the message or null if non present. */ public MessageAnnotations getMessageAnnotations() { ensureMessageDataScanned(); ensureDataIsValid(); return scanForMessageSection(messageAnnotationsPosition, MessageAnnotations.class); }
/** * Returns a copy of the {@link ApplicationProperties} present in the message if present or null. * Changes to the returned MessageAnnotations instance do not affect the original Message. * * @return a copy of the {@link ApplicationProperties} present in the message or null if non present. */ public ApplicationProperties getApplicationProperties() { ensureMessageDataScanned(); ensureDataIsValid(); return scanForMessageSection(applicationPropertiesPosition, ApplicationProperties.class); }
/** * Returns a copy of the message Header if one is present, changes to the returned * Header instance do not affect the original Message. * * @return a copy of the Message Header if one exists or null if none present. */ public Header getHeader() { ensureMessageDataScanned(); ensureDataIsValid(); return scanForMessageSection(headerPosition, Header.class); }
@Override public int getGroupSequence() { ensureMessageDataScanned(); if (properties != null && properties.getGroupSequence() != null) { return properties.getGroupSequence().intValue(); } else { return 0; } }
@Override public SimpleString getGroupID() { ensureMessageDataScanned(); if (properties != null && properties.getGroupId() != null) { return SimpleString.toSimpleString(properties.getGroupId(), coreMessageObjectPools == null ? null : coreMessageObjectPools.getGroupIdStringSimpleStringPool()); } else { return null; } }
@Override public void reencode() { ensureMessageDataScanned(); // The address was updated on a message with Properties so we update them // for cases where there are no properties we aren't adding a properties // section which seems wrong but this preserves previous behavior. if (properties != null && address != null) { properties.setTo(address.toString()); } encodeMessage(); scanMessageData(); messageDataScanned = true; modified = false; }
/** * Creates and returns a Proton-J MessageImpl wrapper around the message data. Changes to * the returned Message are not reflected in this message. * * @return a MessageImpl that wraps the AMQP message data in this {@link AMQPMessage} */ public MessageImpl getProtonMessage() { ensureMessageDataScanned(); ensureDataIsValid(); MessageImpl protonMessage = null; if (data != null) { protonMessage = (MessageImpl) Message.Factory.create(); data.rewind(); protonMessage.decode(data.duplicate()); } return protonMessage; }
@SuppressWarnings({ "unchecked", "rawtypes" }) private <T> T scanForMessageSection(int scanStartPosition, Class...targetTypes) { ensureMessageDataScanned(); // In cases where we parsed out enough to know the value is not encoded in the message // we can exit without doing any reads or buffer hopping. if (scanStartPosition == VALUE_NOT_PRESENT) { return null; } ReadableBuffer buffer = data.duplicate().position(0); final DecoderImpl decoder = TLSEncode.getDecoder(); buffer.position(scanStartPosition); T section = null; decoder.setBuffer(buffer); try { while (buffer.hasRemaining()) { TypeConstructor<?> constructor = decoder.readConstructor(); for (Class<?> type : targetTypes) { if (type.equals(constructor.getTypeClass())) { section = (T) constructor.readValue(); return section; } } constructor.skipValue(); } } finally { decoder.setBuffer(null); } return section; }