/**
*
* Copyright 2004 Protique Ltd
*
* Licensed 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.activemq.message;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.StringTokenizer;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageFormatException;
import javax.jms.MessageNotWriteableException;
import org.activemq.io.util.ByteArray;
import org.activemq.io.util.ByteArrayCompression;
import org.activemq.io.util.MemoryManageable;
import org.activemq.service.MessageIdentity;
import org.activemq.util.IdGenerator;
/**
* The <CODE>Message</CODE> interface is the root interface of all JMS
* messages. It defines the message header and the <CODE>acknowledge</CODE>
* method used for all messages.
* <p/>
* <P>Most message-oriented middleware (MOM) products treat messages as
* lightweight entities that consist
* of a header and a payload. The header contains fields used for message
* routing and identification; the payload contains the application data
* being sent.
* <p/>
* <P>Within this general form, the definition of a message varies
* significantly across products. It would be quite difficult for the JMS API
* to support all of these message models.
* <p/>
* <P>With this in mind, the JMS message model has the following goals:
* <UL>
* <LI>Provide a single, unified message API
* <LI>Provide an API suitable for creating messages that match the
* format used by provider-native messaging applications
* <LI>Support the development of heterogeneous applications that span
* operating systems, machine architectures, and computer languages
* <LI>Support messages containing objects in the Java programming language
* ("Java objects")
* <LI>Support messages containing Extensible Markup Language (XML) pages
* </UL>
* <p/>
* <P>JMS messages are composed of the following parts:
* <UL>
* <LI>Header - All messages support the same set of header fields.
* Header fields contain values used by both clients and providers to
* identify and route messages.
* <LI>Properties - Each message contains a built-in facility for supporting
* application-defined property values. Properties provide an efficient
* mechanism for supporting application-defined message filtering.
* <LI>Body - The JMS API defines several types of message body, which cover
* the majority of messaging styles currently in use.
* </UL>
* <p/>
* <H4>Message Bodies</H4>
* <p/>
* <P>The JMS API defines five types of message body:
* <UL>
* <LI>Stream - A <CODE>StreamMessage</CODE> object's message body contains
* a stream of primitive values in the Java programming
* language ("Java primitives"). It is filled and read sequentially.
* <LI>Map - A <CODE>MapMessage</CODE> object's message body contains a set
* of name-value pairs, where names are <CODE>String</CODE>
* objects, and values are Java primitives. The entries can be accessed
* sequentially or randomly by name. The order of the entries is
* undefined.
* <LI>Text - A <CODE>TextMessage</CODE> object's message body contains a
* <CODE>java.lang.String</CODE> object. This message type can be used
* to transport plain-text messages, and XML messages.
* <LI>Object - An <CODE>ObjectMessage</CODE> object's message body contains
* a <CODE>Serializable</CODE> Java object.
* <LI>Bytes - A <CODE>BytesMessage</CODE> object's message body contains a
* stream of uninterpreted bytes. This message type is for
* literally encoding a body to match an existing message format. In
* many cases, it is possible to use one of the other body types,
* which are easier to use. Although the JMS API allows the use of
* message properties with byte messages, they are typically not used,
* since the inclusion of properties may affect the format.
* </UL>
* <p/>
* <H4>Message Headers</H4>
* <p/>
* <P>The <CODE>JMSCorrelationID</CODE> header field is used for linking one
* message with
* another. It typically links a reply message with its requesting message.
* <p/>
* <P><CODE>JMSCorrelationID</CODE> can hold a provider-specific message ID,
* an application-specific <CODE>String</CODE> object, or a provider-native
* <CODE>byte[]</CODE> value.
* <p/>
* <H4>Message Properties</H4>
* <p/>
* <P>A <CODE>Message</CODE> object contains a built-in facility for supporting
* application-defined property values. In effect, this provides a mechanism
* for adding application-specific header fields to a message.
* <p/>
* <P>Properties allow an application, via message selectors, to have a JMS
* provider select, or filter, messages on its behalf using
* application-specific criteria.
* <p/>
* <P>Property names must obey the rules for a message selector identifier.
* Property names must not be null, and must not be empty strings. If a property
* name is set and it is either null or an empty string, an
* <CODE>IllegalArgumentException</CODE> must be thrown.
* <p/>
* <P>Property values can be <CODE>boolean</CODE>, <CODE>byte</CODE>,
* <CODE>short</CODE>, <CODE>int</CODE>, <CODE>long</CODE>, <CODE>float</CODE>,
* <CODE>double</CODE>, and <CODE>String</CODE>.
* <p/>
* <P>Property values are set prior to sending a message. When a client
* receives a message, its properties are in read-only mode. If a
* client attempts to set properties at this point, a
* <CODE>MessageNotWriteableException</CODE> is thrown. If
* <CODE>clearProperties</CODE> is called, the properties can now be both
* read from and written to. Note that header fields are distinct from
* properties. Header fields are never in read-only mode.
* <p/>
* <P>A property value may duplicate a value in a message's body, or it may
* not. Although JMS does not define a policy for what should or should not
* be made a property, application developers should note that JMS providers
* will likely handle data in a message's body more efficiently than data in
* a message's properties. For best performance, applications should use
* message properties only when they need to customize a message's header.
* The primary reason for doing this is to support customized message
* selection.
* <p/>
* <P>Message properties support the following conversion table. The marked
* cases must be supported. The unmarked cases must throw a
* <CODE>JMSException</CODE>. The <CODE>String</CODE>-to-primitive conversions
* may throw a runtime exception if the
* primitive's <CODE>valueOf</CODE> method does not accept the
* <CODE>String</CODE> as a valid representation of the primitive.
* <p/>
* <P>A value written as the row type can be read as the column type.
* <p/>
* <PRE>
* | | boolean byte short int long float double String
* |----------------------------------------------------------
* |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
* |----------------------------------------------------------
* </PRE>
* <p/>
* <P>In addition to the type-specific set/get methods for properties, JMS
* provides the <CODE>setObjectProperty</CODE> and
* <CODE>getObjectProperty</CODE> methods. These support the same set of
* property types using the objectified primitive values. Their purpose is
* to allow the decision of property type to made at execution time rather
* than at compile time. They support the same property value conversions.
* <p/>
* <P>The <CODE>setObjectProperty</CODE> method accepts values of class
* <CODE>Boolean</CODE>, <CODE>Byte</CODE>, <CODE>Short</CODE>,
* <CODE>Integer</CODE>, <CODE>Long</CODE>, <CODE>Float</CODE>,
* <CODE>Double</CODE>, and <CODE>String</CODE>. An attempt
* to use any other class must throw a <CODE>JMSException</CODE>.
* <p/>
* <P>The <CODE>getObjectProperty</CODE> method only returns values of class
* <CODE>Boolean</CODE>, <CODE>Byte</CODE>, <CODE>Short</CODE>,
* <CODE>Integer</CODE>, <CODE>Long</CODE>, <CODE>Float</CODE>,
* <CODE>Double</CODE>, and <CODE>String</CODE>.
* <p/>
* <P>The order of property values is not defined. To iterate through a
* message's property values, use <CODE>getPropertyNames</CODE> to retrieve
* a property name enumeration and then use the various property get methods
* to retrieve their values.
* <p/>
* <P>A message's properties are deleted by the <CODE>clearProperties</CODE>
* method. This leaves the message with an empty set of properties.
* <p/>
* <P>Getting a property value for a name which has not been set returns a
* null value. Only the <CODE>getStringProperty</CODE> and
* <CODE>getObjectProperty</CODE> methods can return a null value.
* Attempting to read a null value as a primitive type must be treated as
* calling the primitive's corresponding <CODE>valueOf(String)</CODE>
* conversion method with a null value.
* <p/>
* <P>The JMS API reserves the <CODE>JMSX</CODE> property name prefix for JMS
* defined properties.
* The full set of these properties is defined in the Java Message Service
* specification. New JMS defined properties may be added in later versions
* of the JMS API. Support for these properties is optional. The
* <CODE>String[] ConnectionMetaData.getJMSXPropertyNames</CODE> method
* returns the names of the JMSX properties supported by a connection.
* <p/>
* <P>JMSX properties may be referenced in message selectors whether or not
* they are supported by a connection. If they are not present in a
* message, they are treated like any other absent property.
* <p/>
* <P>JMSX properties defined in the specification as "set by provider on
* send" are available to both the producer and the consumers of the message.
* JMSX properties defined in the specification as "set by provider on
* receive" are available only to the consumers.
* <p/>
* <P><CODE>JMSXGroupID</CODE> and <CODE>JMSXGroupSeq</CODE> are standard
* properties that clients
* should use if they want to group messages. All providers must support them.
* Unless specifically noted, the values and semantics of the JMSX properties
* are undefined.
* <p/>
* <P>The JMS API reserves the <CODE>JMS_<I>vendor_name</I></CODE> property
* name prefix for provider-specific properties. Each provider defines its own
* value for <CODE><I>vendor_name</I></CODE>. This is the mechanism a JMS
* provider uses to make its special per-message services available to a JMS
* client.
* <p/>
* <P>The purpose of provider-specific properties is to provide special
* features needed to integrate JMS clients with provider-native clients in a
* single JMS application. They should not be used for messaging between JMS
* clients.
* <p/>
* <H4>Provider Implementations of JMS Message Interfaces</H4>
* <p/>
* <P>The JMS API provides a set of message interfaces that define the JMS
* message
* model. It does not provide implementations of these interfaces.
* <p/>
* <P>Each JMS provider supplies a set of message factories with its
* <CODE>Session</CODE> object for creating instances of messages. This allows
* a provider to use message implementations tailored to its specific needs.
* <p/>
* <P>A provider must be prepared to accept message implementations that are
* not its own. They may not be handled as efficiently as its own
* implementation; however, they must be handled.
* <p/>
* <P>Note the following exception case when a provider is handling a foreign
* message implementation. If the foreign message implementation contains a
* <CODE>JMSReplyTo</CODE> header field that is set to a foreign destination
* implementation, the provider is not required to handle or preserve the
* value of this header field.
* <p/>
* <H4>Message Selectors</H4>
* <p/>
* <P>A JMS message selector allows a client to specify, by
* header field references and property references, the
* messages it is interested in. Only messages whose header
* and property values
* match the
* selector are delivered. What it means for a message not to be delivered
* depends on the <CODE>MessageConsumer</CODE> being used (see
* {@link javax.jms.QueueReceiver QueueReceiver} and
* {@link javax.jms.TopicSubscriber TopicSubscriber}).
* <p/>
* <P>Message selectors cannot reference message body values.
* <p/>
* <P>A message selector matches a message if the selector evaluates to
* true when the message's header field values and property values are
* substituted for their corresponding identifiers in the selector.
* <p/>
* <P>A message selector is a <CODE>String</CODE> whose syntax is based on a
* subset of
* the SQL92 conditional expression syntax. If the value of a message selector
* is an empty string, the value is treated as a null and indicates that there
* is no message selector for the message consumer.
* <p/>
* <P>The order of evaluation of a message selector is from left to right
* within precedence level. Parentheses can be used to change this order.
* <p/>
* <P>Predefined selector literals and operator names are shown here in
* uppercase; however, they are case insensitive.
* <p/>
* <P>A selector can contain:
* <p/>
* <UL>
* <LI>Literals:
* <UL>
* <LI>A string literal is enclosed in single quotes, with a single quote
* represented by doubled single quote; for example,
* <CODE>'literal'</CODE> and <CODE>'literal''s'</CODE>. Like
* string literals in the Java programming language, these use the
* Unicode character encoding.
* <LI>An exact numeric literal is a numeric value without a decimal
* point, such as <CODE>57</CODE>, <CODE>-957</CODE>, and
* <CODE>+62</CODE>; numbers in the range of <CODE>long</CODE> are
* supported. Exact numeric literals use the integer literal
* syntax of the Java programming language.
* <LI>An approximate numeric literal is a numeric value in scientific
* notation, such as <CODE>7E3</CODE> and <CODE>-57.9E2</CODE>, or a
* numeric value with a decimal, such as <CODE>7.</CODE>,
* <CODE>-95.7</CODE>, and <CODE>+6.2</CODE>; numbers in the range of
* <CODE>double</CODE> are supported. Approximate literals use the
* floating-point literal syntax of the Java programming language.
* <LI>The boolean literals <CODE>TRUE</CODE> and <CODE>FALSE</CODE>.
* </UL>
* <LI>Identifiers:
* <UL>
* <LI>An identifier is an unlimited-length sequence of letters
* and digits, the first of which must be a letter. A letter is any
* character for which the method <CODE>Character.isJavaLetter</CODE>
* returns true. This includes <CODE>'_'</CODE> and <CODE>'$'</CODE>.
* A letter or digit is any character for which the method
* <CODE>Character.isJavaLetterOrDigit</CODE> returns true.
* <LI>Identifiers cannot be the names <CODE>NULL</CODE>,
* <CODE>TRUE</CODE>, and <CODE>FALSE</CODE>.
* <LI>Identifiers cannot be <CODE>NOT</CODE>, <CODE>AND</CODE>,
* <CODE>OR</CODE>, <CODE>BETWEEN</CODE>, <CODE>LIKE</CODE>,
* <CODE>IN</CODE>, <CODE>IS</CODE>, or <CODE>ESCAPE</CODE>.
* <LI>Identifiers are either header field references or property
* references. The type of a property value in a message selector
* corresponds to the type used to set the property. If a property
* that does not exist in a message is referenced, its value is
* <CODE>NULL</CODE>.
* <LI>The conversions that apply to the get methods for properties do not
* apply when a property is used in a message selector expression.
* For example, suppose you set a property as a string value, as in the
* following:
* <PRE>myMessage.setStringProperty("NumberOfOrders", "2");</PRE>
* The following expression in a message selector would evaluate to
* false, because a string cannot be used in an arithmetic expression:
* <PRE>"NumberOfOrders > 1"</PRE>
* <LI>Identifiers are case-sensitive.
* <LI>Message header field references are restricted to
* <CODE>JMSDeliveryMode</CODE>, <CODE>JMSPriority</CODE>,
* <CODE>JMSMessageID</CODE>, <CODE>JMSTimestamp</CODE>,
* <CODE>JMSCorrelationID</CODE>, and <CODE>JMSType</CODE>.
* <CODE>JMSMessageID</CODE>, <CODE>JMSCorrelationID</CODE>, and
* <CODE>JMSType</CODE> values may be null and if so are treated as a
* <CODE>NULL</CODE> value.
* <LI>Any name beginning with <CODE>'JMSX'</CODE> is a JMS defined
* property name.
* <LI>Any name beginning with <CODE>'JMS_'</CODE> is a provider-specific
* property name.
* <LI>Any name that does not begin with <CODE>'JMS'</CODE> is an
* application-specific property name.
* </UL>
* <LI>White space is the same as that defined for the Java programming
* language: space, horizontal tab, form feed, and line terminator.
* <LI>Expressions:
* <UL>
* <LI>A selector is a conditional expression; a selector that evaluates
* to <CODE>true</CODE> matches; a selector that evaluates to
* <CODE>false</CODE> or unknown does not match.
* <LI>Arithmetic expressions are composed of themselves, arithmetic
* operations, identifiers (whose value is treated as a numeric
* literal), and numeric literals.
* <LI>Conditional expressions are composed of themselves, comparison
* operations, and logical operations.
* </UL>
* <LI>Standard bracketing <CODE>()</CODE> for ordering expression evaluation
* is supported.
* <LI>Logical operators in precedence order: <CODE>NOT</CODE>,
* <CODE>AND</CODE>, <CODE>OR</CODE>
* <LI>Comparison operators: <CODE>=</CODE>, <CODE>></CODE>, <CODE>>=</CODE>,
* <CODE><</CODE>, <CODE><=</CODE>, <CODE><></CODE> (not equal)
* <UL>
* <LI>Only like type values can be compared. One exception is that it
* is valid to compare exact numeric values and approximate numeric
* values; the type conversion required is defined by the rules of
* numeric promotion in the Java programming language. If the
* comparison of non-like type values is attempted, the value of the
* operation is false. If either of the type values evaluates to
* <CODE>NULL</CODE>, the value of the expression is unknown.
* <LI>String and boolean comparison is restricted to <CODE>=</CODE> and
* <CODE><></CODE>. Two strings are equal
* if and only if they contain the same sequence of characters.
* </UL>
* <LI>Arithmetic operators in precedence order:
* <UL>
* <LI><CODE>+</CODE>, <CODE>-</CODE> (unary)
* <LI><CODE>*</CODE>, <CODE>/</CODE> (multiplication and division)
* <LI><CODE>+</CODE>, <CODE>-</CODE> (addition and subtraction)
* <LI>Arithmetic operations must use numeric promotion in the Java
* programming language.
* </UL>
* <LI><CODE><I>arithmetic-expr1</I> [NOT] BETWEEN <I>arithmetic-expr2</I>
* AND <I>arithmetic-expr3</I></CODE> (comparison operator)
* <UL>
* <LI><CODE>"age BETWEEN 15 AND 19"</CODE> is
* equivalent to
* <CODE>"age >= 15 AND age <= 19"</CODE>
* <LI><CODE>"age NOT BETWEEN 15 AND 19"</CODE>
* is equivalent to
* <CODE>"age < 15 OR age > 19"</CODE>
* </UL>
* <LI><CODE><I>identifier</I> [NOT] IN (<I>string-literal1</I>,
* <I>string-literal2</I>,...)</CODE> (comparison operator where
* <CODE><I>identifier</I></CODE> has a <CODE>String</CODE> or
* <CODE>NULL</CODE> value)
* <UL>
* <LI><CODE>"Country IN (' UK', 'US', 'France')"</CODE>
* is true for
* <CODE>'UK'</CODE> and false for <CODE>'Peru'</CODE>; it is
* equivalent to the expression
* <CODE>"(Country = ' UK') OR (Country = ' US') OR (Country = ' France')"</CODE>
* <LI><CODE>"Country NOT IN (' UK', 'US', 'France')"</CODE>
* is false for <CODE>'UK'</CODE> and true for <CODE>'Peru'</CODE>; it
* is equivalent to the expression
* <CODE>"NOT ((Country = ' UK') OR (Country = ' US') OR (Country = ' France'))"</CODE>
* <LI>If identifier of an <CODE>IN</CODE> or <CODE>NOT IN</CODE>
* operation is <CODE>NULL</CODE>, the value of the operation is
* unknown.
* </UL>
* <LI><CODE><I>identifier</I> [NOT] LIKE <I>pattern-value</I> [ESCAPE
* <I>escape-character</I>]</CODE> (comparison operator, where
* <CODE><I>identifier</I></CODE> has a <CODE>String</CODE> value;
* <CODE><I>pattern-value</I></CODE> is a string literal where
* <CODE>'_'</CODE> stands for any single character; <CODE>'%'</CODE>
* stands for any sequence of characters, including the empty sequence;
* and all other characters stand for themselves. The optional
* <CODE><I>escape-character</I></CODE> is a single-character string
* literal whose character is used to escape the special meaning of the
* <CODE>'_'</CODE> and <CODE>'%'</CODE> in
* <CODE><I>pattern-value</I></CODE>.)
* <UL>
* <LI><CODE>"phone LIKE '12%3'"</CODE> is true for
* <CODE>'123'</CODE> or <CODE>'12993'</CODE> and false for
* <CODE>'1234'</CODE>
* <LI><CODE>"word LIKE 'l_se'"</CODE> is true for
* <CODE>'lose'</CODE> and false for <CODE>'loose'</CODE>
* <LI><CODE>"underscored LIKE '\_%' ESCAPE '\'"</CODE>
* is true for <CODE>'_foo'</CODE> and false for <CODE>'bar'</CODE>
* <LI><CODE>"phone NOT LIKE '12%3'"</CODE> is false for
* <CODE>'123'</CODE> or <CODE>'12993'</CODE> and true for
* <CODE>'1234'</CODE>
* <LI>If <CODE><I>identifier</I></CODE> of a <CODE>LIKE</CODE> or
* <CODE>NOT LIKE</CODE> operation is <CODE>NULL</CODE>, the value
* of the operation is unknown.
* </UL>
* <LI><CODE><I>identifier</I> IS NULL</CODE> (comparison operator that tests
* for a null header field value or a missing property value)
* <UL>
* <LI><CODE>"prop_name IS NULL"</CODE>
* </UL>
* <LI><CODE><I>identifier</I> IS NOT NULL</CODE> (comparison operator that
* tests for the existence of a non-null header field value or a property
* value)
* <UL>
* <LI><CODE>"prop_name IS NOT NULL"</CODE>
* </UL>
* <p/>
* <P>JMS providers are required to verify the syntactic correctness of a
* message selector at the time it is presented. A method that provides a
* syntactically incorrect selector must result in a <CODE>JMSException</CODE>.
* JMS providers may also optionally provide some semantic checking at the time
* the selector is presented. Not all semantic checking can be performed at
* the time a message selector is presented, because property types are not known.
* <p/>
* <P>The following message selector selects messages with a message type
* of car and color of blue and weight greater than 2500 pounds:
* <p/>
* <PRE>"JMSType = 'car' AND color = 'blue' AND weight > 2500"</PRE>
* <p/>
* <H4>Null Values</H4>
* <p/>
* <P>As noted above, property values may be <CODE>NULL</CODE>. The evaluation
* of selector expressions containing <CODE>NULL</CODE> values is defined by
* SQL92 <CODE>NULL</CODE> semantics. A brief description of these semantics
* is provided here.
* <p/>
* <P>SQL treats a <CODE>NULL</CODE> value as unknown. Comparison or arithmetic
* with an unknown value always yields an unknown value.
* <p/>
* <P>The <CODE>IS NULL</CODE> and <CODE>IS NOT NULL</CODE> operators convert
* an unknown value into the respective <CODE>TRUE</CODE> and
* <CODE>FALSE</CODE> values.
* <p/>
* <P>The boolean operators use three-valued logic as defined by the
* following tables:
* <p/>
* <P><B>The definition of the <CODE>AND</CODE> operator</B>
* <p/>
* <PRE>
* | AND | T | F | U
* +------+-------+-------+-------
* | T | T | F | U
* | F | F | F | F
* | U | U | F | U
* +------+-------+-------+-------
* </PRE>
* <p/>
* <P><B>The definition of the <CODE>OR</CODE> operator</B>
* <p/>
* <PRE>
* | OR | T | F | U
* +------+-------+-------+--------
* | T | T | T | T
* | F | T | F | U
* | U | T | U | U
* +------+-------+-------+-------
* </PRE>
* <p/>
* <P><B>The definition of the <CODE>NOT</CODE> operator</B>
* <p/>
* <PRE>
* | NOT
* +------+------
* | T | F
* | F | T
* | U | U
* +------+-------
* </PRE>
* <p/>
* <H4>Special Notes</H4>
* <p/>
* <P>When used in a message selector, the <CODE>JMSDeliveryMode</CODE> header
* field is treated as having the values <CODE>'PERSISTENT'</CODE> and
* <CODE>'NON_PERSISTENT'</CODE>.
* <p/>
* <P>Date and time values should use the standard <CODE>long</CODE>
* millisecond value. When a date or time literal is included in a message
* selector, it should be an integer literal for a millisecond value. The
* standard way to produce millisecond values is to use
* <CODE>java.util.Calendar</CODE>.
* <p/>
* <P>Although SQL supports fixed decimal comparison and arithmetic, JMS
* message selectors do not. This is the reason for restricting exact
* numeric literals to those without a decimal (and the addition of
* numerics with a decimal as an alternate representation for
* approximate numeric values).
* <p/>
* <P>SQL comments are not supported.
*
* @version $Revision: 1.1.1.1 $
* @see javax.jms.MessageConsumer#receive()
* @see javax.jms.MessageConsumer#receive(long)
* @see javax.jms.MessageConsumer#receiveNoWait()
* @see javax.jms.MessageListener#onMessage(Message)
* @see javax.jms.BytesMessage
* @see javax.jms.MapMessage
* @see javax.jms.ObjectMessage
* @see javax.jms.StreamMessage
* @see javax.jms.TextMessage
*/
public class ActiveMQMessage extends AbstractPacket implements Message, Comparable, MemoryManageable, BodyPacket {
/**
* The message producer's default delivery mode is <CODE>PERSISTENT</CODE>.
*
* @see DeliveryMode#PERSISTENT
*/
static final int DEFAULT_DELIVERY_MODE = DeliveryMode.PERSISTENT;
/**
* The message producer's default priority is 4.
*/
static final int DEFAULT_PRIORITY = 4;
/**
* The message producer's default time to live is unlimited; the message
* never expires.
*/
static final long DEFAULT_TIME_TO_LIVE = 0;
/**
* message property types
*/
final static byte EOF = 2;
final static byte BYTES = 3;
final static byte STRING = 4;
final static byte BOOLEAN = 5;
final static byte CHAR = 6;
final static byte BYTE = 7;
final static byte SHORT = 8;
final static byte INT = 9;
final static byte LONG = 10;
final static byte FLOAT = 11;
final static byte DOUBLE = 12;
final static byte NULL = 13;
/**
* Message flag indexes (used for writing/reading to/from a Stream
*/
public static final int CORRELATION_INDEX = 2;
public static final int TYPE_INDEX = 3;
public static final int BROKER_NAME_INDEX = 4;
public static final int CLUSTER_NAME_INDEX = 5;
public static final int TRANSACTION_ID_INDEX = 6;
public static final int REPLY_TO_INDEX = 7;
public static final int TIMESTAMP_INDEX = 8;
public static final int EXPIRATION_INDEX = 9;
public static final int REDELIVERED_INDEX = 10;
public static final int XA_TRANS_INDEX = 11;
public static final int CID_INDEX = 12;
public static final int PROPERTIES_INDEX = 13;
public static final int DISPATCHED_FROM_DLQ_INDEX = 14;
public static final int PAYLOAD_INDEX = 15;
public static final int EXTERNAL_MESSAGE_ID_INDEX = 16;
public static final int MESSAGE_PART_INDEX = 17;
public static final int CACHED_VALUES_INDEX = 18;
public static final int CACHED_DESTINATION_INDEX = 19;
public static final int LONG_SEQUENCE_INDEX = 20;
private static final String DELIVERY_COUNT_NAME = "JMSXDeliveryCount";
/**
* <code>readOnlyMessage</code> denotes if the message is read only
*/
protected boolean readOnlyMessage;
private String jmsMessageID;
private String jmsClientID;
private String jmsCorrelationID;
private String producerKey;
private ActiveMQDestination jmsDestination;
private ActiveMQDestination jmsReplyTo;
private int jmsDeliveryMode = DEFAULT_DELIVERY_MODE;
private boolean jmsRedelivered;
private String jmsType;
private long jmsExpiration;
private int jmsPriority = DEFAULT_PRIORITY;
private long jmsTimestamp;
private Map properties;
private boolean readOnlyProperties;
private String entryBrokerName;
private String entryClusterName;
private int[] consumerNos; //these are set by the broker, and only relevant to consuming connections
private Object transactionId;
private boolean xaTransacted;
private String consumerIdentifier; //this is only used on the Client for acknowledging receipt of a message
private boolean messageConsumed;//only used on the client - to denote if its been delivered and read
private boolean transientConsumed;//only used on the client - to denote if its been delivered and read
private long sequenceNumber;//the sequence for this message from the producerId
private int deliveryCount = 1;//number of times the message has been delivered
private boolean dispatchedFromDLQ;
private MessageAcknowledge messageAcknowledge;
private ByteArray bodyAsBytes;
private MessageIdentity jmsMessageIdentity;
private short messsageHandle;//refers to the id of the MessageProducer that sent the message
private boolean externalMessageId;//is the messageId set from another JMS implementation ?
private boolean messagePart;//is the message split into multiple packets
private short numberOfParts;
private short partNumber;
private String parentMessageID;//if split into multiple parts - the 'real' or first messageId
/**
* Retrieve if a JMS Message type or not
*
* @return true if it is a JMS Message
*/
public boolean isJMSMessage() {
return true;
}
/**
* @return pretty print of this Message
*/
public String toString() {
return super.toString() + " ActiveMQMessage{ " +
", jmsMessageID = " + jmsMessageID +
", bodyAsBytes = " + bodyAsBytes +
", readOnlyMessage = " + readOnlyMessage +
", jmsClientID = '" + jmsClientID + "' " +
", jmsCorrelationID = '" + jmsCorrelationID + "' " +
", jmsDestination = " + jmsDestination +
", jmsReplyTo = " + jmsReplyTo +
", jmsDeliveryMode = " + jmsDeliveryMode +
", jmsRedelivered = " + jmsRedelivered +
", jmsType = '" + jmsType + "' " +
", jmsExpiration = " + jmsExpiration +
", jmsPriority = " + jmsPriority +
", jmsTimestamp = " + jmsTimestamp +
", properties = " + properties +
", readOnlyProperties = " + readOnlyProperties +
", entryBrokerName = '" + entryBrokerName + "' " +
", entryClusterName = '" + entryClusterName + "' " +
", consumerNos = " + toString(consumerNos) +
", transactionId = '" + transactionId + "' " +
", xaTransacted = " + xaTransacted +
", consumerIdentifer = '" + consumerIdentifier + "' " +
", messageConsumed = " + messageConsumed +
", transientConsumed = " + transientConsumed +
", sequenceNumber = " + sequenceNumber +
", deliveryCount = " + deliveryCount +
", dispatchedFromDLQ = " + dispatchedFromDLQ +
", messageAcknowledge = " + messageAcknowledge +
", jmsMessageIdentity = " + jmsMessageIdentity +
", producerKey = " + producerKey +
" }";
}
protected String toString(int[] consumerNos) {
if (consumerNos == null) {
return "null";
}
StringBuffer buffer = new StringBuffer("[");
for (int i = 0; i < consumerNos.length; i++) {
int consumerNo = consumerNos[i];
if (i > 0) {
buffer.append(", ");
}
buffer.append(Integer.toString(i));
}
buffer.append("]");
return buffer.toString();
}
/**
* @return Returns the messageAcknowledge.
*
* @Transient
*/
public MessageAcknowledge getMessageAcknowledge() {
return messageAcknowledge;
}
/**
* @param messageAcknowledge The messageAcknowledge to set.
*/
public void setMessageAcknowledge(MessageAcknowledge messageAcknowledge) {
this.messageAcknowledge = messageAcknowledge;
}
/**
* Return the type of Packet
*
* @return integer representation of the type of Packet
*/
public int getPacketType() {
return ACTIVEMQ_MESSAGE;
}
/**
* set the message readOnly
*
* @param value
*/
public void setReadOnly(boolean value) {
this.readOnlyProperties = value;
this.readOnlyMessage = value;
}
/**
* test to see if a particular Consumer at a Connection
* is meant to receive this Message
*
* @param consumerNumber
* @return true if a target
*/
public boolean isConsumerTarget(int consumerNumber) {
if (consumerNos != null) {
for (int i = 0; i < consumerNos.length; i++) {
if (consumerNos[i] == consumerNumber) {
return true;
}
}
}
return false;
}
/**
* @return consumer Nos as a String
*/
public String getConsumerNosAsString() {
String result = "";
if (consumerNos != null) {
for (int i = 0; i < consumerNos.length; i++) {
if (i > 0) {
result += ",";
}
result += consumerNos[i];
}
}
return result;
}
/**
* Sets the consumer numbers using a String format
*/
public void setConsumerNosAsString(String value) {
if (value == null) {
setConsumerNos(null);
}
else {
List values = new ArrayList();
StringTokenizer enm = new StringTokenizer(value, ",");
while (enm.hasMoreElements()) {
String token = enm.nextToken();
values.add(token);
}
int[] answer = new int[values.size()];
int i = 0;
for (Iterator iter = values.iterator(); iter.hasNext();) {
String text = (String) iter.next();
answer[i++] = Integer.parseInt(text.trim());
}
setConsumerNos(answer);
}
}
/**
* @return true if the message is non-persistent or intended for a temporary destination
*/
public boolean isTemporary() {
return jmsDeliveryMode == DeliveryMode.NON_PERSISTENT ||
(jmsDestination != null && jmsDestination.isTemporary());
}
/**
* @return Returns hash code for this instance
*/
public int hashCode() {
return this.getJMSMessageID() != null ? this.getJMSMessageID().hashCode() : super.hashCode();
}
/**
* Returns true if this instance is equivalent to obj
*
* @param obj the other instance to test
* @return true/false
*/
public boolean equals(Object obj) {
boolean result = obj == this;
if (!result && obj != null && obj instanceof ActiveMQMessage) {
ActiveMQMessage other = (ActiveMQMessage) obj;
//the call getJMSMessageID() will initialize the messageID
//if it hasn't already been set
result = this.getJMSMessageID() == other.getJMSMessageID();
if (!result){
if (this.jmsMessageID != null && this.jmsMessageID.length() > 0 ||
other.jmsMessageID != null && other.jmsMessageID.length() > 0){
if (this.jmsMessageID != null && other.jmsMessageID != null){
result = this.jmsMessageID.equals(other.jmsMessageID);
}
}else{
result = this.getId() == other.getId();
}
}
}
return result;
}
/**
* @param o object to compare
* @return 1 if this > o else 0 if they are equal or -1 if this < o
*/
public int compareTo(Object o) {
if (o instanceof ActiveMQMessage) {
return compareTo((ActiveMQMessage) o);
}
return -1;
}
/**
* Sorted by destination and then messageId
*
* @param that another message to compare against
* @return 1 if this > that else 0 if they are equal or -1 if this < that
*/
public int compareTo(ActiveMQMessage that) {
int answer = 1;
if (that != null && this.jmsDestination != null && that.jmsDestination != null) {
answer = this.jmsDestination.compareTo(that.jmsDestination);
if (answer == 0) {
if (this.jmsMessageID != null && that.jmsMessageID != null) {
answer = IdGenerator.compare(this.jmsMessageID, that.jmsMessageID);
}
else {
answer = 1;
}
}
}
return answer;
}
/**
* @return Returns a shallow copy of the message instance
* @throws JMSException
*/
public ActiveMQMessage shallowCopy() throws JMSException {
ActiveMQMessage other = new ActiveMQMessage();
this.initializeOther(other);
return other;
}
/**
* @return Returns a deep copy of the message - note the header fields are only shallow copied
* @throws JMSException
*/
public ActiveMQMessage deepCopy() throws JMSException {
return shallowCopy();
}
/**
* Indicates if the Message has expired
*
* @param currentTime -
* the current time in milliseconds
* @return true if the message can be expired
*/
public boolean isExpired(long currentTime) {
boolean result = false;
long expiration = this.jmsExpiration;
if (jmsExpiration > 0 && jmsExpiration < currentTime) {
result = true;
}
return result;
}
/**
* @return true if the message is expired
*/
public boolean isExpired() {
return !dispatchedFromDLQ && jmsExpiration > 0 && isExpired(System.currentTimeMillis());
}
/**
* @return true if an advisory message
*/
public boolean isAdvisory(){
return jmsDestination != null && jmsDestination.isAdvisory();
}
/**
* Initializes another message with current values from this instance
*
* @param other the other ActiveMQMessage to initialize
*/
protected void initializeOther(ActiveMQMessage other) {
super.initializeOther(other);
other.jmsMessageID = this.jmsMessageID;
other.jmsClientID = this.jmsClientID;
other.jmsCorrelationID = this.jmsCorrelationID;
other.jmsDestination = this.jmsDestination;
other.jmsReplyTo = this.jmsReplyTo;
other.jmsDeliveryMode = this.jmsDeliveryMode;
other.jmsRedelivered = this.jmsRedelivered;
other.jmsType = this.jmsType;
other.jmsExpiration = this.jmsExpiration;
other.jmsPriority = this.jmsPriority;
other.jmsTimestamp = this.jmsTimestamp;
other.properties = this.properties != null ? new HashMap(this.properties) : null;
other.readOnlyProperties = this.readOnlyProperties;
other.readOnlyMessage = this.readOnlyMessage;
other.entryBrokerName = this.entryBrokerName;
other.entryClusterName = this.entryClusterName;
other.consumerNos = this.consumerNos;
other.transactionId = this.transactionId;
other.xaTransacted = this.xaTransacted;
other.bodyAsBytes = this.bodyAsBytes;
other.messageAcknowledge = this.messageAcknowledge;
other.jmsMessageIdentity = this.jmsMessageIdentity;
other.sequenceNumber = this.sequenceNumber;
other.deliveryCount = this.deliveryCount;
other.dispatchedFromDLQ = this.dispatchedFromDLQ;
other.messsageHandle = this.messsageHandle;
other.consumerIdentifier = this.consumerIdentifier;
other.externalMessageId = this.externalMessageId;
other.producerKey = this.producerKey;
other.messagePart = this.messagePart;
other.numberOfParts = this.numberOfParts;
other.partNumber = this.partNumber;
other.parentMessageID = this.parentMessageID;
}
/**
* Gets the message ID.
* <p/>
* <P>The <CODE>JMSMessageID</CODE> header field contains a value that
* uniquely identifies each message sent by a provider.
* <p/>
* <P>When a message is sent, <CODE>JMSMessageID</CODE> can be ignored.
* When the <CODE>send</CODE> or <CODE>publish</CODE> method returns, it
* contains a provider-assigned value.
* <p/>
* <P>A <CODE>JMSMessageID</CODE> is a <CODE>String</CODE> value that
* should function as a
* unique key for identifying messages in a historical repository.
* The exact scope of uniqueness is provider-defined. It should at
* least cover all messages for a specific installation of a
* provider, where an installation is some connected set of message
* routers.
* <p/>
* <P>All <CODE>JMSMessageID</CODE> values must start with the prefix
* <CODE>'ID:'</CODE>.
* Uniqueness of message ID values across different providers is
* not required.
* <p/>
* <P>Since message IDs take some effort to create and increase a
* message's size, some JMS providers may be able to optimize message
* overhead if they are given a hint that the message ID is not used by
* an application. By calling the
* <CODE>MessageProducer.setDisableMessageID</CODE> method, a JMS client
* enables this potential optimization for all messages sent by that
* message producer. If the JMS provider accepts this
* hint, these messages must have the message ID set to null; if the
* provider ignores the hint, the message ID must be set to its normal
* unique value.
*
* @return the message ID
* @see javax.jms.Message#setJMSMessageID(String)
* @see javax.jms.MessageProducer#setDisableMessageID(boolean)
*/
public String getJMSMessageID() {
if (jmsMessageID == null && producerKey != null){
jmsMessageID = producerKey + sequenceNumber;
}
return jmsMessageID;
}
/**
* Sets the message ID.
* <p/>
* <P>JMS providers set this field when a message is sent. This method
* can be used to change the value for a message that has been received.
*
* @param id the ID of the message
* @see javax.jms.Message#getJMSMessageID()
*/
public void setJMSMessageID(String id) {
this.jmsMessageID = id;
this.jmsMessageIdentity = null;
}
/**
* Another way to get the Message id.
*
* @Transient
*/
public Object getMemoryId() {
return getJMSMessageID();
}
/**
* Gets the message timestamp.
* <p/>
* <P>The <CODE>JMSTimestamp</CODE> header field contains the time a
* message was
* handed off to a provider to be sent. It is not the time the
* message was actually transmitted, because the actual send may occur
* later due to transactions or other client-side queueing of messages.
* <p/>
* <P>When a message is sent, <CODE>JMSTimestamp</CODE> is ignored. When
* the <CODE>send</CODE> or <CODE>publish</CODE>
* method returns, it contains a time value somewhere in the interval
* between the call and the return. The value is in the format of a normal
* millis time value in the Java programming language.
* <p/>
* <P>Since timestamps take some effort to create and increase a
* message's size, some JMS providers may be able to optimize message
* overhead if they are given a hint that the timestamp is not used by an
* application. By calling the
* <CODE>MessageProducer.setDisableMessageTimestamp</CODE> method, a JMS
* client enables this potential optimization for all messages sent by
* that message producer. If the JMS provider accepts this
* hint, these messages must have the timestamp set to zero; if the
* provider ignores the hint, the timestamp must be set to its normal
* value.
*
* @return the message timestamp
* @see javax.jms.Message#setJMSTimestamp(long)
* @see javax.jms.MessageProducer#setDisableMessageTimestamp(boolean)
*/
public long getJMSTimestamp() {
return jmsTimestamp;
}
/**
* Sets the message timestamp.
* <p/>
* <P>JMS providers set this field when a message is sent. This method
* can be used to change the value for a message that has been received.
*
* @param timestamp the timestamp for this message
* @see javax.jms.Message#getJMSTimestamp()
*/
public void setJMSTimestamp(long timestamp) {
this.jmsTimestamp = timestamp;
}
/**
* Gets the correlation ID as an array of bytes for the message.
* <p/>
* <P>The use of a <CODE>byte[]</CODE> value for
* <CODE>JMSCorrelationID</CODE> is non-portable.
*
* @return the correlation ID of a message as an array of bytes
* @see javax.jms.Message#setJMSCorrelationID(String)
* @see javax.jms.Message#getJMSCorrelationID()
* @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[])
*
* @Transient
*/
public byte[] getJMSCorrelationIDAsBytes() {
return this.jmsCorrelationID != null ? this.jmsCorrelationID.getBytes() : null;
}
/**
* Sets the correlation ID as an array of bytes for the message.
* <p/>
* <P>The array is copied before the method returns, so
* future modifications to the array will not alter this message header.
* <p/>
* <P>If a provider supports the native concept of correlation ID, a
* JMS client may need to assign specific <CODE>JMSCorrelationID</CODE>
* values to match those expected by native messaging clients.
* JMS providers without native correlation ID values are not required to
* support this method and its corresponding get method; their
* implementation may throw a
* <CODE>java.lang.UnsupportedOperationException</CODE>.
* <p/>
* <P>The use of a <CODE>byte[]</CODE> value for
* <CODE>JMSCorrelationID</CODE> is non-portable.
*
* @param correlationID the correlation ID value as an array of bytes
* @see javax.jms.Message#setJMSCorrelationID(String)
* @see javax.jms.Message#getJMSCorrelationID()
* @see javax.jms.Message#getJMSCorrelationIDAsBytes()
*/
public void setJMSCorrelationIDAsBytes(byte[] correlationID) {
if (correlationID == null) {
this.jmsCorrelationID = null;
}
else {
this.jmsCorrelationID = new String(correlationID);
}
}
/**
* Sets the correlation ID for the message.
* <p/>
* <P>A client can use the <CODE>JMSCorrelationID</CODE> header field to
* link one message with another. A typical use is to link a response
* message with its request message.
* <p/>
* <P><CODE>JMSCorrelationID</CODE> can hold one of the following:
* <UL>
* <LI>A provider-specific message ID
* <LI>An application-specific <CODE>String</CODE>
* <LI>A provider-native <CODE>byte[]</CODE> value
* </UL>
* <p/>
* <P>Since each message sent by a JMS provider is assigned a message ID
* value, it is convenient to link messages via message ID. All message ID
* values must start with the <CODE>'ID:'</CODE> prefix.
* <p/>
* <P>In some cases, an application (made up of several clients) needs to
* use an application-specific value for linking messages. For instance,
* an application may use <CODE>JMSCorrelationID</CODE> to hold a value
* referencing some external information. Application-specified values
* must not start with the <CODE>'ID:'</CODE> prefix; this is reserved for
* provider-generated message ID values.
* <p/>
* <P>If a provider supports the native concept of correlation ID, a JMS
* client may need to assign specific <CODE>JMSCorrelationID</CODE> values
* to match those expected by clients that do not use the JMS API. A
* <CODE>byte[]</CODE> value is used for this
* purpose. JMS providers without native correlation ID values are not
* required to support <CODE>byte[]</CODE> values. The use of a
* <CODE>byte[]</CODE> value for <CODE>JMSCorrelationID</CODE> is
* non-portable.
*
* @param correlationID the message ID of a message being referred to
* @see javax.jms.Message#getJMSCorrelationID()
* @see javax.jms.Message#getJMSCorrelationIDAsBytes()
* @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[])
*/
public void setJMSCorrelationID(String correlationID) {
this.jmsCorrelationID = correlationID;
}
/**
* Gets the correlation ID for the message.
* <p/>
* <P>This method is used to return correlation ID values that are
* either provider-specific message IDs or application-specific
* <CODE>String</CODE> values.
*
* @return the correlation ID of a message as a <CODE>String</CODE>
* @see javax.jms.Message#setJMSCorrelationID(String)
* @see javax.jms.Message#getJMSCorrelationIDAsBytes()
* @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[])
*/
public String getJMSCorrelationID() {
return this.jmsCorrelationID;
}
/**
* Gets the <CODE>Destination</CODE> object to which a reply to this
* message should be sent.
*
* @return <CODE>Destination</CODE> to which to send a response to this
* message
* @see javax.jms.Message#setJMSReplyTo(Destination)
*/
public Destination getJMSReplyTo() {
return this.jmsReplyTo;
}
/**
* Sets the <CODE>Destination</CODE> object to which a reply to this
* message should be sent.
* <p/>
* <P>The <CODE>JMSReplyTo</CODE> header field contains the destination
* where a reply
* to the current message should be sent. If it is null, no reply is
* expected. The destination may be either a <CODE>Queue</CODE> object or
* a <CODE>Topic</CODE> object.
* <p/>
* <P>Messages sent with a null <CODE>JMSReplyTo</CODE> value may be a
* notification of some event, or they may just be some data the sender
* thinks is of interest.
* <p/>
* <P>Messages with a <CODE>JMSReplyTo</CODE> value typically expect a
* response. A response is optional; it is up to the client to decide.
* These messages are called requests. A message sent in response to a
* request is called a reply.
* <p/>
* <P>In some cases a client may wish to match a request it sent earlier
* with a reply it has just received. The client can use the
* <CODE>JMSCorrelationID</CODE> header field for this purpose.
*
* @param replyTo <CODE>Destination</CODE> to which to send a response to
* this message
* @see javax.jms.Message#getJMSReplyTo()
*/
public void setJMSReplyTo(Destination replyTo) {
this.jmsReplyTo = (ActiveMQDestination) replyTo;
}
/**
* Gets the <CODE>Destination</CODE> object for this message.
* <p/>
* <P>The <CODE>JMSDestination</CODE> header field contains the
* destination to which the message is being sent.
* <p/>
* <P>When a message is sent, this field is ignored. After completion
* of the <CODE>send</CODE> or <CODE>publish</CODE> method, the field
* holds the destination specified by the method.
* <p/>
* <P>When a message is received, its <CODE>JMSDestination</CODE> value
* must be equivalent to the value assigned when it was sent.
*
* @return the destination of this message
* @see javax.jms.Message#setJMSDestination(Destination)
*/
public Destination getJMSDestination() {
return this.jmsDestination;
}
/**
* Sets the <CODE>Destination</CODE> object for this message.
* <p/>
* <P>JMS providers set this field when a message is sent. This method
* can be used to change the value for a message that has been received.
*
* @param destination the destination for this message
* @see javax.jms.Message#getJMSDestination()
*/
public void setJMSDestination(Destination destination) {
this.jmsDestination = (ActiveMQDestination) destination;
}
/**
* Gets the <CODE>DeliveryMode</CODE> value specified for this message.
*
* @return the delivery mode for this message
* @see javax.jms.Message#setJMSDeliveryMode(int)
* @see javax.jms.DeliveryMode
*/
public int getJMSDeliveryMode() {
return this.jmsDeliveryMode;
}
/**
* Sets the <CODE>DeliveryMode</CODE> value for this message.
* <p/>
* <P>JMS providers set this field when a message is sent. This method
* can be used to change the value for a message that has been received.
*
* @param deliveryMode the delivery mode for this message
* @see javax.jms.Message#getJMSDeliveryMode()
* @see javax.jms.DeliveryMode
*/
public void setJMSDeliveryMode(int deliveryMode) {
this.jmsDeliveryMode = deliveryMode;
}
/**
* Gets an indication of whether this message is being redelivered.
* <p/>
* <P>If a client receives a message with the <CODE>JMSRedelivered</CODE>
* field set,
* it is likely, but not guaranteed, that this message was delivered
* earlier but that its receipt was not acknowledged
* at that time.
*
* @return true if this message is being redelivered
* @see javax.jms.Message#setJMSRedelivered(boolean)
*/
public boolean getJMSRedelivered() {
return this.jmsRedelivered;
}
/**
* Specifies whether this message is being redelivered.
* <p/>
* <P>This field is set at the time the message is delivered. This
* method can be used to change the value for a message that has
* been received.
*
* @param redelivered an indication of whether this message is being
* redelivered
* @see javax.jms.Message#getJMSRedelivered()
*/
public void setJMSRedelivered(boolean redelivered) {
this.jmsRedelivered = redelivered;
}
/**
* Gets the message type identifier supplied by the client when the
* message was sent.
*
* @return the message type
* @see javax.jms.Message#setJMSType(String)
*/
public String getJMSType() {
return this.jmsType;
}
/**
* Sets the message type.
* <p/>
* <P>Some JMS providers use a message repository that contains the
* definitions of messages sent by applications. The <CODE>JMSType</CODE>
* header field may reference a message's definition in the provider's
* repository.
* <p/>
* <P>The JMS API does not define a standard message definition repository,
* nor does it define a naming policy for the definitions it contains.
* <p/>
* <P>Some messaging systems require that a message type definition for
* each application message be created and that each message specify its
* type. In order to work with such JMS providers, JMS clients should
* assign a value to <CODE>JMSType</CODE>, whether the application makes
* use of it or not. This ensures that the field is properly set for those
* providers that require it.
* <p/>
* <P>To ensure portability, JMS clients should use symbolic values for
* <CODE>JMSType</CODE> that can be configured at installation time to the
* values defined in the current provider's message repository. If string
* literals are used, they may not be valid type names for some JMS
* providers.
*
* @param type the message type
* @see javax.jms.Message#getJMSType()
*/
public void setJMSType(String type) {
this.jmsType = type;
}
/**
* Gets the message's expiration value.
* <p/>
* <P>When a message is sent, the <CODE>JMSExpiration</CODE> header field
* is left unassigned. After completion of the <CODE>send</CODE> or
* <CODE>publish</CODE> method, it holds the expiration time of the
* message. This is the sum of the time-to-live value specified by the
* client and the GMT at the time of the <CODE>send</CODE> or
* <CODE>publish</CODE>.
* <p/>
* <P>If the time-to-live is specified as zero, <CODE>JMSExpiration</CODE>
* is set to zero to indicate that the message does not expire.
* <p/>
* <P>When a message's expiration time is reached, a provider should
* discard it. The JMS API does not define any form of notification of
* message expiration.
* <p/>
* <P>Clients should not receive messages that have expired; however,
* the JMS API does not guarantee that this will not happen.
*
* @return the time the message expires, which is the sum of the
* time-to-live value specified by the client and the GMT at the
* time of the send
* @see javax.jms.Message#setJMSExpiration(long)
*/
public long getJMSExpiration() {
return this.jmsExpiration;
}
/**
* Sets the message's expiration value.
* <p/>
* <P>JMS providers set this field when a message is sent. This method
* can be used to change the value for a message that has been received.
*
* @param expiration the message's expiration time
* @see javax.jms.Message#getJMSExpiration()
*/
public void setJMSExpiration(long expiration) {
this.jmsExpiration = expiration;
}
/**
* Gets the message priority level.
* <p/>
* <P>The JMS API defines ten levels of priority value, with 0 as the
* lowest
* priority and 9 as the highest. In addition, clients should consider
* priorities 0-4 as gradations of normal priority and priorities 5-9
* as gradations of expedited priority.
* <p/>
* <P>The JMS API does not require that a provider strictly implement
* priority
* ordering of messages; however, it should do its best to deliver
* expedited messages ahead of normal messages.
*
* @return the default message priority
* @see javax.jms.Message#setJMSPriority(int)
*/
public int getJMSPriority() {
return this.jmsPriority;
}
/**
* Sets the priority level for this message.
* <p/>
* <P>JMS providers set this field when a message is sent. This method
* can be used to change the value for a message that has been received.
*
* @param priority the priority of this message
* @see javax.jms.Message#getJMSPriority()
*/
public void setJMSPriority(int priority) {
this.jmsPriority = priority;
}
/**
* Clears a message's properties.
* <p/>
* <P>The message's header fields and body are not cleared.
*/
public synchronized void clearProperties() {
if (this.properties != null) {
this.properties.clear();
}
this.readOnlyProperties = false;
}
/**
* Indicates whether a property value exists.
*
* @param name the name of the property to test
* @return true if the property exists
*/
public boolean propertyExists(String name) {
return this.properties != null ? this.properties.containsKey(name) : false;
}
/**
* Returns the value of the <CODE>boolean</CODE> property with the
* specified name.
*
* @param name the name of the <CODE>boolean</CODE> property
* @return the <CODE>boolean</CODE> property value for the specified name
* @throws JMSException if the JMS provider fails to get the property
* value due to some internal error.
* @throws MessageFormatException if this type conversion is invalid.
*/
public boolean getBooleanProperty(String name) throws JMSException {
return vanillaToBoolean(this.properties, name);
}
/**
* Returns the value of the <CODE>byte</CODE> property with the specified
* name.
*
* @param name the name of the <CODE>byte</CODE> property
* @return the <CODE>byte</CODE> property value for the specified name
* @throws JMSException if the JMS provider fails to get the property
* value due to some internal error.
* @throws MessageFormatException if this type conversion is invalid.
*/
public byte getByteProperty(String name) throws JMSException {
return vanillaToByte(this.properties, name);
}
/**
* Returns the value of the <CODE>short</CODE> property with the specified
* name.
*
* @param name the name of the <CODE>short</CODE> property
* @return the <CODE>short</CODE> property value for the specified name
* @throws JMSException if the JMS provider fails to get the property
* value due to some internal error.
* @throws MessageFormatException if this type conversion is invalid.
*/
public short getShortProperty(String name) throws JMSException {
return vanillaToShort(this.properties, name);
}
/**
* Returns the value of the <CODE>int</CODE> property with the specified
* name.
*
* @param name the name of the <CODE>int</CODE> property
* @return the <CODE>int</CODE> property value for the specified name
* @throws JMSException if the JMS provider fails to get the property
* value due to some internal error.
* @throws MessageFormatException if this type conversion is invalid.
*/
public int getIntProperty(String name) throws JMSException {
return vanillaToInt(this.properties, name);
}
/**
* Returns the value of the <CODE>long</CODE> property with the specified
* name.
*
* @param name the name of the <CODE>long</CODE> property
* @return the <CODE>long</CODE> property value for the specified name
* @throws JMSException if the JMS provider fails to get the property
* value due to some internal error.
* @throws MessageFormatException if this type conversion is invalid.
*/
public long getLongProperty(String name) throws JMSException {
return vanillaToLong(this.properties, name);
}
/**
* Returns the value of the <CODE>float</CODE> property with the specified
* name.
*
* @param name the name of the <CODE>float</CODE> property
* @return the <CODE>float</CODE> property value for the specified name
* @throws JMSException if the JMS provider fails to get the property
* value due to some internal error.
* @throws MessageFormatException if this type conversion is invalid.
*/
public float getFloatProperty(String name) throws JMSException {
return vanillaToFloat(this.properties, name);
}
/**
* Returns the value of the <CODE>double</CODE> property with the specified
* name.
*
* @param name the name of the <CODE>double</CODE> property
* @return the <CODE>double</CODE> property value for the specified name
* @throws JMSException if the JMS provider fails to get the property
* value due to some internal error.
* @throws MessageFormatException if this type conversion is invalid.
*/
public double getDoubleProperty(String name) throws JMSException {
return vanillaToDouble(this.properties, name);
}
/**
* Returns the value of the <CODE>String</CODE> property with the specified
* name.
*
* @param name the name of the <CODE>String</CODE> property
* @return the <CODE>String</CODE> property value for the specified name;
* if there is no property by this name, a null value is returned
* @throws JMSException if the JMS provider fails to get the property
* value due to some internal error.
* @throws MessageFormatException if this type conversion is invalid.
*/
public String getStringProperty(String name) throws JMSException {
return vanillaToString(this.properties, name);
}
/**
* Returns the value of the Java object property with the specified name.
* <p/>
* <P>This method can be used to return, in objectified format,
* an object that has been stored as a property in the message with the
* equivalent <CODE>setObjectProperty</CODE> method call, or its equivalent
* primitive <CODE>set<I>type</I>Property</CODE> method.
*
* @param name the name of the Java object property
* @return the Java object property value with the specified name, in
* objectified format (for example, if the property was set as an
* <CODE>int</CODE>, an <CODE>Integer</CODE> is
* returned); if there is no property by this name, a null value
* is returned
*/
public Object getObjectProperty(String name) {
return this.properties != null ? this.properties.get(name) : null;
}
/**
* Returns an <CODE>Enumeration</CODE> of all the property names.
* <p/>
* <P>Note that JMS standard header fields are not considered
* properties and are not returned in this enumeration.
*
* @return an enumeration of all the names of property values
*
* @Transient
*/
public Enumeration getPropertyNames() {
if (this.properties == null) {
this.properties = new HashMap();
}
return Collections.enumeration(this.properties.keySet());
}
/**
* Retrieve the message properties as a Map
*
* @return the Map representing the properties or null if not set or used
*/
public Map getProperties() {
return this.properties;
}
/**
* Set the Message's properties from an external source
* No checking on correct types is done by this method
*
* @param newProperties
*/
public void setProperties(Map newProperties) {
this.properties = newProperties;
}
/**
* Sets a <CODE>boolean</CODE> property value with the specified name into
* the message.
*
* @param name the name of the <CODE>boolean</CODE> property
* @param value the <CODE>boolean</CODE> property value to set
* @throws JMSException if the JMS provider fails to set the property
* due to some internal error.
* @throws IllegalArgumentException if the name is null or if the name is
* an empty string.
* @throws MessageNotWriteableException if properties are read-only
*/
public void setBooleanProperty(String name, boolean value) throws JMSException {
prepareProperty(name);
this.properties.put(name, (value) ? Boolean.TRUE : Boolean.FALSE);
}
/**
* Sets a <CODE>byte</CODE> property value with the specified name into
* the message.
*
* @param name the name of the <CODE>byte</CODE> property
* @param value the <CODE>byte</CODE> property value to set
* @throws JMSException if the JMS provider fails to set the property
* due to some internal error.
* @throws IllegalArgumentException if the name is null or if the name is
* an empty string.
* @throws MessageNotWriteableException if properties are read-only
*/
public void setByteProperty(String name, byte value) throws JMSException {
prepareProperty(name);
this.properties.put(name, new Byte(value));
}
/**
* Sets a <CODE>short</CODE> property value with the specified name into
* the message.
*
* @param name the name of the <CODE>short</CODE> property
* @param value the <CODE>short</CODE> property value to set
* @throws JMSException if the JMS provider fails to set the property
* due to some internal error.
* @throws IllegalArgumentException if the name is null or if the name is
* an empty string.
* @throws MessageNotWriteableException if properties are read-only
*/
public void setShortProperty(String name, short value) throws JMSException {
prepareProperty(name);
this.properties.put(name, new Short(value));
}
/**
* Sets an <CODE>int</CODE> property value with the specified name into
* the message.
*
* @param name the name of the <CODE>int</CODE> property
* @param value the <CODE>int</CODE> property value to set
* @throws JMSException if the JMS provider fails to set the property
* due to some internal error.
* @throws IllegalArgumentException if the name is null or if the name is
* an empty string.
* @throws MessageNotWriteableException if properties are read-only
*/
public void setIntProperty(String name, int value) throws JMSException {
prepareProperty(name);
this.properties.put(name, new Integer(value));
}
/**
* Sets a <CODE>long</CODE> property value with the specified name into
* the message.
*
* @param name the name of the <CODE>long</CODE> property
* @param value the <CODE>long</CODE> property value to set
* @throws JMSException if the JMS provider fails to set the property
* due to some internal error.
* @throws IllegalArgumentException if the name is null or if the name is
* an empty string.
* @throws MessageNotWriteableException if properties are read-only
*/
public void setLongProperty(String name, long value) throws JMSException {
prepareProperty(name);
this.properties.put(name, new Long(value));
}
/**
* Sets a <CODE>float</CODE> property value with the specified name into
* the message.
*
* @param name the name of the <CODE>float</CODE> property
* @param value the <CODE>float</CODE> property value to set
* @throws JMSException if the JMS provider fails to set the property
* due to some internal error.
* @throws IllegalArgumentException if the name is null or if the name is
* an empty string.
* @throws MessageNotWriteableException if properties are read-only
*/
public void setFloatProperty(String name, float value) throws JMSException {
prepareProperty(name);
this.properties.put(name, new Float(value));
}
/**
* Sets a <CODE>double</CODE> property value with the specified name into
* the message.
*
* @param name the name of the <CODE>double</CODE> property
* @param value the <CODE>double</CODE> property value to set
* @throws JMSException if the JMS provider fails to set the property
* due to some internal error.
* @throws IllegalArgumentException if the name is null or if the name is
* an empty string.
* @throws MessageNotWriteableException if properties are read-only
*/
public void setDoubleProperty(String name, double value) throws JMSException {
prepareProperty(name);
this.properties.put(name, new Double(value));
}
/**
* Sets a <CODE>String</CODE> property value with the specified name into
* the message.
*
* @param name the name of the <CODE>String</CODE> property
* @param value the <CODE>String</CODE> property value to set
* @throws JMSException if the JMS provider fails to set the property
* due to some internal error.
* @throws IllegalArgumentException if the name is null or if the name is
* an empty string.
* @throws MessageNotWriteableException if properties are read-only
*/
public void setStringProperty(String name, String value) throws JMSException {
prepareProperty(name);
if (value == null) {
this.properties.remove(name);
}
else {
this.properties.put(name, value);
}
}
/**
* Sets a Java object property value with the specified name into the
* message.
* <p/>
* <P>Note that this method works only for the objectified primitive
* object types (<CODE>Integer</CODE>, <CODE>Double</CODE>,
* <CODE>Long</CODE> ...) and <CODE>String</CODE> objects.
*
* @param name the name of the Java object property
* @param value the Java object property value to set
* @throws JMSException if the JMS provider fails to set the property
* due to some internal error.
* @throws IllegalArgumentException if the name is null or if the name is
* an empty string.
* @throws MessageFormatException if the object is invalid
* @throws MessageNotWriteableException if properties are read-only
*/
public void setObjectProperty(String name, Object value) throws JMSException {
prepareProperty(name);
if (value == null) {
this.properties.remove(name);
}
else {
if (value instanceof Number ||
value instanceof Character ||
value instanceof Boolean ||
value instanceof String) {
this.properties.put(name, value);
}
else {
throw new MessageFormatException("Cannot set property to type: " + value.getClass().getName());
}
}
}
/**
* Acknowledges all consumed messages of the session of this consumed
* message.
* <p/>
* <P>All consumed JMS messages support the <CODE>acknowledge</CODE>
* method for use when a client has specified that its JMS session's
* consumed messages are to be explicitly acknowledged. By invoking
* <CODE>acknowledge</CODE> on a consumed message, a client acknowledges
* all messages consumed by the session that the message was delivered to.
* <p/>
* <P>Calls to <CODE>acknowledge</CODE> are ignored for both transacted
* sessions and sessions specified to use implicit acknowledgement modes.
* <p/>
* <P>A client may individually acknowledge each message as it is consumed,
* or it may choose to acknowledge messages as an application-defined group
* (which is done by calling acknowledge on the last received message of the group,
* thereby acknowledging all messages consumed by the session.)
* <p/>
* <P>Messages that have been received but not acknowledged may be
* redelivered.
*
* @throws JMSException if the JMS provider fails to acknowledge the
* messages due to some internal error.
* @throws javax.jms.IllegalStateException
* if this method is called on a closed
* session.
* @see javax.jms.Session#CLIENT_ACKNOWLEDGE
*/
public void acknowledge() throws JMSException {
if (this.messageAcknowledge != null) {
this.messageAcknowledge.acknowledge(this);
}
}
/**
* Clears out the message body. Clearing a message's body does not clear
* its header values or property entries.
* <p/>
* <P>If this message body was read-only, calling this method leaves
* the message body in the same state as an empty body in a newly
* created message.
*
* @throws JMSException if the JMS provider fails to clear the message
* body due to some internal error.
*/
public void clearBody() throws JMSException {
this.readOnlyMessage = false;
this.bodyAsBytes = null;
}
boolean vanillaToBoolean(Map table, String name) throws JMSException {
boolean result = false;
Object value = getVanillaProperty(table, name);
if (value != null) {
if (value instanceof Boolean) {
result = ((Boolean) value).booleanValue();
}
else if (value instanceof String) {
// will throw a runtime exception if cannot convert
result = Boolean.valueOf((String) value).booleanValue();
}
else {
throw new MessageFormatException(name + " not a Boolean type");
}
}
return result;
}
byte vanillaToByte(Map table, String name) throws JMSException {
byte result = 0;
Object value = getVanillaProperty(table, name);
if (value != null) {
if (value instanceof Byte) {
result = ((Byte) value).byteValue();
}
else if (value instanceof String) {
result = Byte.valueOf((String) value).byteValue();
}
else {
throw new MessageFormatException(name + " not a Byte type");
}
}
else {
//object doesn't exist - so treat as a null ..
throw new NumberFormatException("Cannot interpret null as a Byte");
}
return result;
}
short vanillaToShort(Map table, String name) throws JMSException {
short result = 0;
Object value = getVanillaProperty(table, name);
if (value != null) {
if (value instanceof Short) {
result = ((Short) value).shortValue();
}
else if (value instanceof String) {
return Short.valueOf((String) value).shortValue();
}
else if (value instanceof Byte) {
result = ((Byte) value).byteValue();
}
else {
throw new MessageFormatException(name + " not a Short type");
}
}
else {
throw new NumberFormatException(name + " is null");
}
return result;
}
int vanillaToInt(Map table, String name) throws JMSException {
int result = 0;
Object value = getVanillaProperty(table, name);
if (value != null) {
if (value instanceof Integer) {
result = ((Integer) value).intValue();
}
else if (value instanceof String) {
result = Integer.valueOf((String) value).intValue();
}
else if (value instanceof Byte) {
result = ((Byte) value).intValue();
}
else if (value instanceof Short) {
result = ((Short) value).intValue();
}
else {
throw new MessageFormatException(name + " not an Integer type");
}
}
else {
throw new NumberFormatException(name + " is null");
}
return result;
}
long vanillaToLong(Map table, String name) throws JMSException {
long result = 0;
Object value = getVanillaProperty(table, name);
if (value != null) {
if (value instanceof Long) {
result = ((Long) value).longValue();
}
else if (value instanceof String) {
// will throw a runtime exception if cannot convert
result = Long.valueOf((String) value).longValue();
}
else if (value instanceof Byte) {
result = ((Byte) value).byteValue();
}
else if (value instanceof Short) {
result = ((Short) value).shortValue();
}
else if (value instanceof Integer) {
result = ((Integer) value).intValue();
}
else {
throw new MessageFormatException(name + " not a Long type");
}
}
else {
throw new NumberFormatException(name + " is null");
}
return result;
}
float vanillaToFloat(Map table, String name) throws JMSException {
float result = 0.0f;
Object value = getVanillaProperty(table, name);
if (value != null) {
if (value instanceof Float) {
result = ((Float) value).floatValue();
}
else if (value instanceof String) {
result = Float.valueOf((String) value).floatValue();
}
else {
throw new MessageFormatException(name + " not a Float type: " + value.getClass());
}
}
else {
throw new NullPointerException(name + " is null");
}
return result;
}
double vanillaToDouble(Map table, String name) throws JMSException {
double result = 0.0d;
Object value = getVanillaProperty(table, name);
if (value != null) {
if (value instanceof Double) {
result = ((Double) value).doubleValue();
}
else if (value instanceof String) {
result = Double.valueOf((String) value).doubleValue();
}
else if (value instanceof Float) {
result = ((Float) value).floatValue();
}
else {
throw new MessageFormatException(name + " not a Double type");
}
}
else {
throw new NullPointerException(name + " is null");
}
return result;
}
Object getVanillaProperty(Map table, String name) {
Object result = null;
if (name == null) {
throw new NullPointerException("name supplied is null");
}
result = getReservedProperty(name);
if (result == null && table != null) {
result = table.get(name);
}
return result;
}
Object getReservedProperty(String name){
Object result = null;
if (name != null && name.equals(DELIVERY_COUNT_NAME)){
result = new Integer(deliveryCount);
}
return result;
}
String vanillaToString(Map table, String name) throws JMSException {
String result = null;
if (table != null) {
Object value = table.get(name);
if (value != null) {
if (value instanceof String || value instanceof Number || value instanceof Boolean) {
result = value.toString();
}
else {
throw new MessageFormatException(name + " not a String type");
}
}
}
return result;
}
private void prepareProperty(String name) throws JMSException {
if (name == null) {
throw new IllegalArgumentException("Invalid property name: cannot be null");
}
if (name.length() == 0) {
throw new IllegalArgumentException("Invalid property name: cannot be empty");
}
if (this.readOnlyProperties) {
throw new MessageNotWriteableException("Properties are read-only");
}
if (this.properties == null) {
this.properties = new HashMap();
}
}
/**
* @return Returns the entryBrokerName.
*/
public String getEntryBrokerName() {
return this.entryBrokerName;
}
/**
* @param newEntryBrokerName The entryBrokerName to set.
*/
public void setEntryBrokerName(String newEntryBrokerName) {
this.entryBrokerName = newEntryBrokerName;
}
/**
* @return Returns the entryClusterName.
*/
public String getEntryClusterName() {
return this.entryClusterName;
}
/**
* @param newEntryClusterName The entryClusterName to set.
*/
public void setEntryClusterName(String newEntryClusterName) {
this.entryClusterName = newEntryClusterName;
}
/**
* @return Returns the consumerNos.
*
* @Transient
*/
public int[] getConsumerNos() {
return this.consumerNos;
}
/**
* @param newConsumerNos The consumerIDs to set.
*/
public void setConsumerNos(int[] newConsumerNos) {
this.consumerNos = newConsumerNos;
}
/**
* @return Returns the jmsClientID.
*/
public String getJMSClientID() {
return this.jmsClientID;
}
/**
* @param newJmsClientID The jmsClientID to set.
*/
public void setJMSClientID(String newJmsClientID) {
this.jmsClientID = newJmsClientID;
}
/**
* @return Returns true if this message is part of a transaction
*/
public boolean isPartOfTransaction() {
return this.transactionId != null;
}
/**
* @return Returns the transactionId.
*
* @Transient
*/
public Object getTransactionId() {
return this.transactionId;
}
/**
* @param newTransactionId The transactionId to set.
*/
public void setTransactionId(Object newTransactionId) {
this.transactionId = newTransactionId;
this.xaTransacted = newTransactionId!=null && newTransactionId.getClass()==ActiveMQXid.class;
}
/**
* @Transient
* @return Returns the consumerId.
*/
public String getConsumerIdentifer() {
return consumerIdentifier;
}
/**
* @param consId The consumerId to set.
*/
public void setConsumerIdentifer(String consId) {
this.consumerIdentifier = consId;
}
/**
* @Transient
* @return Returns the messageConsumed.
*/
public boolean isMessageConsumed() {
return messageConsumed;
}
/**
* @param messageConsumed The messageConsumed to set.
*/
public void setMessageConsumed(boolean messageConsumed) {
this.messageConsumed = messageConsumed;
}
/**
* Prepare a message body for delivery
*
* @throws JMSException
*/
public void prepareMessageBody() throws JMSException {
}
/**
* Convert the message body to data
*
* @throws IOException
*/
public final void convertBodyToBytes() throws IOException {
if (bodyAsBytes == null) {
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
DataOutputStream dataOut = new DataOutputStream(bytesOut);
writeBody(dataOut);
dataOut.flush();
bodyAsBytes = new ByteArray(bytesOut.toByteArray());
dataOut.close();
}
}
/**
* Builds the message body from data
*
* @throws IOException
*/
public final void buildBodyFromBytes() throws IOException {
if (bodyAsBytes != null) {
//inflate bodyAsBytes if needed
if (ByteArrayCompression.isCompressed(bodyAsBytes)){
ByteArrayCompression compression = new ByteArrayCompression();
bodyAsBytes = compression.inflate(bodyAsBytes);
}
ByteArrayInputStream bytesIn = new ByteArrayInputStream(bodyAsBytes.getBuf(),bodyAsBytes.getOffset(),bodyAsBytes.getLength());
DataInputStream dataIn = new DataInputStream(bytesIn);
readBody(dataIn);
dataIn.close();
}
}
/**
* Used serialize the message body to an output stream
*
* @param dataOut
* @throws IOException
*/
public void writeBody(DataOutput dataOut) throws IOException {
}
/**
* Used to help build the body from an input stream
*
* @param dataIn
* @throws IOException
*/
public void readBody(DataInput dataIn) throws IOException {
}
/**
* @return Returns the bodyAsBytes.
* @throws IOException
*
* @Transient
*/
public ByteArray getBodyAsBytes() throws IOException {
if (bodyAsBytes == null) {
convertBodyToBytes();
}
return bodyAsBytes;
}
/**
* return the data after applying compression
* @param compression
* @return compressed ByteArray
* @throws IOException
*/
public ByteArray getBodyAsBytes(ByteArrayCompression compression) throws IOException {
bodyAsBytes = compression.deflate(getBodyAsBytes());
return bodyAsBytes;
}
/**
* @return true if the body is already stored as bytes
*/
public boolean isBodyConvertedToBytes(){
return bodyAsBytes != null;
}
/**
* @param data The bodyAsBytes to set.
* @param offset
* @param length
*/
public void setBodyAsBytes(byte[] data,int offset, int length) {
this.bodyAsBytes = new ByteArray(data);
}
/**
* set the body as bytes
* @param ba
*/
public void setBodyAsBytes(ByteArray ba){
this.bodyAsBytes = ba;
}
/**
* write map properties to an output stream
*
* @param table
* @param dataOut
* @throws IOException
*/
public void writeMapProperties(Map table, DataOutput dataOut) throws IOException {
if (table != null) {
dataOut.writeShort(table.size());
for (Iterator iter = table.keySet().iterator(); iter.hasNext();) {
String key = iter.next().toString();
dataOut.writeUTF(key);
Object value = table.get(key);
if (value == null) {
dataOut.write(ActiveMQMessage.NULL);
}
else if (value instanceof byte[]) {
byte[] data = (byte[]) value;
dataOut.write(ActiveMQMessage.BYTES);
if (data != null) {
dataOut.writeInt(data.length);
dataOut.write(data);
}
else {
dataOut.writeInt(-1);
}
}
else if (value instanceof Byte) {
dataOut.write(ActiveMQMessage.BYTE);
Byte v = (Byte) value;
dataOut.writeByte(v.byteValue());
}
else if (value instanceof Boolean) {
dataOut.write(ActiveMQMessage.BOOLEAN);
Boolean v = (Boolean) value;
dataOut.writeBoolean(v.booleanValue());
}
else if (value instanceof String) {
dataOut.write(ActiveMQMessage.STRING);
dataOut.writeUTF(value.toString());
}
else if (value instanceof Character) {
dataOut.write(ActiveMQMessage.CHAR);
Character v = (Character) value;
dataOut.writeChar(v.charValue());
}
else if (value instanceof Number) {
Number v = (Number) value;
if (value instanceof Long) {
dataOut.write(ActiveMQMessage.LONG);
dataOut.writeLong(v.longValue());
}
else if (value instanceof Integer) {
dataOut.write(ActiveMQMessage.INT);
dataOut.writeInt(v.intValue());
}
else if (value instanceof Short) {
dataOut.write(ActiveMQMessage.SHORT);
dataOut.writeShort(v.shortValue());
}
else if (value instanceof Float) {
dataOut.write(ActiveMQMessage.FLOAT);
dataOut.writeFloat(v.floatValue());
}
else if (value instanceof Double) {
dataOut.write(ActiveMQMessage.DOUBLE);
dataOut.writeDouble(v.doubleValue());
}
}
else {
throw new RuntimeException("Do not know how to parse value of type: " + value.getClass());
}
}
}
else {
dataOut.writeShort(-1);
}
}
/**
* @param dataIn
* @return
* @throws IOException
*/
public Map readMapProperties(DataInput dataIn) throws IOException {
Map result = null;
int size = dataIn.readShort();
if (size > -1) {
result = new HashMap();
for (int i = 0; i < size; i++) {
String key = dataIn.readUTF();
Object value = null;
int type = dataIn.readByte();
if (type == ActiveMQMessage.NULL) {
value = null;
}
else if (type == ActiveMQMessage.BYTES) {
byte[] data = null;
int dataSize = dataIn.readInt();
if (dataSize > -1) {
data = new byte[dataSize];
dataIn.readFully(data);
}
value = data;
}
else if (type == ActiveMQMessage.BYTE) {
value = new Byte(dataIn.readByte());
}
else if (type == ActiveMQMessage.BOOLEAN) {
value = (dataIn.readBoolean()) ? Boolean.TRUE : Boolean.FALSE;
}
else if (type == ActiveMQMessage.STRING) {
value = dataIn.readUTF();
}
else if (type == ActiveMQMessage.CHAR) {
value = new Character(dataIn.readChar());
}
else if (type == ActiveMQMessage.LONG) {
value = new Long(dataIn.readLong());
}
else if (type == ActiveMQMessage.INT) {
value = new Integer(dataIn.readInt());
}
else if (type == ActiveMQMessage.SHORT) {
value = new Short(dataIn.readShort());
}
else if (type == ActiveMQMessage.FLOAT) {
value = new Float(dataIn.readFloat());
}
else if (type == ActiveMQMessage.DOUBLE) {
value = new Double(dataIn.readDouble());
}
else {
throw new RuntimeException("Do not know how to parse type: " + type);
}
result.put(key, value);
}
}
return result;
}
/**
* @return Returns the xaTransacted.
*/
public boolean isXaTransacted() {
return xaTransacted;
}
/**
* @return the ActiveMQDestination
*
* @Transient
*/
public ActiveMQDestination getJMSActiveMQDestination() {
return jmsDestination;
}
/**
* @return the message identity, which contains the String messageID
* and the lazily populated sequence number
*
* @Transient
*/
public MessageIdentity getJMSMessageIdentity() {
if (jmsMessageIdentity == null) {
jmsMessageIdentity = new MessageIdentity(this.getJMSMessageID());
}
return jmsMessageIdentity;
}
/**
* @param messageIdentity - message identity for this object
*/
public void setJMSMessageIdentity(MessageIdentity messageIdentity) {
this.jmsMessageIdentity = messageIdentity;
}
/**
* Determine if the message originated in the network from the named broker
* @param brokerName
* @return true if entry point matches the brokerName
*/
public boolean isEntryBroker(String brokerName){
boolean result = entryBrokerName != null && brokerName != null && entryBrokerName.equals(brokerName);
return result;
}
/**
* Determine if the message originated in the network from the named cluster
* @param clusterName
* @return true if the entry point matches the clusterName
*/
public boolean isEntryCluster(String clusterName){
boolean result = entryClusterName != null && clusterName != null && entryClusterName.equals(clusterName);
return result;
}
/**
* @Transient
* @return Returns the transientConsumed.
*/
public boolean isTransientConsumed() {
return transientConsumed;
}
/**
* @param transientConsumed The transientConsumed to set.
*/
public void setTransientConsumed(boolean transientConsumed) {
this.transientConsumed = transientConsumed;
}
/**
* @return Returns the sequenceNumber.
*/
public long getSequenceNumber() {
return sequenceNumber;
}
/**
* @param sequenceNumber The sequenceNumber to set.
*/
public void setSequenceNumber(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
/**
* @return Returns the deliveryCount.
*/
public int getDeliveryCount() {
return deliveryCount;
}
/**
* @param deliveryCount The deliveredCount to set.
*/
public void setDeliveryCount(int deliveryCount) {
this.deliveryCount = deliveryCount;
}
/**
* Increment the delivery count
* @return the new value of the delivery count
*/
public int incrementDeliveryCount(){
return ++this.deliveryCount;
}
/**
* @return true if the delivery mode is persistent
*/
public boolean isPersistent(){
return jmsDeliveryMode == DeliveryMode.PERSISTENT;
}
/**
* @return Returns the dispatchedFromDLQ.
*/
public boolean isDispatchedFromDLQ() {
return dispatchedFromDLQ;
}
/**
* @param dispatchedFromDLQ The dispatchedFromDLQ to set.
*/
public void setDispatchedFromDLQ(boolean dispatchedFromDLQ) {
this.dispatchedFromDLQ = dispatchedFromDLQ;
}
/**
* @return Returns the messsageHandle.
*/
public short getMesssageHandle() {
return messsageHandle;
}
/**
* @param messsageHandle The messsageHandle to set.
*/
public void setMesssageHandle(short messsageHandle) {
this.messsageHandle = messsageHandle;
}
/**
* @return Returns the externalMessageId.
*/
public boolean isExternalMessageId() {
return externalMessageId;
}
/**
* @param externalMessageId The externalMessageId to set.
*/
public void setExternalMessageId(boolean externalMessageId) {
this.externalMessageId = externalMessageId;
}
/**
* @return Returns the producerKey.
*/
public String getProducerKey() {
return producerKey;
}
/**
* @param producerKey The producerKey to set.
*/
public void setProducerKey(String producerKey) {
this.producerKey = producerKey;
}
/**
* reset message fragmentation infomation
* on this message
*
*/
public void resetMessagePart(){
messagePart = false;
partNumber = 0;
parentMessageID = null;
}
/**
* @return Returns the messagePart.
*/
public boolean isMessagePart() {
return messagePart;
}
/**
* @return true if this is the last part of a fragmented message
*/
public boolean isLastMessagePart(){
return numberOfParts -1 == partNumber;
}
/**
* @param messagePart The messagePart to set.
*/
public void setMessagePart(boolean messagePart) {
this.messagePart = messagePart;
}
/**
* @return Returns the numberOfParts.
*/
public short getNumberOfParts() {
return numberOfParts;
}
/**
* @param numberOfParts The numberOfParts to set.
*/
public void setNumberOfParts(short numberOfParts) {
this.numberOfParts = numberOfParts;
}
/**
* @return Returns the partNumber.
*/
public short getPartNumber() {
return partNumber;
}
/**
* @param partNumber The partNumber to set.
*/
public void setPartNumber(short partNumber) {
this.partNumber = partNumber;
}
/**
* @return Returns the parentMessageId.
*/
public String getParentMessageID() {
return parentMessageID;
}
/**
* @param parentMessageId The parentMessageId to set.
*/
public void setParentMessageID(String parentMessageId) {
this.parentMessageID = parentMessageId;
}
public int getPriority() {
return getJMSPriority();
}
/**
* A helper method for the OpenWire protocol
*/
public String getTransactionIDString() throws IOException {
return ActiveMQXid.transactionIDToString(getTransactionId());
}
/**
* A helper method for the OpenWire protocol
*/
public void setTransactionIDString(String text) throws IOException {
setTransactionId(ActiveMQXid.transactionIDFromString(text));
}
}