View Javadoc

    1   /***
    2    *
    3    * Copyright 2004 Protique Ltd
    4    *
    5    * Licensed under the Apache License, Version 2.0 (the "License");
    6    * you may not use this file except in compliance with the License.
    7    * You may obtain a copy of the License at
    8    *
    9    * http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   **/
    18
    19  package org.codehaus.activemq.message;
    20
    21  import org.codehaus.activemq.service.MessageIdentity;
    22  import org.codehaus.activemq.util.IdGenerator;
    23
    24  import javax.jms.DeliveryMode;
    25  import javax.jms.Destination;
    26  import javax.jms.JMSException;
    27  import javax.jms.Message;
    28  import javax.jms.MessageFormatException;
    29  import javax.jms.MessageNotWriteableException;
    30  import java.io.ByteArrayInputStream;
    31  import java.io.ByteArrayOutputStream;
    32  import java.io.DataInput;
    33  import java.io.DataInputStream;
    34  import java.io.DataOutput;
    35  import java.io.DataOutputStream;
    36  import java.io.IOException;
    37  import java.util.Enumeration;
    38  import java.util.Hashtable;
    39
    40  /***
    41   * The <CODE>Message</CODE> interface is the root interface of all JMS
    42   * messages. It defines the message header and the <CODE>acknowledge</CODE>
    43   * method used for all messages.
    44   * <p/>
    45   * <P>Most message-oriented middleware (MOM) products treat messages as
    46   * lightweight entities that consist
    47   * of a header and a payload. The header contains fields used for message
    48   * routing and identification; the payload contains the application data
    49   * being sent.
    50   * <p/>
    51   * <P>Within this general form, the definition of a message varies
    52   * significantly across products. It would be quite difficult for the JMS API
    53   * to support all of these message models.
    54   * <p/>
    55   * <P>With this in mind, the JMS message model has the following goals:
    56   * <UL>
    57   * <LI>Provide a single, unified message API
    58   * <LI>Provide an API suitable for creating messages that match the
    59   * format used by provider-native messaging applications
    60   * <LI>Support the development of heterogeneous applications that span
    61   * operating systems, machine architectures, and computer languages
    62   * <LI>Support messages containing objects in the Java programming language
    63   * ("Java objects")
    64   * <LI>Support messages containing Extensible Markup Language (XML) pages
    65   * </UL>
    66   * <p/>
    67   * <P>JMS messages are composed of the following parts:
    68   * <UL>
    69   * <LI>Header - All messages support the same set of header fields.
    70   * Header fields contain values used by both clients and providers to
    71   * identify and route messages.
    72   * <LI>Properties - Each message contains a built-in facility for supporting
    73   * application-defined property values. Properties provide an efficient
    74   * mechanism for supporting application-defined message filtering.
    75   * <LI>Body - The JMS API defines several types of message body, which cover
    76   * the majority of messaging styles currently in use.
    77   * </UL>
    78   * <p/>
    79   * <H4>Message Bodies</H4>
    80   * <p/>
    81   * <P>The JMS API defines five types of message body:
    82   * <UL>
    83   * <LI>Stream - A <CODE>StreamMessage</CODE> object's message body contains
    84   * a stream of primitive values in the Java programming
    85   * language ("Java primitives"). It is filled and read sequentially.
    86   * <LI>Map - A <CODE>MapMessage</CODE> object's message body contains a set
    87   * of name-value pairs, where names are <CODE>String</CODE>
    88   * objects, and values are Java primitives. The entries can be accessed
    89   * sequentially or randomly by name. The order of the entries is
    90   * undefined.
    91   * <LI>Text - A <CODE>TextMessage</CODE> object's message body contains a
    92   * <CODE>java.lang.String</CODE> object. This message type can be used
    93   * to transport plain-text messages, and XML messages.
    94   * <LI>Object - An <CODE>ObjectMessage</CODE> object's message body contains
    95   * a <CODE>Serializable</CODE> Java object.
    96   * <LI>Bytes - A <CODE>BytesMessage</CODE> object's message body contains a
    97   * stream of uninterpreted bytes. This message type is for
    98   * literally encoding a body to match an existing message format. In
    99   * many cases, it is possible to use one of the other body types,
    100  * which are easier to use. Although the JMS API allows the use of
    101  * message properties with byte messages, they are typically not used,
    102  * since the inclusion of properties may affect the format.
    103  * </UL>
    104  * <p/>
    105  * <H4>Message Headers</H4>
    106  * <p/>
    107  * <P>The <CODE>JMSCorrelationID</CODE> header field is used for linking one
    108  * message with
    109  * another. It typically links a reply message with its requesting message.
    110  * <p/>
    111  * <P><CODE>JMSCorrelationID</CODE> can hold a provider-specific message ID,
    112  * an application-specific <CODE>String</CODE> object, or a provider-native
    113  * <CODE>byte[]</CODE> value.
    114  * <p/>
    115  * <H4>Message Properties</H4>
    116  * <p/>
    117  * <P>A <CODE>Message</CODE> object contains a built-in facility for supporting
    118  * application-defined property values. In effect, this provides a mechanism
    119  * for adding application-specific header fields to a message.
    120  * <p/>
    121  * <P>Properties allow an application, via message selectors, to have a JMS
    122  * provider select, or filter, messages on its behalf using
    123  * application-specific criteria.
    124  * <p/>
    125  * <P>Property names must obey the rules for a message selector identifier.
    126  * Property names must not be null, and must not be empty strings. If a property
    127  * name is set and it is either null or an empty string, an
    128  * <CODE>IllegalArgumentException</CODE> must be thrown.
    129  * <p/>
    130  * <P>Property values can be <CODE>boolean</CODE>, <CODE>byte</CODE>,
    131  * <CODE>short</CODE>, <CODE>int</CODE>, <CODE>long</CODE>, <CODE>float</CODE>,
    132  * <CODE>double</CODE>, and <CODE>String</CODE>.
    133  * <p/>
    134  * <P>Property values are set prior to sending a message. When a client
    135  * receives a message, its properties are in read-only mode. If a
    136  * client attempts to set properties at this point, a
    137  * <CODE>MessageNotWriteableException</CODE> is thrown. If
    138  * <CODE>clearProperties</CODE> is called, the properties can now be both
    139  * read from and written to. Note that header fields are distinct from
    140  * properties. Header fields are never in read-only mode.
    141  * <p/>
    142  * <P>A property value may duplicate a value in a message's body, or it may
    143  * not. Although JMS does not define a policy for what should or should not
    144  * be made a property, application developers should note that JMS providers
    145  * will likely handle data in a message's body more efficiently than data in
    146  * a message's properties. For best performance, applications should use
    147  * message properties only when they need to customize a message's header.
    148  * The primary reason for doing this is to support customized message
    149  * selection.
    150  * <p/>
    151  * <P>Message properties support the following conversion table. The marked
    152  * cases must be supported. The unmarked cases must throw a
    153  * <CODE>JMSException</CODE>. The <CODE>String</CODE>-to-primitive conversions
    154  * may throw a runtime exception if the
    155  * primitive's <CODE>valueOf</CODE> method does not accept the
    156  * <CODE>String</CODE> as a valid representation of the primitive.
    157  * <p/>
    158  * <P>A value written as the row type can be read as the column type.
    159  * <p/>
    160  * <PRE>
    161  * |        | boolean byte short int long float double String
    162  * |----------------------------------------------------------
    163  * |boolean |    X                                       X
    164  * |byte    |          X     X    X   X                  X
    165  * |short   |                X    X   X                  X
    166  * |int     |                     X   X                  X
    167  * |long    |                         X                  X
    168  * |float   |                               X     X      X
    169  * |double  |                                     X      X
    170  * |String  |    X     X     X    X   X     X     X      X
    171  * |----------------------------------------------------------
    172  * </PRE>
    173  * <p/>
    174  * <P>In addition to the type-specific set/get methods for properties, JMS
    175  * provides the <CODE>setObjectProperty</CODE> and
    176  * <CODE>getObjectProperty</CODE> methods. These support the same set of
    177  * property types using the objectified primitive values. Their purpose is
    178  * to allow the decision of property type to made at execution time rather
    179  * than at compile time. They support the same property value conversions.
    180  * <p/>
    181  * <P>The <CODE>setObjectProperty</CODE> method accepts values of class
    182  * <CODE>Boolean</CODE>, <CODE>Byte</CODE>, <CODE>Short</CODE>,
    183  * <CODE>Integer</CODE>, <CODE>Long</CODE>, <CODE>Float</CODE>,
    184  * <CODE>Double</CODE>, and <CODE>String</CODE>. An attempt
    185  * to use any other class must throw a <CODE>JMSException</CODE>.
    186  * <p/>
    187  * <P>The <CODE>getObjectProperty</CODE> method only returns values of class
    188  * <CODE>Boolean</CODE>, <CODE>Byte</CODE>, <CODE>Short</CODE>,
    189  * <CODE>Integer</CODE>, <CODE>Long</CODE>, <CODE>Float</CODE>,
    190  * <CODE>Double</CODE>, and <CODE>String</CODE>.
    191  * <p/>
    192  * <P>The order of property values is not defined. To iterate through a
    193  * message's property values, use <CODE>getPropertyNames</CODE> to retrieve
    194  * a property name enumeration and then use the various property get methods
    195  * to retrieve their values.
    196  * <p/>
    197  * <P>A message's properties are deleted by the <CODE>clearProperties</CODE>
    198  * method. This leaves the message with an empty set of properties.
    199  * <p/>
    200  * <P>Getting a property value for a name which has not been set returns a
    201  * null value. Only the <CODE>getStringProperty</CODE> and
    202  * <CODE>getObjectProperty</CODE> methods can return a null value.
    203  * Attempting to read a null value as a primitive type must be treated as
    204  * calling the primitive's corresponding <CODE>valueOf(String)</CODE>
    205  * conversion method with a null value.
    206  * <p/>
    207  * <P>The JMS API reserves the <CODE>JMSX</CODE> property name prefix for JMS
    208  * defined properties.
    209  * The full set of these properties is defined in the Java Message Service
    210  * specification. New JMS defined properties may be added in later versions
    211  * of the JMS API.  Support for these properties is optional. The
    212  * <CODE>String[] ConnectionMetaData.getJMSXPropertyNames</CODE> method
    213  * returns the names of the JMSX properties supported by a connection.
    214  * <p/>
    215  * <P>JMSX properties may be referenced in message selectors whether or not
    216  * they are supported by a connection. If they are not present in a
    217  * message, they are treated like any other absent property.
    218  * <p/>
    219  * <P>JMSX properties defined in the specification as "set by provider on
    220  * send" are available to both the producer and the consumers of the message.
    221  * JMSX properties defined in the specification as "set by provider on
    222  * receive" are available only to the consumers.
    223  * <p/>
    224  * <P><CODE>JMSXGroupID</CODE> and <CODE>JMSXGroupSeq</CODE> are standard
    225  * properties that clients
    226  * should use if they want to group messages. All providers must support them.
    227  * Unless specifically noted, the values and semantics of the JMSX properties
    228  * are undefined.
    229  * <p/>
    230  * <P>The JMS API reserves the <CODE>JMS_<I>vendor_name</I></CODE> property
    231  * name prefix for provider-specific properties. Each provider defines its own
    232  * value for <CODE><I>vendor_name</I></CODE>. This is the mechanism a JMS
    233  * provider uses to make its special per-message services available to a JMS
    234  * client.
    235  * <p/>
    236  * <P>The purpose of provider-specific properties is to provide special
    237  * features needed to integrate JMS clients with provider-native clients in a
    238  * single JMS application. They should not be used for messaging between JMS
    239  * clients.
    240  * <p/>
    241  * <H4>Provider Implementations of JMS Message Interfaces</H4>
    242  * <p/>
    243  * <P>The JMS API provides a set of message interfaces that define the JMS
    244  * message
    245  * model. It does not provide implementations of these interfaces.
    246  * <p/>
    247  * <P>Each JMS provider supplies a set of message factories with its
    248  * <CODE>Session</CODE> object for creating instances of messages. This allows
    249  * a provider to use message implementations tailored to its specific needs.
    250  * <p/>
    251  * <P>A provider must be prepared to accept message implementations that are
    252  * not its own. They may not be handled as efficiently as its own
    253  * implementation; however, they must be handled.
    254  * <p/>
    255  * <P>Note the following exception case when a provider is handling a foreign
    256  * message implementation. If the foreign message implementation contains a
    257  * <CODE>JMSReplyTo</CODE> header field that is set to a foreign destination
    258  * implementation, the provider is not required to handle or preserve the
    259  * value of this header field.
    260  * <p/>
    261  * <H4>Message Selectors</H4>
    262  * <p/>
    263  * <P>A JMS message selector allows a client to specify, by
    264  * header field references and property references, the
    265  * messages it is interested in. Only messages whose header
    266  * and property values
    267  * match the
    268  * selector are delivered. What it means for a message not to be delivered
    269  * depends on the <CODE>MessageConsumer</CODE> being used (see
    270  * {@link javax.jms.QueueReceiver QueueReceiver} and
    271  * {@link javax.jms.TopicSubscriber TopicSubscriber}).
    272  * <p/>
    273  * <P>Message selectors cannot reference message body values.
    274  * <p/>
    275  * <P>A message selector matches a message if the selector evaluates to
    276  * true when the message's header field values and property values are
    277  * substituted for their corresponding identifiers in the selector.
    278  * <p/>
    279  * <P>A message selector is a <CODE>String</CODE> whose syntax is based on a
    280  * subset of
    281  * the SQL92 conditional expression syntax. If the value of a message selector
    282  * is an empty string, the value is treated as a null and indicates that there
    283  * is no message selector for the message consumer.
    284  * <p/>
    285  * <P>The order of evaluation of a message selector is from left to right
    286  * within precedence level. Parentheses can be used to change this order.
    287  * <p/>
    288  * <P>Predefined selector literals and operator names are shown here in
    289  * uppercase; however, they are case insensitive.
    290  * <p/>
    291  * <P>A selector can contain:
    292  * <p/>
    293  * <UL>
    294  * <LI>Literals:
    295  * <UL>
    296  * <LI>A string literal is enclosed in single quotes, with a single quote
    297  * represented by doubled single quote; for example,
    298  * <CODE>'literal'</CODE> and <CODE>'literal''s'</CODE>. Like
    299  * string literals in the Java programming language, these use the
    300  * Unicode character encoding.
    301  * <LI>An exact numeric literal is a numeric value without a decimal
    302  * point, such as <CODE>57</CODE>, <CODE>-957</CODE>, and
    303  * <CODE>+62</CODE>; numbers in the range of <CODE>long</CODE> are
    304  * supported. Exact numeric literals use the integer literal
    305  * syntax of the Java programming language.
    306  * <LI>An approximate numeric literal is a numeric value in scientific
    307  * notation, such as <CODE>7E3</CODE> and <CODE>-57.9E2</CODE>, or a
    308  * numeric value with a decimal, such as <CODE>7.</CODE>,
    309  * <CODE>-95.7</CODE>, and <CODE>+6.2</CODE>; numbers in the range of
    310  * <CODE>double</CODE> are supported. Approximate literals use the
    311  * floating-point literal syntax of the Java programming language.
    312  * <LI>The boolean literals <CODE>TRUE</CODE> and <CODE>FALSE</CODE>.
    313  * </UL>
    314  * <LI>Identifiers:
    315  * <UL>
    316  * <LI>An identifier is an unlimited-length sequence of letters
    317  * and digits, the first of which must be a letter. A letter is any
    318  * character for which the method <CODE>Character.isJavaLetter</CODE>
    319  * returns true. This includes <CODE>'_'</CODE> and <CODE>'$'</CODE>.
    320  * A letter or digit is any character for which the method
    321  * <CODE>Character.isJavaLetterOrDigit</CODE> returns true.
    322  * <LI>Identifiers cannot be the names <CODE>NULL</CODE>,
    323  * <CODE>TRUE</CODE>, and <CODE>FALSE</CODE>.
    324  * <LI>Identifiers cannot be <CODE>NOT</CODE>, <CODE>AND</CODE>,
    325  * <CODE>OR</CODE>, <CODE>BETWEEN</CODE>, <CODE>LIKE</CODE>,
    326  * <CODE>IN</CODE>, <CODE>IS</CODE>, or <CODE>ESCAPE</CODE>.
    327  * <LI>Identifiers are either header field references or property
    328  * references.  The type of a property value in a message selector
    329  * corresponds to the type used to set the property. If a property
    330  * that does not exist in a message is referenced, its value is
    331  * <CODE>NULL</CODE>.
    332  * <LI>The conversions that apply to the get methods for properties do not
    333  * apply when a property is used in a message selector expression.
    334  * For example, suppose you set a property as a string value, as in the
    335  * following:
    336  * <PRE>myMessage.setStringProperty("NumberOfOrders", "2");</PRE>
    337  * The following expression in a message selector would evaluate to
    338  * false, because a string cannot be used in an arithmetic expression:
    339  * <PRE>"NumberOfOrders > 1"</PRE>
    340  * <LI>Identifiers are case-sensitive.
    341  * <LI>Message header field references are restricted to
    342  * <CODE>JMSDeliveryMode</CODE>, <CODE>JMSPriority</CODE>,
    343  * <CODE>JMSMessageID</CODE>, <CODE>JMSTimestamp</CODE>,
    344  * <CODE>JMSCorrelationID</CODE>, and <CODE>JMSType</CODE>.
    345  * <CODE>JMSMessageID</CODE>, <CODE>JMSCorrelationID</CODE>, and
    346  * <CODE>JMSType</CODE> values may be null and if so are treated as a
    347  * <CODE>NULL</CODE> value.
    348  * <LI>Any name beginning with <CODE>'JMSX'</CODE> is a JMS defined
    349  * property name.
    350  * <LI>Any name beginning with <CODE>'JMS_'</CODE> is a provider-specific
    351  * property name.
    352  * <LI>Any name that does not begin with <CODE>'JMS'</CODE> is an
    353  * application-specific property name.
    354  * </UL>
    355  * <LI>White space is the same as that defined for the Java programming
    356  * language: space, horizontal tab, form feed, and line terminator.
    357  * <LI>Expressions:
    358  * <UL>
    359  * <LI>A selector is a conditional expression; a selector that evaluates
    360  * to <CODE>true</CODE> matches; a selector that evaluates to
    361  * <CODE>false</CODE> or unknown does not match.
    362  * <LI>Arithmetic expressions are composed of themselves, arithmetic
    363  * operations, identifiers (whose value is treated as a numeric
    364  * literal), and numeric literals.
    365  * <LI>Conditional expressions are composed of themselves, comparison
    366  * operations, and logical operations.
    367  * </UL>
    368  * <LI>Standard bracketing <CODE>()</CODE> for ordering expression evaluation
    369  * is supported.
    370  * <LI>Logical operators in precedence order: <CODE>NOT</CODE>,
    371  * <CODE>AND</CODE>, <CODE>OR</CODE>
    372  * <LI>Comparison operators: <CODE>=</CODE>, <CODE>></CODE>, <CODE>>=</CODE>,
    373  * <CODE><</CODE>, <CODE><=</CODE>, <CODE><></CODE> (not equal)
    374  * <UL>
    375  * <LI>Only like type values can be compared. One exception is that it
    376  * is valid to compare exact numeric values and approximate numeric
    377  * values; the type conversion required is defined by the rules of
    378  * numeric promotion in the Java programming language. If the
    379  * comparison of non-like type values is attempted, the value of the
    380  * operation is false. If either of the type values evaluates to
    381  * <CODE>NULL</CODE>, the value of the expression is unknown.
    382  * <LI>String and boolean comparison is restricted to <CODE>=</CODE> and
    383  * <CODE><></CODE>. Two strings are equal
    384  * if and only if they contain the same sequence of characters.
    385  * </UL>
    386  * <LI>Arithmetic operators in precedence order:
    387  * <UL>
    388  * <LI><CODE>+</CODE>, <CODE>-</CODE> (unary)
    389  * <LI><CODE>*</CODE>, <CODE>/</CODE> (multiplication and division)
    390  * <LI><CODE>+</CODE>, <CODE>-</CODE> (addition and subtraction)
    391  * <LI>Arithmetic operations must use numeric promotion in the Java
    392  * programming language.
    393  * </UL>
    394  * <LI><CODE><I>arithmetic-expr1</I> [NOT] BETWEEN <I>arithmetic-expr2</I>
    395  * AND <I>arithmetic-expr3</I></CODE> (comparison operator)
    396  * <UL>
    397  * <LI><CODE>"age&nbsp;BETWEEN&nbsp;15&nbsp;AND&nbsp;19"</CODE> is
    398  * equivalent to
    399  * <CODE>"age&nbsp;>=&nbsp;15&nbsp;AND&nbsp;age&nbsp;<=&nbsp;19"</CODE>
    400  * <LI><CODE>"age&nbsp;NOT&nbsp;BETWEEN&nbsp;15&nbsp;AND&nbsp;19"</CODE>
    401  * is equivalent to
    402  * <CODE>"age&nbsp;<&nbsp;15&nbsp;OR&nbsp;age&nbsp;>&nbsp;19"</CODE>
    403  * </UL>
    404  * <LI><CODE><I>identifier</I> [NOT] IN (<I>string-literal1</I>,
    405  * <I>string-literal2</I>,...)</CODE> (comparison operator where
    406  * <CODE><I>identifier</I></CODE> has a <CODE>String</CODE> or
    407  * <CODE>NULL</CODE> value)
    408  * <UL>
    409  * <LI><CODE>"Country&nbsp;IN&nbsp;('&nbsp;UK',&nbsp;'US',&nbsp;'France')"</CODE>
    410  * is true for
    411  * <CODE>'UK'</CODE> and false for <CODE>'Peru'</CODE>; it is
    412  * equivalent to the expression
    413  * <CODE>"(Country&nbsp;=&nbsp;'&nbsp;UK')&nbsp;OR&nbsp;(Country&nbsp;=&nbsp;'&nbsp;US')&nbsp;OR&nbsp;(Country&nbsp;=&nbsp;'&nbsp;France')"</CODE>
    414  * <LI><CODE>"Country&nbsp;NOT&nbsp;IN&nbsp;('&nbsp;UK',&nbsp;'US',&nbsp;'France')"</CODE>
    415  * is false for <CODE>'UK'</CODE> and true for <CODE>'Peru'</CODE>; it
    416  * is equivalent to the expression
    417  * <CODE>"NOT&nbsp;((Country&nbsp;=&nbsp;'&nbsp;UK')&nbsp;OR&nbsp;(Country&nbsp;=&nbsp;'&nbsp;US')&nbsp;OR&nbsp;(Country&nbsp;=&nbsp;'&nbsp;France'))"</CODE>
    418  * <LI>If identifier of an <CODE>IN</CODE> or <CODE>NOT IN</CODE>
    419  * operation is <CODE>NULL</CODE>, the value of the operation is
    420  * unknown.
    421  * </UL>
    422  * <LI><CODE><I>identifier</I> [NOT] LIKE <I>pattern-value</I> [ESCAPE
    423  * <I>escape-character</I>]</CODE> (comparison operator, where
    424  * <CODE><I>identifier</I></CODE> has a <CODE>String</CODE> value;
    425  * <CODE><I>pattern-value</I></CODE> is a string literal where
    426  * <CODE>'_'</CODE> stands for any single character; <CODE>'%'</CODE>
    427  * stands for any sequence of characters, including the empty sequence;
    428  * and all other characters stand for themselves. The optional
    429  * <CODE><I>escape-character</I></CODE> is a single-character string
    430  * literal whose character is used to escape the special meaning of the
    431  * <CODE>'_'</CODE> and <CODE>'%'</CODE> in
    432  * <CODE><I>pattern-value</I></CODE>.)
    433  * <UL>
    434  * <LI><CODE>"phone&nbsp;LIKE&nbsp;'12%3'"</CODE> is true for
    435  * <CODE>'123'</CODE> or <CODE>'12993'</CODE> and false for
    436  * <CODE>'1234'</CODE>
    437  * <LI><CODE>"word&nbsp;LIKE&nbsp;'l_se'"</CODE> is true for
    438  * <CODE>'lose'</CODE> and false for <CODE>'loose'</CODE>
    439  * <LI><CODE>"underscored&nbsp;LIKE&nbsp;'\_%'&nbsp;ESCAPE&nbsp;'\'"</CODE>
    440  * is true for <CODE>'_foo'</CODE> and false for <CODE>'bar'</CODE>
    441  * <LI><CODE>"phone&nbsp;NOT&nbsp;LIKE&nbsp;'12%3'"</CODE> is false for
    442  * <CODE>'123'</CODE> or <CODE>'12993'</CODE> and true for
    443  * <CODE>'1234'</CODE>
    444  * <LI>If <CODE><I>identifier</I></CODE> of a <CODE>LIKE</CODE> or
    445  * <CODE>NOT LIKE</CODE> operation is <CODE>NULL</CODE>, the value
    446  * of the operation is unknown.
    447  * </UL>
    448  * <LI><CODE><I>identifier</I> IS NULL</CODE> (comparison operator that tests
    449  * for a null header field value or a missing property value)
    450  * <UL>
    451  * <LI><CODE>"prop_name&nbsp;IS&nbsp;NULL"</CODE>
    452  * </UL>
    453  * <LI><CODE><I>identifier</I> IS NOT NULL</CODE> (comparison operator that
    454  * tests for the existence of a non-null header field value or a property
    455  * value)
    456  * <UL>
    457  * <LI><CODE>"prop_name&nbsp;IS&nbsp;NOT&nbsp;NULL"</CODE>
    458  * </UL>
    459  * <p/>
    460  * <P>JMS providers are required to verify the syntactic correctness of a
    461  * message selector at the time it is presented. A method that provides a
    462  * syntactically incorrect selector must result in a <CODE>JMSException</CODE>.
    463  * JMS providers may also optionally provide some semantic checking at the time
    464  * the selector is presented. Not all semantic checking can be performed at
    465  * the time a message selector is presented, because property types are not known.
    466  * <p/>
    467  * <P>The following message selector selects messages with a message type
    468  * of car and color of blue and weight greater than 2500 pounds:
    469  * <p/>
    470  * <PRE>"JMSType&nbsp;=&nbsp;'car'&nbsp;AND&nbsp;color&nbsp;=&nbsp;'blue'&nbsp;AND&nbsp;weight&nbsp;>&nbsp;2500"</PRE>
    471  * <p/>
    472  * <H4>Null Values</H4>
    473  * <p/>
    474  * <P>As noted above, property values may be <CODE>NULL</CODE>. The evaluation
    475  * of selector expressions containing <CODE>NULL</CODE> values is defined by
    476  * SQL92 <CODE>NULL</CODE> semantics. A brief description of these semantics
    477  * is provided here.
    478  * <p/>
    479  * <P>SQL treats a <CODE>NULL</CODE> value as unknown. Comparison or arithmetic
    480  * with an unknown value always yields an unknown value.
    481  * <p/>
    482  * <P>The <CODE>IS NULL</CODE> and <CODE>IS NOT NULL</CODE> operators convert
    483  * an unknown value into the respective <CODE>TRUE</CODE> and
    484  * <CODE>FALSE</CODE> values.
    485  * <p/>
    486  * <P>The boolean operators use three-valued logic as defined by the
    487  * following tables:
    488  * <p/>
    489  * <P><B>The definition of the <CODE>AND</CODE> operator</B>
    490  * <p/>
    491  * <PRE>
    492  * | AND  |   T   |   F   |   U
    493  * +------+-------+-------+-------
    494  * |  T   |   T   |   F   |   U
    495  * |  F   |   F   |   F   |   F
    496  * |  U   |   U   |   F   |   U
    497  * +------+-------+-------+-------
    498  * </PRE>
    499  * <p/>
    500  * <P><B>The definition of the <CODE>OR</CODE> operator</B>
    501  * <p/>
    502  * <PRE>
    503  * | OR   |   T   |   F   |   U
    504  * +------+-------+-------+--------
    505  * |  T   |   T   |   T   |   T
    506  * |  F   |   T   |   F   |   U
    507  * |  U   |   T   |   U   |   U
    508  * +------+-------+-------+-------
    509  * </PRE>
    510  * <p/>
    511  * <P><B>The definition of the <CODE>NOT</CODE> operator</B>
    512  * <p/>
    513  * <PRE>
    514  * | NOT
    515  * +------+------
    516  * |  T   |   F
    517  * |  F   |   T
    518  * |  U   |   U
    519  * +------+-------
    520  * </PRE>
    521  * <p/>
    522  * <H4>Special Notes</H4>
    523  * <p/>
    524  * <P>When used in a message selector, the <CODE>JMSDeliveryMode</CODE> header
    525  * field is treated as having the values <CODE>'PERSISTENT'</CODE> and
    526  * <CODE>'NON_PERSISTENT'</CODE>.
    527  * <p/>
    528  * <P>Date and time values should use the standard <CODE>long</CODE>
    529  * millisecond value. When a date or time literal is included in a message
    530  * selector, it should be an integer literal for a millisecond value. The
    531  * standard way to produce millisecond values is to use
    532  * <CODE>java.util.Calendar</CODE>.
    533  * <p/>
    534  * <P>Although SQL supports fixed decimal comparison and arithmetic, JMS
    535  * message selectors do not. This is the reason for restricting exact
    536  * numeric literals to those without a decimal (and the addition of
    537  * numerics with a decimal as an alternate representation for
    538  * approximate numeric values).
    539  * <p/>
    540  * <P>SQL comments are not supported.
    541  *
    542  * @version $Revision: 1.34 $
    543  * @see javax.jms.MessageConsumer#receive()
    544  * @see javax.jms.MessageConsumer#receive(long)
    545  * @see javax.jms.MessageConsumer#receiveNoWait()
    546  * @see javax.jms.MessageListener#onMessage(Message)
    547  * @see javax.jms.BytesMessage
    548  * @see javax.jms.MapMessage
    549  * @see javax.jms.ObjectMessage
    550  * @see javax.jms.StreamMessage
    551  * @see javax.jms.TextMessage
    552  */
    553
    554 public class ActiveMQMessage extends AbstractPacket implements Message, Comparable {
    555
    556     /***
    557      * The message producer's default delivery mode is <CODE>PERSISTENT</CODE>.
    558      *
    559      * @see DeliveryMode#PERSISTENT
    560      */
    561     static final int DEFAULT_DELIVERY_MODE = DeliveryMode.PERSISTENT;
    562
    563     /***
    564      * The message producer's default priority is 4.
    565      */
    566     static final int DEFAULT_PRIORITY = 4;
    567
    568     /***
    569      * The message producer's default time to live is unlimited; the message
    570      * never expires.
    571      */
    572     static final long DEFAULT_TIME_TO_LIVE = 0;
    573
    574     /***
    575      * message property types
    576      */
    577     final static byte EOF = 2;
    578     final static byte BYTES = 3;
    579     final static byte STRING = 4;
    580     final static byte BOOLEAN = 5;
    581     final static byte CHAR = 6;
    582     final static byte BYTE = 7;
    583     final static byte SHORT = 8;
    584     final static byte INT = 9;
    585     final static byte LONG = 10;
    586     final static byte FLOAT = 11;
    587     final static byte DOUBLE = 12;
    588     final static byte NULL = 13;
    589
    590     /***
    591      * Message flag indexes (used for writing/reading to/from a Stream
    592      */
    593     static final int CORRELATION_INDEX = 2;
    594     static final int TYPE_INDEX = 3;
    595     static final int BROKER_NAME_INDEX = 4;
    596     static final int CLUSTER_NAME_INDEX = 5;
    597     static final int TRANSACTION_ID_INDEX = 6;
    598     static final int REPLY_TO_INDEX = 7;
    599     static final int TIMESTAMP_INDEX = 8;
    600     static final int EXPIRATION_INDEX = 9;
    601     static final int REDELIVERED_INDEX = 10;
    602     static final int XA_TRANS_INDEX = 11;
    603     static final int CID_INDEX = 12;
    604     static final int PROPERTIES_INDEX = 13;
    605     static final int PAYLOAD_INDEX = 15;
    606
    607
    608     /***
    609      * <code>readOnlyMessage</code> denotes if the message is read only
    610      */
    611     protected boolean readOnlyMessage;
    612
    613     //private String jmsMessageID;
    614     private String jmsClientID;
    615     private String jmsCorrelationID;
    616     private ActiveMQDestination jmsDestination;
    617     private ActiveMQDestination jmsReplyTo;
    618     private int jmsDeliveryMode = DEFAULT_DELIVERY_MODE;
    619     private boolean jmsRedelivered;
    620     private String jmsType;
    621     private long jmsExpiration;
    622     private int jmsPriority = DEFAULT_PRIORITY;
    623     private long jmsTimestamp;
    624     private Hashtable properties;
    625     private boolean readOnlyProperties;
    626     private String entryBrokerName;
    627     private String entryClusterName;
    628     private int[] consumerNos; //these are set by the broker, and only relevant to consuming connections
    629     private String producerID;//set by the Producer on sending the message
    630     private String transactionId;
    631     private boolean xaTransacted;
    632     private String consumerId; //this is only used on the Client for acknowledging receipt of a message
    633     private boolean messageConsumed;//only used on the client - to denote if its been delivered and read
    634     private boolean transientConsumed;//only used on the client - to denote if its been delivered and read
    635
    636     private MessageAcknowledge messageAcknowledge;
    637     private byte[] bodyAsBytes;
    638     private MessageIdentity jmsMessageIdentity;
    639
    640
    641     /***
    642      * Retrieve if a JMS Message type or not
    643      *
    644      * @return true if it is a JMS Message
    645      */
    646     public boolean isJMSMessage() {
    647         return true;
    648     }
    649
    650
    651     /***
    652      * @return pretty print of this Message
    653      */
    654     public String toString() {
    655         return getPacketTypeAsString(getPacketType()) + ": dest = " + jmsDestination + ", id = " + getId();
    656     }
    657
    658
    659     /***
    660      * @return Returns the messageAcknowledge.
    661      */
    662     public MessageAcknowledge getMessageAcknowledge() {
    663         return messageAcknowledge;
    664     }
    665
    666     /***
    667      * @param messageAcknowledge The messageAcknowledge to set.
    668      */
    669     public void setMessageAcknowledge(MessageAcknowledge messageAcknowledge) {
    670         this.messageAcknowledge = messageAcknowledge;
    671     }
    672
    673     /***
    674      * Return the type of Packet
    675      *
    676      * @return integer representation of the type of Packet
    677      */
    678
    679     public int getPacketType() {
    680         return ACTIVEMQ_MESSAGE;
    681     }
    682
    683
    684     /***
    685      * set the message readOnly
    686      *
    687      * @param value
    688      */
    689     public void setReadOnly(boolean value) {
    690         this.readOnlyProperties = value;
    691         this.readOnlyMessage = value;
    692     }
    693
    694     /***
    695      * test to see if a particular Consumer at a Connection
    696      * is meant to receive this Message
    697      *
    698      * @param consumerNumber
    699      * @return true if a target
    700      */
    701
    702     public boolean isConsumerTarget(int consumerNumber) {
    703         if (consumerNos != null) {
    704             for (int i = 0; i < consumerNos.length; i++) {
    705                 if (consumerNos[i] == consumerNumber) {
    706                     return true;
    707                 }
    708             }
    709         }
    710         return false;
    711     }
    712
    713     /***
    714      * @return consumer Nos as a String
    715      */
    716     public String getConsumerNosAsString() {
    717         String result = "";
    718         if (consumerNos != null) {
    719             for (int i = 0; i < consumerNos.length; i++) {
    720                 result += consumerNos[i] + ",";
    721             }
    722         }
    723         return result;
    724     }
    725
    726     /***
    727      * @return true if the message is non-persistent or intended for a temporary destination
    728      */
    729     public boolean isTemporary() {
    730         return jmsDeliveryMode == DeliveryMode.NON_PERSISTENT ||
    731                 (jmsDestination != null && jmsDestination.isTemporary());
    732     }
    733
    734     /***
    735      * @return Returns hash code for this instance
    736      */
    737
    738     public int hashCode() {
    739         return this.getId() != null ? this.getId().hashCode() : super.hashCode();
    740     }
    741
    742     /***
    743      * Returns true if this instance is equivalent to obj
    744      *
    745      * @param obj the other instance to test
    746      * @return true/false
    747      */
    748
    749     public boolean equals(Object obj) {
    750         boolean result = obj == this;
    751         if (!result && obj != null && obj instanceof ActiveMQMessage) {
    752             ActiveMQMessage other = (ActiveMQMessage) obj;
    753             result = this.getId() == other.getId() ||
    754                     (this.getId() != null && other.getId() != null &&
    755                     this.getId().equals(other.getId()));
    756         }
    757         return result;
    758     }
    759
    760     /***
    761      * @param o object to compare
    762      * @return 1 if this > o else 0 if they are equal or -1 if this < o
    763      */
    764     public int compareTo(Object o) {
    765         if (o instanceof ActiveMQMessage) {
    766             return compareTo((ActiveMQMessage) o);
    767         }
    768         return -1;
    769     }
    770
    771     /***
    772      * Sorted by destination and then messageId
    773      *
    774      * @param that another message to compare against
    775      * @return 1 if this > that else 0 if they are equal or -1 if this < that
    776      */
    777     public int compareTo(ActiveMQMessage that) {
    778         int answer = 1;
    779
    780         if (that != null && this.jmsDestination != null && that.jmsDestination != null) {
    781             answer = this.jmsDestination.compareTo(that.jmsDestination);
    782             if (answer == 0) {
    783                 if (this.getId() != null && that.getId() != null) {
    784                     answer = IdGenerator.compare(this.getId(), that.getId());
    785                 }
    786                 else {
    787                     answer = 1;
    788                 }
    789             }
    790         }
    791         return answer;
    792     }
    793
    794
    795     /***
    796      * @return Returns a shallow copy of the message instance
    797      * @throws JMSException
    798      */
    799
    800     public ActiveMQMessage shallowCopy() throws JMSException {
    801         ActiveMQMessage other = new ActiveMQMessage();
    802         this.initializeOther(other);
    803         return other;
    804     }
    805
    806     /***
    807      * @return Returns a deep copy of the message - note the header fields are only shallow copied
    808      * @throws JMSException
    809      */
    810
    811     public ActiveMQMessage deepCopy() throws JMSException {
    812         return shallowCopy();
    813     }
    814
    815
    816     /***
    817      * Indicates if the Message has expired
    818      *
    819      * @param currentTime -
    820      *                    the current time in milliseconds
    821      * @return true if the message can be expired
    822      */
    823     public boolean isExpired(long currentTime) {
    824         boolean result = false;
    825         long expiration = this.jmsExpiration;
    826         if (expiration > 0 && expiration < currentTime) {
    827             result = true;
    828         }
    829         return result;
    830     }
    831
    832     /***
    833      * @return true if the message is expired
    834      */
    835     public boolean isExpired() {
    836         return isExpired(System.currentTimeMillis());
    837     }
    838
    839     /***
    840      * Initializes another message with current values from this instance
    841      *
    842      * @param other the other ActiveMQMessage to initialize
    843      */
    844     protected void initializeOther(ActiveMQMessage other) {
    845         super.initializeOther(other);
    846         other.jmsClientID = this.jmsClientID;
    847         other.jmsCorrelationID = this.jmsCorrelationID;
    848         other.jmsDestination = this.jmsDestination;
    849         other.jmsReplyTo = this.jmsReplyTo;
    850         other.jmsDeliveryMode = this.jmsDeliveryMode;
    851         other.jmsRedelivered = this.jmsRedelivered;
    852         other.jmsType = this.jmsType;
    853         other.jmsExpiration = this.jmsExpiration;
    854         other.jmsPriority = this.jmsPriority;
    855         other.jmsTimestamp = this.jmsTimestamp;
    856         other.properties = this.properties;
    857         other.readOnlyProperties = this.readOnlyProperties;
    858         other.readOnlyMessage = this.readOnlyMessage;
    859         other.entryBrokerName = this.entryBrokerName;
    860         other.entryClusterName = this.entryClusterName;
    861         other.consumerNos = this.consumerNos;
    862         other.producerID = this.producerID;
    863         other.transactionId = this.transactionId;
    864         other.xaTransacted = this.xaTransacted;
    865         other.bodyAsBytes = this.bodyAsBytes;
    866         other.messageAcknowledge = this.messageAcknowledge;
    867         other.jmsMessageIdentity = this.jmsMessageIdentity;
    868     }
    869
    870
    871     /***
    872      * Gets the message ID.
    873      * <p/>
    874      * <P>The <CODE>JMSMessageID</CODE> header field contains a value that
    875      * uniquely identifies each message sent by a provider.
    876      * <p/>
    877      * <P>When a message is sent, <CODE>JMSMessageID</CODE> can be ignored.
    878      * When the <CODE>send</CODE> or <CODE>publish</CODE> method returns, it
    879      * contains a provider-assigned value.
    880      * <p/>
    881      * <P>A <CODE>JMSMessageID</CODE> is a <CODE>String</CODE> value that
    882      * should function as a
    883      * unique key for identifying messages in a historical repository.
    884      * The exact scope of uniqueness is provider-defined. It should at
    885      * least cover all messages for a specific installation of a
    886      * provider, where an installation is some connected set of message
    887      * routers.
    888      * <p/>
    889      * <P>All <CODE>JMSMessageID</CODE> values must start with the prefix
    890      * <CODE>'ID:'</CODE>.
    891      * Uniqueness of message ID values across different providers is
    892      * not required.
    893      * <p/>
    894      * <P>Since message IDs take some effort to create and increase a
    895      * message's size, some JMS providers may be able to optimize message
    896      * overhead if they are given a hint that the message ID is not used by
    897      * an application. By calling the
    898      * <CODE>MessageProducer.setDisableMessageID</CODE> method, a JMS client
    899      * enables this potential optimization for all messages sent by that
    900      * message producer. If the JMS provider accepts this
    901      * hint, these messages must have the message ID set to null; if the
    902      * provider ignores the hint, the message ID must be set to its normal
    903      * unique value.
    904      *
    905      * @return the message ID
    906      * @see javax.jms.Message#setJMSMessageID(String)
    907      * @see javax.jms.MessageProducer#setDisableMessageID(boolean)
    908      */
    909
    910     public String getJMSMessageID() {
    911         return getId();
    912     }
    913
    914
    915     /***
    916      * Sets the message ID.
    917      * <p/>
    918      * <P>JMS providers set this field when a message is sent. This method
    919      * can be used to change the value for a message that has been received.
    920      *
    921      * @param id the ID of the message
    922      * @see javax.jms.Message#getJMSMessageID()
    923      */
    924
    925     public void setJMSMessageID(String id) {
    926         setId(id);
    927         this.jmsMessageIdentity = null;
    928     }
    929
    930
    931     /***
    932      * Gets the message timestamp.
    933      * <p/>
    934      * <P>The <CODE>JMSTimestamp</CODE> header field contains the time a
    935      * message was
    936      * handed off to a provider to be sent. It is not the time the
    937      * message was actually transmitted, because the actual send may occur
    938      * later due to transactions or other client-side queueing of messages.
    939      * <p/>
    940      * <P>When a message is sent, <CODE>JMSTimestamp</CODE> is ignored. When
    941      * the <CODE>send</CODE> or <CODE>publish</CODE>
    942      * method returns, it contains a time value somewhere in the interval
    943      * between the call and the return. The value is in the format of a normal
    944      * millis time value in the Java programming language.
    945      * <p/>
    946      * <P>Since timestamps take some effort to create and increase a
    947      * message's size, some JMS providers may be able to optimize message
    948      * overhead if they are given a hint that the timestamp is not used by an
    949      * application. By calling the
    950      * <CODE>MessageProducer.setDisableMessageTimestamp</CODE> method, a JMS
    951      * client enables this potential optimization for all messages sent by
    952      * that message producer. If the JMS provider accepts this
    953      * hint, these messages must have the timestamp set to zero; if the
    954      * provider ignores the hint, the timestamp must be set to its normal
    955      * value.
    956      *
    957      * @return the message timestamp
    958      * @see javax.jms.Message#setJMSTimestamp(long)
    959      * @see javax.jms.MessageProducer#setDisableMessageTimestamp(boolean)
    960      */
    961
    962     public long getJMSTimestamp() {
    963         return jmsTimestamp;
    964     }
    965
    966
    967     /***
    968      * Sets the message timestamp.
    969      * <p/>
    970      * <P>JMS providers set this field when a message is sent. This method
    971      * can be used to change the value for a message that has been received.
    972      *
    973      * @param timestamp the timestamp for this message
    974      * @see javax.jms.Message#getJMSTimestamp()
    975      */
    976
    977     public void setJMSTimestamp(long timestamp) {
    978         this.jmsTimestamp = timestamp;
    979     }
    980
    981
    982     /***
    983      * Gets the correlation ID as an array of bytes for the message.
    984      * <p/>
    985      * <P>The use of a <CODE>byte[]</CODE> value for
    986      * <CODE>JMSCorrelationID</CODE> is non-portable.
    987      *
    988      * @return the correlation ID of a message as an array of bytes
    989      * @see javax.jms.Message#setJMSCorrelationID(String)
    990      * @see javax.jms.Message#getJMSCorrelationID()
    991      * @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[])
    992      */
    993
    994     public byte[] getJMSCorrelationIDAsBytes() {
    995         return this.jmsCorrelationID != null ? this.jmsCorrelationID.getBytes() : null;
    996     }
    997
    998
    999     /***
    1000      * Sets the correlation ID as an array of bytes for the message.
    1001      * <p/>
    1002      * <P>The array is copied before the method returns, so
    1003      * future modifications to the array will not alter this message header.
    1004      * <p/>
    1005      * <P>If a provider supports the native concept of correlation ID, a
    1006      * JMS client may need to assign specific <CODE>JMSCorrelationID</CODE>
    1007      * values to match those expected by native messaging clients.
    1008      * JMS providers without native correlation ID values are not required to
    1009      * support this method and its corresponding get method; their
    1010      * implementation may throw a
    1011      * <CODE>java.lang.UnsupportedOperationException</CODE>.
    1012      * <p/>
    1013      * <P>The use of a <CODE>byte[]</CODE> value for
    1014      * <CODE>JMSCorrelationID</CODE> is non-portable.
    1015      *
    1016      * @param correlationID the correlation ID value as an array of bytes
    1017      * @see javax.jms.Message#setJMSCorrelationID(String)
    1018      * @see javax.jms.Message#getJMSCorrelationID()
    1019      * @see javax.jms.Message#getJMSCorrelationIDAsBytes()
    1020      */
    1021
    1022     public void setJMSCorrelationIDAsBytes(byte[] correlationID) {
    1023         if (correlationID == null) {
    1024             this.jmsCorrelationID = null;
    1025         }
    1026         else {
    1027             this.jmsCorrelationID = new String(correlationID);
    1028         }
    1029     }
    1030
    1031
    1032     /***
    1033      * Sets the correlation ID for the message.
    1034      * <p/>
    1035      * <P>A client can use the <CODE>JMSCorrelationID</CODE> header field to
    1036      * link one message with another. A typical use is to link a response
    1037      * message with its request message.
    1038      * <p/>
    1039      * <P><CODE>JMSCorrelationID</CODE> can hold one of the following:
    1040      * <UL>
    1041      * <LI>A provider-specific message ID
    1042      * <LI>An application-specific <CODE>String</CODE>
    1043      * <LI>A provider-native <CODE>byte[]</CODE> value
    1044      * </UL>
    1045      * <p/>
    1046      * <P>Since each message sent by a JMS provider is assigned a message ID
    1047      * value, it is convenient to link messages via message ID. All message ID
    1048      * values must start with the <CODE>'ID:'</CODE> prefix.
    1049      * <p/>
    1050      * <P>In some cases, an application (made up of several clients) needs to
    1051      * use an application-specific value for linking messages. For instance,
    1052      * an application may use <CODE>JMSCorrelationID</CODE> to hold a value
    1053      * referencing some external information. Application-specified values
    1054      * must not start with the <CODE>'ID:'</CODE> prefix; this is reserved for
    1055      * provider-generated message ID values.
    1056      * <p/>
    1057      * <P>If a provider supports the native concept of correlation ID, a JMS
    1058      * client may need to assign specific <CODE>JMSCorrelationID</CODE> values
    1059      * to match those expected by clients that do not use the JMS API. A
    1060      * <CODE>byte[]</CODE> value is used for this
    1061      * purpose. JMS providers without native correlation ID values are not
    1062      * required to support <CODE>byte[]</CODE> values. The use of a
    1063      * <CODE>byte[]</CODE> value for <CODE>JMSCorrelationID</CODE> is
    1064      * non-portable.
    1065      *
    1066      * @param correlationID the message ID of a message being referred to
    1067      * @see javax.jms.Message#getJMSCorrelationID()
    1068      * @see javax.jms.Message#getJMSCorrelationIDAsBytes()
    1069      * @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[])
    1070      */
    1071
    1072     public void setJMSCorrelationID(String correlationID) {
    1073         this.jmsCorrelationID = correlationID;
    1074     }
    1075
    1076
    1077     /***
    1078      * Gets the correlation ID for the message.
    1079      * <p/>
    1080      * <P>This method is used to return correlation ID values that are
    1081      * either provider-specific message IDs or application-specific
    1082      * <CODE>String</CODE> values.
    1083      *
    1084      * @return the correlation ID of a message as a <CODE>String</CODE>
    1085      * @see javax.jms.Message#setJMSCorrelationID(String)
    1086      * @see javax.jms.Message#getJMSCorrelationIDAsBytes()
    1087      * @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[])
    1088      */
    1089
    1090     public String getJMSCorrelationID() {
    1091         return this.jmsCorrelationID;
    1092     }
    1093
    1094
    1095     /***
    1096      * Gets the <CODE>Destination</CODE> object to which a reply to this
    1097      * message should be sent.
    1098      *
    1099      * @return <CODE>Destination</CODE> to which to send a response to this
    1100      *         message
    1101      * @see javax.jms.Message#setJMSReplyTo(Destination)
    1102      */
    1103
    1104     public Destination getJMSReplyTo() {
    1105         return this.jmsReplyTo;
    1106
    1107     }
    1108
    1109
    1110     /***
    1111      * Sets the <CODE>Destination</CODE> object to which a reply to this
    1112      * message should be sent.
    1113      * <p/>
    1114      * <P>The <CODE>JMSReplyTo</CODE> header field contains the destination
    1115      * where a reply
    1116      * to the current message should be sent. If it is null, no reply is
    1117      * expected. The destination may be either a <CODE>Queue</CODE> object or
    1118      * a <CODE>Topic</CODE> object.
    1119      * <p/>
    1120      * <P>Messages sent with a null <CODE>JMSReplyTo</CODE> value may be a
    1121      * notification of some event, or they may just be some data the sender
    1122      * thinks is of interest.
    1123      * <p/>
    1124      * <P>Messages with a <CODE>JMSReplyTo</CODE> value typically expect a
    1125      * response. A response is optional; it is up to the client to decide.
    1126      * These messages are called requests. A message sent in response to a
    1127      * request is called a reply.
    1128      * <p/>
    1129      * <P>In some cases a client may wish to match a request it sent earlier
    1130      * with a reply it has just received. The client can use the
    1131      * <CODE>JMSCorrelationID</CODE> header field for this purpose.
    1132      *
    1133      * @param replyTo <CODE>Destination</CODE> to which to send a response to
    1134      *                this message
    1135      * @see javax.jms.Message#getJMSReplyTo()
    1136      */
    1137
    1138     public void setJMSReplyTo(Destination replyTo) {
    1139         this.jmsReplyTo = (ActiveMQDestination) replyTo;
    1140     }
    1141
    1142
    1143     /***
    1144      * Gets the <CODE>Destination</CODE> object for this message.
    1145      * <p/>
    1146      * <P>The <CODE>JMSDestination</CODE> header field contains the
    1147      * destination to which the message is being sent.
    1148      * <p/>
    1149      * <P>When a message is sent, this field is ignored. After completion
    1150      * of the <CODE>send</CODE> or <CODE>publish</CODE> method, the field
    1151      * holds the destination specified by the method.
    1152      * <p/>
    1153      * <P>When a message is received, its <CODE>JMSDestination</CODE> value
    1154      * must be equivalent to the value assigned when it was sent.
    1155      *
    1156      * @return the destination of this message
    1157      * @see javax.jms.Message#setJMSDestination(Destination)
    1158      */
    1159
    1160     public Destination getJMSDestination() {
    1161         return this.jmsDestination;
    1162     }
    1163
    1164
    1165     /***
    1166      * Sets the <CODE>Destination</CODE> object for this message.
    1167      * <p/>
    1168      * <P>JMS providers set this field when a message is sent. This method
    1169      * can be used to change the value for a message that has been received.
    1170      *
    1171      * @param destination the destination for this message
    1172      * @see javax.jms.Message#getJMSDestination()
    1173      */
    1174
    1175     public void setJMSDestination(Destination destination) {
    1176         this.jmsDestination = (ActiveMQDestination) destination;
    1177     }
    1178
    1179
    1180     /***
    1181      * Gets the <CODE>DeliveryMode</CODE> value specified for this message.
    1182      *
    1183      * @return the delivery mode for this message
    1184      * @see javax.jms.Message#setJMSDeliveryMode(int)
    1185      * @see javax.jms.DeliveryMode
    1186      */
    1187
    1188     public int getJMSDeliveryMode() {
    1189         return this.jmsDeliveryMode;
    1190     }
    1191
    1192
    1193     /***
    1194      * Sets the <CODE>DeliveryMode</CODE> value for this message.
    1195      * <p/>
    1196      * <P>JMS providers set this field when a message is sent. This method
    1197      * can be used to change the value for a message that has been received.
    1198      *
    1199      * @param deliveryMode the delivery mode for this message
    1200      * @see javax.jms.Message#getJMSDeliveryMode()
    1201      * @see javax.jms.DeliveryMode
    1202      */
    1203
    1204     public void setJMSDeliveryMode(int deliveryMode) {
    1205         this.jmsDeliveryMode = deliveryMode;
    1206     }
    1207
    1208
    1209     /***
    1210      * Gets an indication of whether this message is being redelivered.
    1211      * <p/>
    1212      * <P>If a client receives a message with the <CODE>JMSRedelivered</CODE>
    1213      * field set,
    1214      * it is likely, but not guaranteed, that this message was delivered
    1215      * earlier but that its receipt was not acknowledged
    1216      * at that time.
    1217      *
    1218      * @return true if this message is being redelivered
    1219      * @see javax.jms.Message#setJMSRedelivered(boolean)
    1220      */
    1221
    1222     public boolean getJMSRedelivered() {
    1223         return this.jmsRedelivered;
    1224     }
    1225
    1226
    1227     /***
    1228      * Specifies whether this message is being redelivered.
    1229      * <p/>
    1230      * <P>This field is set at the time the message is delivered. This
    1231      * method can be used to change the value for a message that has
    1232      * been received.
    1233      *
    1234      * @param redelivered an indication of whether this message is being
    1235      *                    redelivered
    1236      * @see javax.jms.Message#getJMSRedelivered()
    1237      */
    1238
    1239     public void setJMSRedelivered(boolean redelivered) {
    1240         this.jmsRedelivered = redelivered;
    1241     }
    1242
    1243
    1244     /***
    1245      * Gets the message type identifier supplied by the client when the
    1246      * message was sent.
    1247      *
    1248      * @return the message type
    1249      * @see javax.jms.Message#setJMSType(String)
    1250      */
    1251
    1252     public String getJMSType() {
    1253         return this.jmsType;
    1254     }
    1255
    1256     /***
    1257      * Sets the message type.
    1258      * <p/>
    1259      * <P>Some JMS providers use a message repository that contains the
    1260      * definitions of messages sent by applications. The <CODE>JMSType</CODE>
    1261      * header field may reference a message's definition in the provider's
    1262      * repository.
    1263      * <p/>
    1264      * <P>The JMS API does not define a standard message definition repository,
    1265      * nor does it define a naming policy for the definitions it contains.
    1266      * <p/>
    1267      * <P>Some messaging systems require that a message type definition for
    1268      * each application message be created and that each message specify its
    1269      * type. In order to work with such JMS providers, JMS clients should
    1270      * assign a value to <CODE>JMSType</CODE>, whether the application makes
    1271      * use of it or not. This ensures that the field is properly set for those
    1272      * providers that require it.
    1273      * <p/>
    1274      * <P>To ensure portability, JMS clients should use symbolic values for
    1275      * <CODE>JMSType</CODE> that can be configured at installation time to the
    1276      * values defined in the current provider's message repository. If string
    1277      * literals are used, they may not be valid type names for some JMS
    1278      * providers.
    1279      *
    1280      * @param type the message type
    1281      * @see javax.jms.Message#getJMSType()
    1282      */
    1283
    1284     public void setJMSType(String type) {
    1285         this.jmsType = type;
    1286     }
    1287
    1288
    1289     /***
    1290      * Gets the message's expiration value.
    1291      * <p/>
    1292      * <P>When a message is sent, the <CODE>JMSExpiration</CODE> header field
    1293      * is left unassigned. After completion of the <CODE>send</CODE> or
    1294      * <CODE>publish</CODE> method, it holds the expiration time of the
    1295      * message. This is the sum of the time-to-live value specified by the
    1296      * client and the GMT at the time of the <CODE>send</CODE> or
    1297      * <CODE>publish</CODE>.
    1298      * <p/>
    1299      * <P>If the time-to-live is specified as zero, <CODE>JMSExpiration</CODE>
    1300      * is set to zero to indicate that the message does not expire.
    1301      * <p/>
    1302      * <P>When a message's expiration time is reached, a provider should
    1303      * discard it. The JMS API does not define any form of notification of
    1304      * message expiration.
    1305      * <p/>
    1306      * <P>Clients should not receive messages that have expired; however,
    1307      * the JMS API does not guarantee that this will not happen.
    1308      *
    1309      * @return the time the message expires, which is the sum of the
    1310      *         time-to-live value specified by the client and the GMT at the
    1311      *         time of the send
    1312      * @see javax.jms.Message#setJMSExpiration(long)
    1313      */
    1314
    1315     public long getJMSExpiration() {
    1316         return this.jmsExpiration;
    1317     }
    1318
    1319
    1320     /***
    1321      * Sets the message's expiration value.
    1322      * <p/>
    1323      * <P>JMS providers set this field when a message is sent. This method
    1324      * can be used to change the value for a message that has been received.
    1325      *
    1326      * @param expiration the message's expiration time
    1327      * @see javax.jms.Message#getJMSExpiration()
    1328      */
    1329
    1330     public void setJMSExpiration(long expiration) {
    1331         this.jmsExpiration = expiration;
    1332     }
    1333
    1334     /***
    1335      * Gets the message priority level.
    1336      * <p/>
    1337      * <P>The JMS API defines ten levels of priority value, with 0 as the
    1338      * lowest
    1339      * priority and 9 as the highest. In addition, clients should consider
    1340      * priorities 0-4 as gradations of normal priority and priorities 5-9
    1341      * as gradations of expedited priority.
    1342      * <p/>
    1343      * <P>The JMS API does not require that a provider strictly implement
    1344      * priority
    1345      * ordering of messages; however, it should do its best to deliver
    1346      * expedited messages ahead of normal messages.
    1347      *
    1348      * @return the default message priority
    1349      * @see javax.jms.Message#setJMSPriority(int)
    1350      */
    1351
    1352     public int getJMSPriority() {
    1353         return this.jmsPriority;
    1354     }
    1355
    1356
    1357     /***
    1358      * Sets the priority level for this message.
    1359      * <p/>
    1360      * <P>JMS providers set this field when a message is sent. This method
    1361      * can be used to change the value for a message that has been received.
    1362      *
    1363      * @param priority the priority of this message
    1364      * @see javax.jms.Message#getJMSPriority()
    1365      */
    1366
    1367     public void setJMSPriority(int priority) {
    1368         this.jmsPriority = priority;
    1369     }
    1370
    1371     /***
    1372      * Clears a message's properties.
    1373      * <p/>
    1374      * <P>The message's header fields and body are not cleared.
    1375      */
    1376
    1377     public synchronized void clearProperties() {
    1378         if (this.properties != null) {
    1379             this.properties.clear();
    1380         }
    1381         this.readOnlyProperties = false;
    1382     }
    1383
    1384
    1385     /***
    1386      * Indicates whether a property value exists.
    1387      *
    1388      * @param name the name of the property to test
    1389      * @return true if the property exists
    1390      */
    1391
    1392     public boolean propertyExists(String name) {
    1393         return this.properties != null ? this.properties.containsKey(name) : false;
    1394     }
    1395
    1396
    1397     /***
    1398      * Returns the value of the <CODE>boolean</CODE> property with the
    1399      * specified name.
    1400      *
    1401      * @param name the name of the <CODE>boolean</CODE> property
    1402      * @return the <CODE>boolean</CODE> property value for the specified name
    1403      * @throws JMSException           if the JMS provider fails to get the property
    1404      *                                value due to some internal error.
    1405      * @throws MessageFormatException if this type conversion is invalid.
    1406      */
    1407
    1408     public boolean getBooleanProperty(String name) throws JMSException {
    1409         return vanillaToBoolean(this.properties, name);
    1410     }
    1411
    1412
    1413     /***
    1414      * Returns the value of the <CODE>byte</CODE> property with the specified
    1415      * name.
    1416      *
    1417      * @param name the name of the <CODE>byte</CODE> property
    1418      * @return the <CODE>byte</CODE> property value for the specified name
    1419      * @throws JMSException           if the JMS provider fails to get the property
    1420      *                                value due to some internal error.
    1421      * @throws MessageFormatException if this type conversion is invalid.
    1422      */
    1423
    1424     public byte getByteProperty(String name) throws JMSException {
    1425         return vanillaToByte(this.properties, name);
    1426     }
    1427
    1428
    1429     /***
    1430      * Returns the value of the <CODE>short</CODE> property with the specified
    1431      * name.
    1432      *
    1433      * @param name the name of the <CODE>short</CODE> property
    1434      * @return the <CODE>short</CODE> property value for the specified name
    1435      * @throws JMSException           if the JMS provider fails to get the property
    1436      *                                value due to some internal error.
    1437      * @throws MessageFormatException if this type conversion is invalid.
    1438      */
    1439
    1440     public short getShortProperty(String name) throws JMSException {
    1441         return vanillaToShort(this.properties, name);
    1442     }
    1443
    1444
    1445     /***
    1446      * Returns the value of the <CODE>int</CODE> property with the specified
    1447      * name.
    1448      *
    1449      * @param name the name of the <CODE>int</CODE> property
    1450      * @return the <CODE>int</CODE> property value for the specified name
    1451      * @throws JMSException           if the JMS provider fails to get the property
    1452      *                                value due to some internal error.
    1453      * @throws MessageFormatException if this type conversion is invalid.
    1454      */
    1455
    1456     public int getIntProperty(String name) throws JMSException {
    1457         return vanillaToInt(this.properties, name);
    1458     }
    1459
    1460
    1461     /***
    1462      * Returns the value of the <CODE>long</CODE> property with the specified
    1463      * name.
    1464      *
    1465      * @param name the name of the <CODE>long</CODE> property
    1466      * @return the <CODE>long</CODE> property value for the specified name
    1467      * @throws JMSException           if the JMS provider fails to get the property
    1468      *                                value due to some internal error.
    1469      * @throws MessageFormatException if this type conversion is invalid.
    1470      */
    1471
    1472     public long getLongProperty(String name) throws JMSException {
    1473         return vanillaToLong(this.properties, name);
    1474     }
    1475
    1476
    1477     /***
    1478      * Returns the value of the <CODE>float</CODE> property with the specified
    1479      * name.
    1480      *
    1481      * @param name the name of the <CODE>float</CODE> property
    1482      * @return the <CODE>float</CODE> property value for the specified name
    1483      * @throws JMSException           if the JMS provider fails to get the property
    1484      *                                value due to some internal error.
    1485      * @throws MessageFormatException if this type conversion is invalid.
    1486      */
    1487
    1488     public float getFloatProperty(String name) throws JMSException {
    1489         return vanillaToFloat(this.properties, name);
    1490     }
    1491
    1492
    1493     /***
    1494      * Returns the value of the <CODE>double</CODE> property with the specified
    1495      * name.
    1496      *
    1497      * @param name the name of the <CODE>double</CODE> property
    1498      * @return the <CODE>double</CODE> property value for the specified name
    1499      * @throws JMSException           if the JMS provider fails to get the property
    1500      *                                value due to some internal error.
    1501      * @throws MessageFormatException if this type conversion is invalid.
    1502      */
    1503
    1504     public double getDoubleProperty(String name) throws JMSException {
    1505         return vanillaToDouble(this.properties, name);
    1506     }
    1507
    1508
    1509     /***
    1510      * Returns the value of the <CODE>String</CODE> property with the specified
    1511      * name.
    1512      *
    1513      * @param name the name of the <CODE>String</CODE> property
    1514      * @return the <CODE>String</CODE> property value for the specified name;
    1515      *         if there is no property by this name, a null value is returned
    1516      * @throws JMSException           if the JMS provider fails to get the property
    1517      *                                value due to some internal error.
    1518      * @throws MessageFormatException if this type conversion is invalid.
    1519      */
    1520
    1521     public String getStringProperty(String name) throws JMSException {
    1522         return vanillaToString(this.properties, name);
    1523     }
    1524
    1525
    1526     /***
    1527      * Returns the value of the Java object property with the specified name.
    1528      * <p/>
    1529      * <P>This method can be used to return, in objectified format,
    1530      * an object that has been stored as a property in the message with the
    1531      * equivalent <CODE>setObjectProperty</CODE> method call, or its equivalent
    1532      * primitive <CODE>set<I>type</I>Property</CODE> method.
    1533      *
    1534      * @param name the name of the Java object property
    1535      * @return the Java object property value with the specified name, in
    1536      *         objectified format (for example, if the property was set as an
    1537      *         <CODE>int</CODE>, an <CODE>Integer</CODE> is
    1538      *         returned); if there is no property by this name, a null value
    1539      *         is returned
    1540      */
    1541
    1542     public Object getObjectProperty(String name) {
    1543         return this.properties != null ? this.properties.get(name) : null;
    1544     }
    1545
    1546
    1547     /***
    1548      * Returns an <CODE>Enumeration</CODE> of all the property names.
    1549      * <p/>
    1550      * <P>Note that JMS standard header fields are not considered
    1551      * properties and are not returned in this enumeration.
    1552      *
    1553      * @return an enumeration of all the names of property values
    1554      */
    1555
    1556     public Enumeration getPropertyNames() {
    1557         if (this.properties == null) {
    1558             this.properties = new Hashtable();
    1559         }
    1560         return this.properties.keys();
    1561     }
    1562
    1563     /***
    1564      * Retrieve the message properties as a Hashtable
    1565      *
    1566      * @return the Hashtable representing the properties or null if not set or used
    1567      */
    1568
    1569     public Hashtable getProperties() {
    1570         return this.properties;
    1571     }
    1572
    1573     /***
    1574      * Set the Message's properties from an external source
    1575      * No checking on correct types is done by this method
    1576      *
    1577      * @param newProperties
    1578      */
    1579
    1580     void setProperties(Hashtable newProperties) {
    1581         this.properties = newProperties;
    1582     }
    1583
    1584
    1585     /***
    1586      * Sets a <CODE>boolean</CODE> property value with the specified name into
    1587      * the message.
    1588      *
    1589      * @param name  the name of the <CODE>boolean</CODE> property
    1590      * @param value the <CODE>boolean</CODE> property value to set
    1591      * @throws JMSException                 if the JMS provider fails to set the property
    1592      *                                      due to some internal error.
    1593      * @throws IllegalArgumentException     if the name is null or if the name is
    1594      *                                      an empty string.
    1595      * @throws MessageNotWriteableException if properties are read-only
    1596      */
    1597
    1598     public void setBooleanProperty(String name, boolean value) throws JMSException {
    1599         prepareProperty(name);
    1600         this.properties.put(name, (value) ? Boolean.TRUE : Boolean.FALSE);
    1601     }
    1602
    1603
    1604     /***
    1605      * Sets a <CODE>byte</CODE> property value with the specified name into
    1606      * the message.
    1607      *
    1608      * @param name  the name of the <CODE>byte</CODE> property
    1609      * @param value the <CODE>byte</CODE> property value to set
    1610      * @throws JMSException                 if the JMS provider fails to set the property
    1611      *                                      due to some internal error.
    1612      * @throws IllegalArgumentException     if the name is null or if the name is
    1613      *                                      an empty string.
    1614      * @throws MessageNotWriteableException if properties are read-only
    1615      */
    1616
    1617     public void setByteProperty(String name, byte value) throws JMSException {
    1618         prepareProperty(name);
    1619         this.properties.put(name, new Byte(value));
    1620     }
    1621
    1622
    1623     /***
    1624      * Sets a <CODE>short</CODE> property value with the specified name into
    1625      * the message.
    1626      *
    1627      * @param name  the name of the <CODE>short</CODE> property
    1628      * @param value the <CODE>short</CODE> property value to set
    1629      * @throws JMSException                 if the JMS provider fails to set the property
    1630      *                                      due to some internal error.
    1631      * @throws IllegalArgumentException     if the name is null or if the name is
    1632      *                                      an empty string.
    1633      * @throws MessageNotWriteableException if properties are read-only
    1634      */
    1635
    1636     public void setShortProperty(String name, short value) throws JMSException {
    1637         prepareProperty(name);
    1638         this.properties.put(name, new Short(value));
    1639     }
    1640
    1641
    1642     /***
    1643      * Sets an <CODE>int</CODE> property value with the specified name into
    1644      * the message.
    1645      *
    1646      * @param name  the name of the <CODE>int</CODE> property
    1647      * @param value the <CODE>int</CODE> property value to set
    1648      * @throws JMSException                 if the JMS provider fails to set the property
    1649      *                                      due to some internal error.
    1650      * @throws IllegalArgumentException     if the name is null or if the name is
    1651      *                                      an empty string.
    1652      * @throws MessageNotWriteableException if properties are read-only
    1653      */
    1654
    1655     public void setIntProperty(String name, int value) throws JMSException {
    1656         prepareProperty(name);
    1657         this.properties.put(name, new Integer(value));
    1658     }
    1659
    1660
    1661     /***
    1662      * Sets a <CODE>long</CODE> property value with the specified name into
    1663      * the message.
    1664      *
    1665      * @param name  the name of the <CODE>long</CODE> property
    1666      * @param value the <CODE>long</CODE> property value to set
    1667      * @throws JMSException                 if the JMS provider fails to set the property
    1668      *                                      due to some internal error.
    1669      * @throws IllegalArgumentException     if the name is null or if the name is
    1670      *                                      an empty string.
    1671      * @throws MessageNotWriteableException if properties are read-only
    1672      */
    1673
    1674     public void setLongProperty(String name, long value) throws JMSException {
    1675         prepareProperty(name);
    1676         this.properties.put(name, new Long(value));
    1677     }
    1678
    1679
    1680     /***
    1681      * Sets a <CODE>float</CODE> property value with the specified name into
    1682      * the message.
    1683      *
    1684      * @param name  the name of the <CODE>float</CODE> property
    1685      * @param value the <CODE>float</CODE> property value to set
    1686      * @throws JMSException                 if the JMS provider fails to set the property
    1687      *                                      due to some internal error.
    1688      * @throws IllegalArgumentException     if the name is null or if the name is
    1689      *                                      an empty string.
    1690      * @throws MessageNotWriteableException if properties are read-only
    1691      */
    1692
    1693     public void setFloatProperty(String name, float value) throws JMSException {
    1694         prepareProperty(name);
    1695         this.properties.put(name, new Float(value));
    1696
    1697     }
    1698
    1699
    1700     /***
    1701      * Sets a <CODE>double</CODE> property value with the specified name into
    1702      * the message.
    1703      *
    1704      * @param name  the name of the <CODE>double</CODE> property
    1705      * @param value the <CODE>double</CODE> property value to set
    1706      * @throws JMSException                 if the JMS provider fails to set the property
    1707      *                                      due to some internal error.
    1708      * @throws IllegalArgumentException     if the name is null or if the name is
    1709      *                                      an empty string.
    1710      * @throws MessageNotWriteableException if properties are read-only
    1711      */
    1712
    1713     public void setDoubleProperty(String name, double value) throws JMSException {
    1714         prepareProperty(name);
    1715         this.properties.put(name, new Double(value));
    1716     }
    1717
    1718
    1719     /***
    1720      * Sets a <CODE>String</CODE> property value with the specified name into
    1721      * the message.
    1722      *
    1723      * @param name  the name of the <CODE>String</CODE> property
    1724      * @param value the <CODE>String</CODE> property value to set
    1725      * @throws JMSException                 if the JMS provider fails to set the property
    1726      *                                      due to some internal error.
    1727      * @throws IllegalArgumentException     if the name is null or if the name is
    1728      *                                      an empty string.
    1729      * @throws MessageNotWriteableException if properties are read-only
    1730      */
    1731
    1732     public void setStringProperty(String name, String value) throws JMSException {
    1733         prepareProperty(name);
    1734         if (value == null) {
    1735             this.properties.remove(name);
    1736         }
    1737         else {
    1738             this.properties.put(name, value);
    1739         }
    1740     }
    1741
    1742
    1743     /***
    1744      * Sets a Java object property value with the specified name into the
    1745      * message.
    1746      * <p/>
    1747      * <P>Note that this method works only for the objectified primitive
    1748      * object types (<CODE>Integer</CODE>, <CODE>Double</CODE>,
    1749      * <CODE>Long</CODE> ...) and <CODE>String</CODE> objects.
    1750      *
    1751      * @param name  the name of the Java object property
    1752      * @param value the Java object property value to set
    1753      * @throws JMSException                 if the JMS provider fails to set the property
    1754      *                                      due to some internal error.
    1755      * @throws IllegalArgumentException     if the name is null or if the name is
    1756      *                                      an empty string.
    1757      * @throws MessageFormatException       if the object is invalid
    1758      * @throws MessageNotWriteableException if properties are read-only
    1759      */
    1760
    1761     public void setObjectProperty(String name, Object value) throws JMSException {
    1762         prepareProperty(name);
    1763         if (value == null) {
    1764             this.properties.remove(name);
    1765         }
    1766         else {
    1767             if (value instanceof Number ||
    1768                     value instanceof Character ||
    1769                     value instanceof Boolean ||
    1770                     value instanceof String) {
    1771                 this.properties.put(name, value);
    1772             }
    1773             else {
    1774                 throw new MessageFormatException("Cannot set property to type: " + value.getClass().getName());
    1775             }
    1776         }
    1777     }
    1778
    1779
    1780     /***
    1781      * Acknowledges all consumed messages of the session of this consumed
    1782      * message.
    1783      * <p/>
    1784      * <P>All consumed JMS messages support the <CODE>acknowledge</CODE>
    1785      * method for use when a client has specified that its JMS session's
    1786      * consumed messages are to be explicitly acknowledged.  By invoking
    1787      * <CODE>acknowledge</CODE> on a consumed message, a client acknowledges
    1788      * all messages consumed by the session that the message was delivered to.
    1789      * <p/>
    1790      * <P>Calls to <CODE>acknowledge</CODE> are ignored for both transacted
    1791      * sessions and sessions specified to use implicit acknowledgement modes.
    1792      * <p/>
    1793      * <P>A client may individually acknowledge each message as it is consumed,
    1794      * or it may choose to acknowledge messages as an application-defined group
    1795      * (which is done by calling acknowledge on the last received message of the group,
    1796      * thereby acknowledging all messages consumed by the session.)
    1797      * <p/>
    1798      * <P>Messages that have been received but not acknowledged may be
    1799      * redelivered.
    1800      *
    1801      * @throws JMSException if the JMS provider fails to acknowledge the
    1802      *                      messages due to some internal error.
    1803      * @throws javax.jms.IllegalStateException
    1804      *                      if this method is called on a closed
    1805      *                      session.
    1806      * @see javax.jms.Session#CLIENT_ACKNOWLEDGE
    1807      */
    1808
    1809     public void acknowledge() throws JMSException {
    1810         if (this.messageAcknowledge != null) {
    1811             this.messageAcknowledge.acknowledge();
    1812         }
    1813     }
    1814
    1815
    1816     /***
    1817      * Clears out the message body. Clearing a message's body does not clear
    1818      * its header values or property entries.
    1819      * <p/>
    1820      * <P>If this message body was read-only, calling this method leaves
    1821      * the message body in the same state as an empty body in a newly
    1822      * created message.
    1823      *
    1824      * @throws JMSException if the JMS provider fails to clear the message
    1825      *                      body due to some internal error.
    1826      */
    1827
    1828     public void clearBody() throws JMSException {
    1829         this.readOnlyMessage = false;
    1830         this.bodyAsBytes = null;
    1831     }
    1832
    1833     boolean vanillaToBoolean(Hashtable table, String name) throws JMSException {
    1834         boolean result = false;
    1835         if (table != null) {
    1836             Object value = table.get(name);
    1837             if (value != null) {
    1838                 if (value instanceof Boolean) {
    1839                     result = ((Boolean) value).booleanValue();
    1840                 }
    1841                 else if (value instanceof String) {
    1842                     // will throw a runtime exception if cannot convert
    1843                     result = Boolean.valueOf((String) value).booleanValue();
    1844                 }
    1845                 else {
    1846                     throw new MessageFormatException(name + " not a Boolean type");
    1847                 }
    1848             }
    1849         }
    1850         return result;
    1851     }
    1852
    1853     byte vanillaToByte(Hashtable table, String name) throws JMSException {
    1854         byte result = 0;
    1855         if (table != null) {
    1856             Object value = table.get(name);
    1857             if (value != null) {
    1858                 if (value instanceof Byte) {
    1859                     result = ((Byte) value).byteValue();
    1860                 }
    1861                 else if (value instanceof String) {
    1862                     result = Byte.valueOf((String) value).byteValue();
    1863                 }
    1864                 else {
    1865                     throw new MessageFormatException(name + " not a Byte type");
    1866                 }
    1867             }
    1868             else {
    1869 //object doesn't exist - so treat as a null ..
    1870                 throw new NumberFormatException("Cannot interpret null as a Byte");
    1871             }
    1872         }
    1873         return result;
    1874     }
    1875
    1876     short vanillaToShort(Hashtable table, String name) throws JMSException {
    1877         short result = 0;
    1878         if (table != null) {
    1879             Object value = table.get(name);
    1880             if (value != null) {
    1881                 if (value instanceof Short) {
    1882                     result = ((Short) value).shortValue();
    1883                 }
    1884                 else if (value instanceof String) {
    1885                     return Short.valueOf((String) value).shortValue();
    1886                 }
    1887                 else if (value instanceof Byte) {
    1888                     result = ((Byte) value).byteValue();
    1889                 }
    1890                 else {
    1891                     throw new MessageFormatException(name + " not a Short type");
    1892                 }
    1893             }
    1894             else {
    1895                 throw new NumberFormatException(name + " is null");
    1896             }
    1897         }
    1898         return result;
    1899     }
    1900
    1901     int vanillaToInt(Hashtable table, String name) throws JMSException {
    1902         int result = 0;
    1903         if (table != null) {
    1904             Object value = table.get(name);
    1905             if (value != null) {
    1906                 if (value instanceof Integer) {
    1907                     result = ((Integer) value).intValue();
    1908                 }
    1909                 else if (value instanceof String) {
    1910                     result = Integer.valueOf((String) value).intValue();
    1911                 }
    1912                 else if (value instanceof Byte) {
    1913                     result = ((Byte) value).intValue();
    1914                 }
    1915                 else if (value instanceof Short) {
    1916                     result = ((Short) value).intValue();
    1917                 }
    1918                 else {
    1919                     throw new MessageFormatException(name + " not an Integer type");
    1920                 }
    1921             }
    1922             else {
    1923                 throw new NumberFormatException(name + " is null");
    1924             }
    1925         }
    1926         return result;
    1927     }
    1928
    1929     long vanillaToLong(Hashtable table, String name) throws JMSException {
    1930         long result = 0;
    1931         if (table != null) {
    1932             Object value = table.get(name);
    1933             if (value != null) {
    1934                 if (value instanceof Long) {
    1935                     result = ((Long) value).longValue();
    1936                 }
    1937                 else if (value instanceof String) {
    1938                     // will throw a runtime exception if cannot convert
    1939                     result = Long.valueOf((String) value).longValue();
    1940                 }
    1941                 else if (value instanceof Byte) {
    1942                     result = ((Byte) value).byteValue();
    1943                 }
    1944                 else if (value instanceof Short) {
    1945                     result = ((Short) value).shortValue();
    1946                 }
    1947                 else if (value instanceof Integer) {
    1948                     result = ((Integer) value).intValue();
    1949                 }
    1950                 else {
    1951                     throw new MessageFormatException(name + " not a Long type");
    1952                 }
    1953             }
    1954             else {
    1955                 throw new NumberFormatException(name + " is null");
    1956             }
    1957         }
    1958         return result;
    1959     }
    1960
    1961     float vanillaToFloat(Hashtable table, String name) throws JMSException {
    1962         float result = 0.0f;
    1963         if (table != null) {
    1964             Object value = table.get(name);
    1965             if (value != null) {
    1966                 if (value instanceof Float) {
    1967                     result = ((Float) value).floatValue();
    1968                 }
    1969                 else if (value instanceof String) {
    1970                     result = Float.valueOf((String) value).floatValue();
    1971                 }
    1972                 else {
    1973                     throw new MessageFormatException(name + " not a Float type: " + value.getClass());
    1974                 }
    1975             }
    1976             else {
    1977                 throw new NullPointerException(name + " is null");
    1978             }
    1979         }
    1980         return result;
    1981     }
    1982
    1983     double vanillaToDouble(Hashtable table, String name) throws JMSException {
    1984         double result = 0.0d;
    1985         if (table != null) {
    1986             Object value = table.get(name);
    1987             if (value != null) {
    1988                 if (value instanceof Double) {
    1989                     result = ((Double) value).doubleValue();
    1990                 }
    1991                 else if (value instanceof String) {
    1992                     result = Double.valueOf((String) value).doubleValue();
    1993                 }
    1994                 else if (value instanceof Float) {
    1995                     result = ((Float) value).floatValue();
    1996                 }
    1997                 else {
    1998                     throw new MessageFormatException(name + " not a Double type");
    1999                 }
    2000             }
    2001             else {
    2002                 throw new NullPointerException(name + " is null");
    2003             }
    2004         }
    2005         return result;
    2006     }
    2007
    2008
    2009     String vanillaToString(Hashtable table, String name) throws JMSException {
    2010         String result = null;
    2011         if (table != null) {
    2012             Object value = table.get(name);
    2013             if (value != null) {
    2014                 if (value instanceof String || value instanceof Number || value instanceof Boolean) {
    2015                     result = value.toString();
    2016                 }
    2017                 else {
    2018                     throw new MessageFormatException(name + " not a String type");
    2019                 }
    2020             }
    2021         }
    2022         return result;
    2023     }
    2024
    2025     private void prepareProperty(String name) throws JMSException {
    2026         if (name == null) {
    2027             throw new IllegalArgumentException("Invalid property name: cannot be null");
    2028         }
    2029         if (name.length() == 0) {
    2030             throw new IllegalArgumentException("Invalid property name: cannot be empty");
    2031         }
    2032         if (this.readOnlyProperties) {
    2033             throw new MessageNotWriteableException("Properties are read-only");
    2034         }
    2035         if (this.properties == null) {
    2036             this.properties = new Hashtable();
    2037         }
    2038     }
    2039
    2040     /***
    2041      * @return Returns the entryBrokerName.
    2042      */
    2043     public String getEntryBrokerName() {
    2044         return this.entryBrokerName;
    2045     }
    2046
    2047     /***
    2048      * @param newEntryBrokerName The entryBrokerName to set.
    2049      */
    2050     public void setEntryBrokerName(String newEntryBrokerName) {
    2051         this.entryBrokerName = newEntryBrokerName;
    2052     }
    2053
    2054     /***
    2055      * @return Returns the entryClusterName.
    2056      */
    2057     public String getEntryClusterName() {
    2058         return this.entryClusterName;
    2059     }
    2060
    2061     /***
    2062      * @param newEntryClusterName The entryClusterName to set.
    2063      */
    2064     public void setEntryClusterName(String newEntryClusterName) {
    2065         this.entryClusterName = newEntryClusterName;
    2066     }
    2067
    2068     /***
    2069      * @return Returns the consumerNos.
    2070      */
    2071     public int[] getConsumerNos() {
    2072         return this.consumerNos;
    2073     }
    2074
    2075     /***
    2076      * @param newConsumerNos The consumerIDs to set.
    2077      */
    2078     public void setConsumerNos(int[] newConsumerNos) {
    2079         this.consumerNos = newConsumerNos;
    2080     }
    2081
    2082     /***
    2083      * @return Returns the jmsClientID.
    2084      */
    2085     public String getJMSClientID() {
    2086         return this.jmsClientID;
    2087     }
    2088
    2089     /***
    2090      * @param newJmsClientID The jmsClientID to set.
    2091      */
    2092     public void setJMSClientID(String newJmsClientID) {
    2093         this.jmsClientID = newJmsClientID;
    2094     }
    2095
    2096     /***
    2097      * @return Returns the producerID.
    2098      */
    2099     public String getProducerID() {
    2100         return this.producerID;
    2101     }
    2102
    2103     /***
    2104      * @param newProducerID The producerID to set.
    2105      */
    2106     public void setProducerID(String newProducerID) {
    2107         this.producerID = newProducerID;
    2108     }
    2109
    2110
    2111     /***
    2112      * @return Returns true if this message is part of a transaction
    2113      */
    2114
    2115     public boolean isPartOfTransaction() {
    2116         return this.transactionId != null && this.transactionId.length() > 0;
    2117     }
    2118
    2119     /***
    2120      * @return Returns the transactionId.
    2121      */
    2122     public String getTransactionId() {
    2123         return this.transactionId;
    2124     }
    2125
    2126     /***
    2127      * @param newTransactionId The transactionId to set.
    2128      */
    2129     public void setTransactionId(String newTransactionId) {
    2130         this.transactionId = newTransactionId;
    2131     }
    2132
    2133     /***
    2134      * @return Returns the consumerId.
    2135      */
    2136     public String getConsumerId() {
    2137         return consumerId;
    2138     }
    2139
    2140     /***
    2141      * @param consId The consumerId to set.
    2142      */
    2143     public void setConsumerId(String consId) {
    2144         this.consumerId = consId;
    2145     }
    2146
    2147     /***
    2148      * @return Returns the messageConsumed.
    2149      */
    2150     public boolean isMessageConsumed() {
    2151         return messageConsumed;
    2152     }
    2153
    2154     /***
    2155      * @param messageConsumed The messageConsumed to set.
    2156      */
    2157     public void setMessageConsumed(boolean messageConsumed) {
    2158         this.messageConsumed = messageConsumed;
    2159     }
    2160
    2161
    2162     /***
    2163      * Prepare a message body for delivery
    2164      *
    2165      * @throws JMSException
    2166      */
    2167     public void prepareMessageBody() throws JMSException {
    2168     }
    2169
    2170     /***
    2171      * Convert the message body to data
    2172      *
    2173      * @throws IOException
    2174      */
    2175     public final void convertBodyToBytes() throws IOException {
    2176         if (bodyAsBytes == null) {
    2177             ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
    2178             DataOutputStream dataOut = new DataOutputStream(bytesOut);
    2179             writeBody(dataOut);
    2180             dataOut.flush();
    2181             bodyAsBytes = bytesOut.toByteArray();
    2182             dataOut.close();
    2183         }
    2184     }
    2185
    2186     /***
    2187      * Builds the message body from data
    2188      *
    2189      * @throws IOException
    2190      */
    2191     public final void buildBodyFromBytes() throws IOException {
    2192         if (bodyAsBytes != null) {
    2193             ByteArrayInputStream bytesIn = new ByteArrayInputStream(bodyAsBytes);
    2194             DataInputStream dataIn = new DataInputStream(bytesIn);
    2195             readBody(dataIn);
    2196             dataIn.close();
    2197         }
    2198     }
    2199
    2200     /***
    2201      * Used serialize the message body to an output stream
    2202      *
    2203      * @param dataOut
    2204      * @throws IOException
    2205      */
    2206
    2207     protected void writeBody(DataOutput dataOut) throws IOException {
    2208
    2209     }
    2210
    2211     /***
    2212      * Used to help build the body from an input stream
    2213      *
    2214      * @param dataIn
    2215      * @throws IOException
    2216      */
    2217
    2218     protected void readBody(DataInput dataIn) throws IOException {
    2219
    2220     }
    2221
    2222     /***
    2223      * @return Returns the bodyAsBytes.
    2224      * @throws IOException
    2225      */
    2226     public byte[] getBodyAsBytes() throws IOException {
    2227         if (bodyAsBytes == null) {
    2228             convertBodyToBytes();
    2229         }
    2230         return bodyAsBytes;
    2231     }
    2232
    2233     /***
    2234      * @param bodyAsBytes The bodyAsBytes to set.
    2235      */
    2236     public void setBodyAsBytes(byte[] bodyAsBytes) {
    2237         this.bodyAsBytes = bodyAsBytes;
    2238     }
    2239
    2240     /***
    2241      * write map properties to an output stream
    2242      *
    2243      * @param table
    2244      * @param dataOut
    2245      * @throws IOException
    2246      */
    2247
    2248     protected void writeMapProperties(Hashtable table, DataOutput dataOut) throws IOException {
    2249         if (table != null) {
    2250             dataOut.writeShort(table.size());
    2251             for (Enumeration iter = table.keys(); iter.hasMoreElements();) {
    2252                 String key = iter.nextElement().toString();
    2253                 dataOut.writeUTF(key);
    2254                 Object value = table.get(key);
    2255
    2256                 if (value instanceof byte[]) {
    2257                     byte[] data = (byte[]) value;
    2258                     dataOut.write(ActiveMQMessage.BYTES);
    2259                     if (data != null) {
    2260                         dataOut.writeInt(data.length);
    2261                         dataOut.write(data);
    2262                     }
    2263                     else {
    2264                         dataOut.writeInt(-1);
    2265                     }
    2266                 }
    2267                 else if (value instanceof Byte) {
    2268                     dataOut.write(ActiveMQMessage.BYTE);
    2269                     Byte v = (Byte) value;
    2270                     dataOut.writeByte(v.byteValue());
    2271                 }
    2272                 else if (value instanceof Boolean) {
    2273                     dataOut.write(ActiveMQMessage.BOOLEAN);
    2274                     Boolean v = (Boolean) value;
    2275                     dataOut.writeBoolean(v.booleanValue());
    2276                 }
    2277                 else if (value instanceof String) {
    2278                     dataOut.write(ActiveMQMessage.STRING);
    2279                     dataOut.writeUTF(value.toString());
    2280                 }
    2281                 else if (value instanceof Character) {
    2282                     dataOut.write(ActiveMQMessage.CHAR);
    2283                     Character v = (Character) value;
    2284                     dataOut.writeChar(v.charValue());
    2285                 }
    2286                 else if (value instanceof Number) {
    2287                     Number v = (Number) value;
    2288
    2289                     if (value instanceof Long) {
    2290                         dataOut.write(ActiveMQMessage.LONG);
    2291                         dataOut.writeLong(v.longValue());
    2292                     }
    2293                     else if (value instanceof Integer) {
    2294                         dataOut.write(ActiveMQMessage.INT);
    2295                         dataOut.writeInt(v.intValue());
    2296                     }
    2297                     else if (value instanceof Short) {
    2298                         dataOut.write(ActiveMQMessage.SHORT);
    2299                         dataOut.writeShort(v.shortValue());
    2300                     }
    2301                     else if (value instanceof Float) {
    2302                         dataOut.write(ActiveMQMessage.FLOAT);
    2303                         dataOut.writeFloat(v.floatValue());
    2304                     }
    2305                     else if (value instanceof Double) {
    2306                         dataOut.write(ActiveMQMessage.DOUBLE);
    2307                         dataOut.writeDouble(v.doubleValue());
    2308                     }
    2309                 }
    2310                 else {
    2311                     throw new RuntimeException("Do not know how to parse value of type: " + value.getClass());
    2312                 }
    2313
    2314             }
    2315         }
    2316         else {
    2317             dataOut.writeShort(-1);
    2318         }
    2319     }
    2320
    2321     /***
    2322      * @param dataIn
    2323      * @return
    2324      * @throws IOException
    2325      */
    2326     protected Hashtable readMapProperties(DataInput dataIn) throws IOException {
    2327         Hashtable result = null;
    2328         int size = dataIn.readShort();
    2329         if (size > -1) {
    2330             result = new Hashtable();
    2331             for (int i = 0; i < size; i++) {
    2332                 String key = dataIn.readUTF();
    2333                 Object value = null;
    2334                 int type = dataIn.readByte();
    2335                 if (type == ActiveMQMessage.BYTES) {
    2336                     byte[] data = null;
    2337                     int dataSize = dataIn.readInt();
    2338                     if (dataSize > -1) {
    2339                         data = new byte[dataSize];
    2340                         dataIn.readFully(data);
    2341                     }
    2342                     value = data;
    2343                 }
    2344                 else if (type == ActiveMQMessage.BYTE) {
    2345                     value = new Byte(dataIn.readByte());
    2346                 }
    2347                 else if (type == ActiveMQMessage.BOOLEAN) {
    2348                     value = (dataIn.readBoolean()) ? Boolean.TRUE : Boolean.FALSE;
    2349                 }
    2350                 else if (type == ActiveMQMessage.STRING) {
    2351                     value = dataIn.readUTF();
    2352                 }
    2353                 else if (type == ActiveMQMessage.CHAR) {
    2354                     value = new Character(dataIn.readChar());
    2355                 }
    2356                 else if (type == ActiveMQMessage.LONG) {
    2357                     value = new Long(dataIn.readLong());
    2358                 }
    2359                 else if (type == ActiveMQMessage.INT) {
    2360                     value = new Integer(dataIn.readInt());
    2361                 }
    2362                 else if (type == ActiveMQMessage.SHORT) {
    2363                     value = new Short(dataIn.readShort());
    2364                 }
    2365                 else if (type == ActiveMQMessage.FLOAT) {
    2366                     value = new Float(dataIn.readFloat());
    2367                 }
    2368                 else if (type == ActiveMQMessage.DOUBLE) {
    2369                     value = new Double(dataIn.readDouble());
    2370                 }
    2371                 else {
    2372                     throw new RuntimeException("Do not know how to parse type: " + type);
    2373                 }
    2374                 result.put(key, value);
    2375             }
    2376         }
    2377         return result;
    2378     }
    2379
    2380     /***
    2381      * @return Returns the xaTransacted.
    2382      */
    2383     public boolean isXaTransacted() {
    2384         return xaTransacted;
    2385     }
    2386
    2387     /***
    2388      * @param xaTransacted The xaTransacted to set.
    2389      */
    2390     public void setXaTransacted(boolean xaTransacted) {
    2391         this.xaTransacted = xaTransacted;
    2392     }
    2393
    2394     /***
    2395      * @return the ActiveMQDestination
    2396      */
    2397     public ActiveMQDestination getJMSActiveMQDestination() {
    2398         return jmsDestination;
    2399     }
    2400
    2401     /***
    2402      * @return the message identity, which contains the String messageID
    2403      *         and the lazily populated sequence number
    2404      */
    2405     public MessageIdentity getJMSMessageIdentity() {
    2406         if (jmsMessageIdentity == null) {
    2407             jmsMessageIdentity = new MessageIdentity(getId());
    2408         }
    2409         return jmsMessageIdentity;
    2410     }
    2411
    2412     /***
    2413      * @param messageIdentity - message identity for this object
    2414      */
    2415     public void setJMSMessageIdentity(MessageIdentity messageIdentity) {
    2416         this.jmsMessageIdentity = messageIdentity;
    2417     }
    2418
    2419     /***
    2420      * Determine if the message originated in the network from the named broker
    2421      * @param brokerName
    2422      * @return true if entry point matches the brokerName
    2423      */
    2424     public boolean isEntryBroker(String brokerName){
    2425         boolean result = entryBrokerName != null && brokerName != null && entryBrokerName.equals(brokerName);
    2426         return result;
    2427     }
    2428
    2429     /***
    2430      * Determine if the message originated in the network from the named cluster
    2431      * @param clusterName
    2432      * @return true if the entry point matches the clusterName
    2433      */
    2434     public boolean isEntryCluster(String clusterName){
    2435         boolean result = entryClusterName != null && clusterName != null && entryClusterName.equals(clusterName);
    2436         return result;
    2437     }
    2438
    2439     /***
    2440      * @return Returns the transientConsumed.
    2441      */
    2442     public boolean isTransientConsumed() {
    2443         return transientConsumed;
    2444     }
    2445     /***
    2446      * @param transientConsumed The transientConsumed to set.
    2447      */
    2448     public void setTransientConsumed(boolean transientConsumed) {
    2449         this.transientConsumed = transientConsumed;
    2450     }
    2451 }