/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.activemq.artemis.api.core; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Set; import io.netty.buffer.ByteBuf; import org.apache.activemq.artemis.core.persistence.Persister; /** * A Message is a routable instance that has a payload. * <p> * The payload (the "body") is opaque to the messaging system. A Message also has a fixed set of * headers (required by the messaging system) and properties (defined by the users) that can be used * by the messaging system to route the message (e.g. to ensure it matches a queue filter). * <h2>Message Properties</h2> * <p> * Message can contain properties specified by the users. It is possible to convert from some types * to other types as specified by the following table: * <pre> * | | boolean byte short int long float double String byte[] * |---------------------------------------------------------------- * |boolean | X X * |byte | X X X X X * |short | X X X X * |int | X X X * |long | X X * |float | X X X * |double | X X * |String | X X X X X X X X * |byte[] | X * |----------------------------------------------------------------- * </pre> * <p> * If conversion is not allowed (for example calling {@code getFloatProperty} on a property set a * {@code boolean}), a {@link ActiveMQPropertyConversionException} will be thrown. * * * User cases that will be covered by Message * * Receiving a buffer: * * Message encode = new CoreMessage(); // or any other implementation * encode.receiveBuffer(buffer); * * * Sending to a buffer: * * Message encode; * size = encode.getEncodeSize(); * encode.encodeDirectly(bufferOutput); * */ public interface Message { // This is an estimate of how much memory a Message takes up, exclusing body and properties // Note, it is only an estimate, it's not possible to be entirely sure with Java // This figure is calculated using the test utilities in org.apache.activemq.tests.unit.util.sizeof // The value is somewhat higher on 64 bit architectures, probably due to different alignment int memoryOffset = 352; SimpleString HDR_ROUTE_TO_IDS = new SimpleString("_AMQ_ROUTE_TO"); SimpleString HDR_SCALEDOWN_TO_IDS = new SimpleString("_AMQ_SCALEDOWN_TO"); SimpleString HDR_ROUTE_TO_ACK_IDS = new SimpleString("_AMQ_ACK_ROUTE_TO"); // used by the bridges to set duplicates SimpleString HDR_BRIDGE_DUPLICATE_ID = new SimpleString("_AMQ_BRIDGE_DUP"); /** * the actual time the message was expired. * * * */ SimpleString HDR_ACTUAL_EXPIRY_TIME = new SimpleString("_AMQ_ACTUAL_EXPIRY"); /** * The original address of a message when a message is transferred through DLQ or expiry */ SimpleString HDR_ORIGINAL_ADDRESS = new SimpleString("_AMQ_ORIG_ADDRESS"); /** * The original address of a message when a message is transferred through DLQ or expiry */ SimpleString HDR_ORIGINAL_QUEUE = new SimpleString("_AMQ_ORIG_QUEUE"); /** * The original message ID before th emessage was transferred. */ SimpleString HDR_ORIG_MESSAGE_ID = new SimpleString("_AMQ_ORIG_MESSAGE_ID"); /** * For the Message Grouping feature. */ SimpleString HDR_GROUP_ID = new SimpleString("_AMQ_GROUP_ID"); /** * to determine if the Large Message was compressed. */ SimpleString HDR_LARGE_COMPRESSED = new SimpleString("_AMQ_LARGE_COMPRESSED"); /** * The body size of a large message before it was compressed. */ SimpleString HDR_LARGE_BODY_SIZE = new SimpleString("_AMQ_LARGE_SIZE"); /** * To be used with Scheduled Delivery. */ SimpleString HDR_SCHEDULED_DELIVERY_TIME = new SimpleString("_AMQ_SCHED_DELIVERY"); /** * To be used with duplicate detection. */ SimpleString HDR_DUPLICATE_DETECTION_ID = new SimpleString("_AMQ_DUPL_ID"); /** * To be used with Last value queues. */ SimpleString HDR_LAST_VALUE_NAME = new SimpleString("_AMQ_LVQ_NAME"); /** * To define the mime-type of body messages. Mainly for stomp but it could be informed on any message for user purposes. */ SimpleString HDR_CONTENT_TYPE = new SimpleString("_AMQ_CONTENT_TYPE"); /** * The name of the validated user who sent the message. Useful for auditing. */ SimpleString HDR_VALIDATED_USER = new SimpleString("_AMQ_VALIDATED_USER"); /** * The Routing Type for this message. Ensures that this message is only routed to queues with matching routing type. */ SimpleString HDR_ROUTING_TYPE = new SimpleString("_AMQ_ROUTING_TYPE"); byte DEFAULT_TYPE = 0; byte OBJECT_TYPE = 2; byte TEXT_TYPE = 3; byte BYTES_TYPE = 4; byte MAP_TYPE = 5; byte STREAM_TYPE = 6; default void cleanupInternalProperties() { // only on core } default RoutingType getRoutingType() { return null; } default Message setRoutingType(RoutingType routingType) { return this; } default SimpleString getLastValueProperty() { return null; } /** * @deprecated do not use this, use through ICoreMessage or ClientMessage */ @Deprecated default InputStream getBodyInputStream() { return null; } /** * @deprecated do not use this, use through ICoreMessage or ClientMessage */ @Deprecated default ActiveMQBuffer getBodyBuffer() { return null; } /** * @deprecated do not use this, use through ICoreMessage or ClientMessage */ @Deprecated default byte getType() { return (byte)0; } /** * @deprecated do not use this, use through ICoreMessage or ClientMessage */ @Deprecated default Message setType(byte type) { return this; } void messageChanged(); /** Used to calculate what is the delivery time. * Return null if not scheduled. */ Long getScheduledDeliveryTime(); default Message setScheduledDeliveryTime(Long time) { return this; } /** Context can be used by the application server to inject extra control, like a protocol specific on the server. * There is only one per Object, use it wisely! * * Note: the intent of this was to replace PageStore reference on Message, but it will be later increased by adidn a ServerPojo * */ RefCountMessageListener getContext(); default SimpleString getGroupID() { return null; } SimpleString getReplyTo(); Message setReplyTo(SimpleString address); Message setContext(RefCountMessageListener context); /** The buffer will belong to this message, until release is called. */ Message setBuffer(ByteBuf buffer); ByteBuf getBuffer(); /** It will generate a new instance of the message encode, being a deep copy, new properties, new everything */ Message copy(); /** It will generate a new instance of the message encode, being a deep copy, new properties, new everything */ Message copy(long newID); default boolean acceptsConsumer(long uniqueConsumerID) { return true; } default void rejectConsumer(long uniqueConsumerID) { } /** * Returns the messageID. * <br> * The messageID is set when the message is handled by the server. */ long getMessageID(); // used for NO-LOCAL: mainly for AMQP default Message setConnectionID(String connectionID) { return this; } default String getConnectionID() { return null; } Message setMessageID(long id); default boolean isLargeMessage() { return false; } /** * Returns the expiration time of this message. */ long getExpiration(); /** * Sets the expiration of this message. * * @param expiration expiration time */ Message setExpiration(long expiration); /** * Returns whether this message is expired or not. */ default boolean isExpired() { if (getExpiration() == 0) { return false; } return System.currentTimeMillis() - getExpiration() >= 0; } /** * * This represents historically the JMSMessageID. * We had in the past used this for the MessageID that was sent on core messages... * * later on when we added AMQP this name clashed with AMQPMessage.getUserID(); * * @return the user id */ Object getUserID(); Message setUserID(Object userID); default String getValidatedUserID() { return null; } default Message setValidatedUserID(String validatedUserID) { return this; } /** * Returns whether this message is durable or not. */ boolean isDurable(); /** * Sets whether this message is durable or not. * * @param durable {@code true} to flag this message as durable, {@code false} else */ Message setDurable(boolean durable); Persister<Message> getPersister(); String getAddress(); Message setAddress(String address); SimpleString getAddressSimpleString(); Message setAddress(SimpleString address); long getTimestamp(); Message setTimestamp(long timestamp); /** * Returns the message priority. * <p> * Values range from 0 (less priority) to 9 (more priority) inclusive. */ byte getPriority(); /** * Sets the message priority. * <p> * Value must be between 0 and 9 inclusive. * * @param priority the new message priority */ Message setPriority(byte priority); /** Used to receive this message from an encoded medium buffer */ void receiveBuffer(ByteBuf buffer); /** Used to send this message to an encoded medium buffer. * @param buffer the buffer used. * @param deliveryCount Some protocols (AMQP) will have this as part of the message. */ void sendBuffer(ByteBuf buffer, int deliveryCount); int getPersistSize(); void persist(ActiveMQBuffer targetRecord); void reloadPersistence(ActiveMQBuffer record); default void releaseBuffer() { ByteBuf buffer = getBuffer(); if (buffer != null) { buffer.release(); } setBuffer(null); } default void reencode() { // only valid probably on AMQP } default void referenceOriginalMessage(final Message original, String originalQueue) { String queueOnMessage = original.getAnnotationString(Message.HDR_ORIGINAL_QUEUE); if (queueOnMessage != null) { setAnnotation(Message.HDR_ORIGINAL_QUEUE, queueOnMessage); } else if (originalQueue != null) { setAnnotation(Message.HDR_ORIGINAL_QUEUE, originalQueue); } Object originalID = original.getAnnotation(Message.HDR_ORIG_MESSAGE_ID); if (originalID != null) { setAnnotation(Message.HDR_ORIGINAL_ADDRESS, original.getAnnotationString(Message.HDR_ORIGINAL_ADDRESS)); setAnnotation(Message.HDR_ORIG_MESSAGE_ID, originalID); } else { setAnnotation(Message.HDR_ORIGINAL_ADDRESS, original.getAddress()); setAnnotation(Message.HDR_ORIG_MESSAGE_ID, original.getMessageID()); } // reset expiry setExpiration(0); } /** * it will translate a property named HDR_DUPLICATE_DETECTION_ID. * @return */ default byte[] getDuplicateIDBytes() { Object duplicateID = getDuplicateProperty(); if (duplicateID == null) { return null; } else { if (duplicateID instanceof SimpleString) { return ((SimpleString) duplicateID).getData(); } else if (duplicateID instanceof String) { return new SimpleString(duplicateID.toString()).getData(); } else { return (byte[]) duplicateID; } } } default Object getDuplicateProperty() { return null; } Message putBooleanProperty(String key, boolean value); Message putByteProperty(String key, byte value); Message putBytesProperty(String key, byte[] value); Message putShortProperty(String key, short value); Message putCharProperty(String key, char value); Message putIntProperty(String key, int value); Message putLongProperty(String key, long value); Message putFloatProperty(String key, float value); Message putDoubleProperty(String key, double value); Message putBooleanProperty(SimpleString key, boolean value); Message putByteProperty(SimpleString key, byte value); Message putBytesProperty(SimpleString key, byte[] value); Message putShortProperty(SimpleString key, short value); Message putCharProperty(SimpleString key, char value); Message putIntProperty(SimpleString key, int value); Message putLongProperty(SimpleString key, long value); Message putFloatProperty(SimpleString key, float value); Message putDoubleProperty(SimpleString key, double value); /** * Puts a String property in this message. * * @param key property name * @param value property value */ Message putStringProperty(String key, String value); Message putObjectProperty(String key, Object value) throws ActiveMQPropertyConversionException; Message putObjectProperty(SimpleString key, Object value) throws ActiveMQPropertyConversionException; Object removeProperty(String key); boolean containsProperty(String key); Boolean getBooleanProperty(String key) throws ActiveMQPropertyConversionException; Byte getByteProperty(String key) throws ActiveMQPropertyConversionException; Double getDoubleProperty(String key) throws ActiveMQPropertyConversionException; Integer getIntProperty(String key) throws ActiveMQPropertyConversionException; Long getLongProperty(String key) throws ActiveMQPropertyConversionException; Object getObjectProperty(String key); Short getShortProperty(String key) throws ActiveMQPropertyConversionException; Float getFloatProperty(String key) throws ActiveMQPropertyConversionException; String getStringProperty(String key) throws ActiveMQPropertyConversionException; SimpleString getSimpleStringProperty(String key) throws ActiveMQPropertyConversionException; byte[] getBytesProperty(String key) throws ActiveMQPropertyConversionException; Object removeProperty(SimpleString key); boolean containsProperty(SimpleString key); Boolean getBooleanProperty(SimpleString key) throws ActiveMQPropertyConversionException; Byte getByteProperty(SimpleString key) throws ActiveMQPropertyConversionException; Double getDoubleProperty(SimpleString key) throws ActiveMQPropertyConversionException; Integer getIntProperty(SimpleString key) throws ActiveMQPropertyConversionException; Long getLongProperty(SimpleString key) throws ActiveMQPropertyConversionException; Object getObjectProperty(SimpleString key); default Object removeAnnotation(SimpleString key) { return removeProperty(key); } default String getAnnotationString(SimpleString key) { Object value = getAnnotation(key); if (value != null) { return value.toString(); } else { return null; } } Object getAnnotation(SimpleString key); /** Callers must call {@link #reencode()} in order to be sent to clients */ default Message setAnnotation(SimpleString key, Object value) { putObjectProperty(key, value); return this; } Short getShortProperty(SimpleString key) throws ActiveMQPropertyConversionException; Float getFloatProperty(SimpleString key) throws ActiveMQPropertyConversionException; String getStringProperty(SimpleString key) throws ActiveMQPropertyConversionException; SimpleString getSimpleStringProperty(SimpleString key) throws ActiveMQPropertyConversionException; byte[] getBytesProperty(SimpleString key) throws ActiveMQPropertyConversionException; Message putStringProperty(SimpleString key, SimpleString value); /** * Returns the size of the <em>encoded</em> message. */ int getEncodeSize(); /** * Returns all the names of the properties for this message. */ Set<SimpleString> getPropertyNames(); int getRefCount(); int incrementRefCount() throws Exception; int decrementRefCount() throws Exception; int incrementDurableRefCount(); int decrementDurableRefCount(); /** * @return Returns the message in Map form, useful when encoding to JSON */ default Map<String, Object> toMap() { Map map = toPropertyMap(); map.put("messageID", getMessageID()); Object userID = getUserID(); if (getUserID() != null) { map.put("userID", "ID:" + userID.toString()); } map.put("address", getAddress()); map.put("durable", isDurable()); map.put("expiration", getExpiration()); map.put("timestamp", getTimestamp()); map.put("priority", (int)getPriority()); return map; } /** * @return Returns the message properties in Map form, useful when encoding to JSON */ default Map<String, Object> toPropertyMap() { Map map = new HashMap<>(); for (SimpleString name : getPropertyNames()) { map.put(name.toString(), getObjectProperty(name.toString())); } return map; } /** This should make you convert your message into Core format. */ ICoreMessage toCore(); int getMemoryEstimate(); }