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.21 $
    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 = 0;
    594     static final int TYPE_INDEX = 1;
    595     static final int BROKER_NAME_INDEX = 2;
    596     static final int CLUSTER_NAME_INDEX = 3;
    597     static final int TRANSACTION_ID_INDEX = 4;
    598     static final int REPLY_TO_INDEX = 5;
    599     static final int TIMESTAMP_INDEX = 6;
    600     static final int EXPIRATION_INDEX = 7;
    601     static final int REDELIVERED_INDEX = 8;
    602     static final int XA_TRANS_INDEX = 9;
    603     static final int CID_INDEX = 10;
    604     static final int PROPERTIES_INDEX = 11;
    605     static final int PAYLOAD_INDEX = 12;
    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 MessageAcknowledge messageAcknowledge;
    635     private byte[] bodyAsBytes;
    636     private MessageIdentity messageIdentity;
    637
    638
    639     /***
    640      * Retrieve if a JMS Message type or not
    641      *
    642      * @return true if it is a JMS Message
    643      */
    644     public boolean isJMSMessage() {
    645         return true;
    646     }
    647
    648
    649     /***
    650      * @return pretty print of this Message
    651      */
    652     public String toString() {
    653         return getPacketTypeAsString(getPacketType()) + ": dest = " + jmsDestination + ", id = " + jmsMessageID;
    654     }
    655
    656
    657     /***
    658      * @return Returns the messageAcknowledge.
    659      */
    660     public MessageAcknowledge getMessageAcknowledge() {
    661         return messageAcknowledge;
    662     }
    663
    664     /***
    665      * @param messageAcknowledge The messageAcknowledge to set.
    666      */
    667     public void setMessageAcknowledge(MessageAcknowledge messageAcknowledge) {
    668         this.messageAcknowledge = messageAcknowledge;
    669     }
    670
    671     /***
    672      * Return the type of Packet
    673      *
    674      * @return integer representation of the type of Packet
    675      */
    676
    677     public int getPacketType() {
    678         return ACTIVEMQ_MESSAGE;
    679     }
    680
    681     /***
    682      * @return the unique id for this Packet
    683      */
    684
    685     public String getId() {
    686         return this.jmsMessageID;
    687     }
    688
    689     /***
    690      * Set the unique id for this Packet
    691      *
    692      * @param newId
    693      */
    694
    695     public void setId(String newId) {
    696         this.jmsMessageID = newId;
    697     }
    698
    699
    700     /***
    701      * set the message readOnly
    702      *
    703      * @param value
    704      */
    705     public void setReadOnly(boolean value) {
    706         this.readOnlyProperties = value;
    707         this.readOnlyMessage = value;
    708     }
    709
    710     /***
    711      * test to see if a particular Consumer at a Connection
    712      * is meant to receive this Message
    713      *
    714      * @param consumerNumber
    715      * @return true if a target
    716      */
    717
    718     public boolean isConsumerTarget(int consumerNumber) {
    719         if (consumerNos != null) {
    720             for (int i = 0; i < consumerNos.length; i++) {
    721                 if (consumerNos[i] == consumerNumber) {
    722                     return true;
    723                 }
    724             }
    725         }
    726         return false;
    727
    728     }
    729
    730     /***
    731      * @return Returns hash code for this instance
    732      */
    733
    734     public int hashCode() {
    735         return this.jmsMessageID != null ? this.jmsMessageID.hashCode() : super.hashCode();
    736     }
    737
    738     /***
    739      * Returns true if this instance is equivalent to obj
    740      *
    741      * @param obj the other instance to test
    742      * @return true/false
    743      */
    744
    745     public boolean equals(Object obj) {
    746         boolean result = obj == this;
    747         if (!result && obj != null && obj instanceof ActiveMQMessage) {
    748             ActiveMQMessage other = (ActiveMQMessage) obj;
    749             result = this.jmsMessageID == other.jmsMessageID ||
    750                     (this.jmsMessageID != null && other.jmsMessageID != null &&
    751                     this.jmsMessageID.equals(other.jmsMessageID));
    752         }
    753         return result;
    754     }
    755
    756     /***
    757      * @param o object to compare
    758      * @return 1 if this > o else 0 if they are equal or -1 if this < o
    759      */
    760     public int compareTo(Object o) {
    761         if (o instanceof ActiveMQMessage) {
    762             return compareTo((ActiveMQMessage) o);
    763         }
    764         return -1;
    765     }
    766
    767     /***
    768      * Sorted by destination and then messageId
    769      *
    770      * @param that another message to compare against
    771      * @return 1 if this > that else 0 if they are equal or -1 if this < that
    772      */
    773     public int compareTo(ActiveMQMessage that) {
    774         int answer = 1;
    775
    776         if (that != null && this.jmsDestination != null && that.jmsDestination != null) {
    777             answer = this.jmsDestination.compareTo(that.jmsDestination);
    778             if (answer == 0) {
    779                 if (this.jmsMessageID != null && that.jmsMessageID != null) {
    780                     answer = IdGenerator.compare(this.jmsMessageID, that.jmsMessageID);
    781                 }
    782                 else {
    783                     answer = 1;
    784                 }
    785             }
    786         }
    787         return answer;
    788     }
    789
    790
    791     /***
    792      * @return Returns a shallow copy of the message instance
    793      * @throws JMSException
    794      */
    795
    796     public ActiveMQMessage shallowCopy() throws JMSException {
    797         ActiveMQMessage other = new ActiveMQMessage();
    798         this.initializeOther(other);
    799         return other;
    800     }
    801
    802     /***
    803      * @return Returns a deep copy of the message - note the header fields are only shallow copied
    804      * @throws JMSException
    805      */
    806
    807     public ActiveMQMessage deepCopy() throws JMSException {
    808         return shallowCopy();
    809     }
    810
    811
    812     /***
    813      * Indicates if the Message has expired
    814      *
    815      * @param currentTime -
    816      *                    the current time in milliseconds
    817      * @return true if the message can be expired
    818      * @throws JMSException on internal error
    819      */
    820     public boolean isExpired(long currentTime) throws JMSException {
    821         boolean result = false;
    822         long expiration = getJMSExpiration();
    823         if (expiration > 0 && expiration < currentTime) {
    824             result = true;
    825         }
    826         return result;
    827     }
    828
    829     public boolean isExpired() throws JMSException {
    830         return isExpired(System.currentTimeMillis());
    831     }
    832
    833     /***
    834      * Initializes another message with current values from this instance
    835      *
    836      * @param other the other ActiveMQMessage to initialize
    837      */
    838     protected void initializeOther(ActiveMQMessage other) {
    839         other.jmsMessageID = this.jmsMessageID;
    840         other.jmsClientID = this.jmsClientID;
    841         other.jmsCorrelationID = this.jmsCorrelationID;
    842         other.jmsDestination = this.jmsDestination;
    843         other.jmsReplyTo = this.jmsReplyTo;
    844         other.jmsDeliveryMode = this.jmsDeliveryMode;
    845         other.jmsRedelivered = this.jmsRedelivered;
    846         other.jmsType = this.jmsType;
    847         other.jmsExpiration = this.jmsExpiration;
    848         other.jmsPriority = this.jmsPriority;
    849         other.jmsTimestamp = this.jmsTimestamp;
    850         other.properties = this.properties;
    851         other.readOnlyProperties = this.readOnlyProperties;
    852         other.readOnlyMessage = this.readOnlyMessage;
    853         other.entryBrokerName = this.entryBrokerName;
    854         other.entryClusterName = this.entryClusterName;
    855         other.consumerNos = this.consumerNos;
    856         other.producerID = this.producerID;
    857         other.transactionId = this.transactionId;
    858         other.xaTransacted = this.xaTransacted;
    859         other.bodyAsBytes = this.bodyAsBytes;
    860         other.messageAcknowledge = this.messageAcknowledge;
    861         other.messageIdentity = this.messageIdentity;
    862     }
    863
    864     /***
    865      * Gets the message ID.
    866      * <p/>
    867      * <P>The <CODE>JMSMessageID</CODE> header field contains a value that
    868      * uniquely identifies each message sent by a provider.
    869      * <p/>
    870      * <P>When a message is sent, <CODE>JMSMessageID</CODE> can be ignored.
    871      * When the <CODE>send</CODE> or <CODE>publish</CODE> method returns, it
    872      * contains a provider-assigned value.
    873      * <p/>
    874      * <P>A <CODE>JMSMessageID</CODE> is a <CODE>String</CODE> value that
    875      * should function as a
    876      * unique key for identifying messages in a historical repository.
    877      * The exact scope of uniqueness is provider-defined. It should at
    878      * least cover all messages for a specific installation of a
    879      * provider, where an installation is some connected set of message
    880      * routers.
    881      * <p/>
    882      * <P>All <CODE>JMSMessageID</CODE> values must start with the prefix
    883      * <CODE>'ID:'</CODE>.
    884      * Uniqueness of message ID values across different providers is
    885      * not required.
    886      * <p/>
    887      * <P>Since message IDs take some effort to create and increase a
    888      * message's size, some JMS providers may be able to optimize message
    889      * overhead if they are given a hint that the message ID is not used by
    890      * an application. By calling the
    891      * <CODE>MessageProducer.setDisableMessageID</CODE> method, a JMS client
    892      * enables this potential optimization for all messages sent by that
    893      * message producer. If the JMS provider accepts this
    894      * hint, these messages must have the message ID set to null; if the
    895      * provider ignores the hint, the message ID must be set to its normal
    896      * unique value.
    897      *
    898      * @return the message ID
    899      * @see javax.jms.Message#setJMSMessageID(String)
    900      * @see javax.jms.MessageProducer#setDisableMessageID(boolean)
    901      */
    902
    903     public String getJMSMessageID() {
    904         return this.jmsMessageID;
    905     }
    906
    907
    908     /***
    909      * Sets the message ID.
    910      * <p/>
    911      * <P>JMS providers set this field when a message is sent. This method
    912      * can be used to change the value for a message that has been received.
    913      *
    914      * @param id the ID of the message
    915      * @see javax.jms.Message#getJMSMessageID()
    916      */
    917
    918     public void setJMSMessageID(String id) {
    919         this.jmsMessageID = id;
    920     }
    921
    922
    923     /***
    924      * Gets the message timestamp.
    925      * <p/>
    926      * <P>The <CODE>JMSTimestamp</CODE> header field contains the time a
    927      * message was
    928      * handed off to a provider to be sent. It is not the time the
    929      * message was actually transmitted, because the actual send may occur
    930      * later due to transactions or other client-side queueing of messages.
    931      * <p/>
    932      * <P>When a message is sent, <CODE>JMSTimestamp</CODE> is ignored. When
    933      * the <CODE>send</CODE> or <CODE>publish</CODE>
    934      * method returns, it contains a time value somewhere in the interval
    935      * between the call and the return. The value is in the format of a normal
    936      * millis time value in the Java programming language.
    937      * <p/>
    938      * <P>Since timestamps take some effort to create and increase a
    939      * message's size, some JMS providers may be able to optimize message
    940      * overhead if they are given a hint that the timestamp is not used by an
    941      * application. By calling the
    942      * <CODE>MessageProducer.setDisableMessageTimestamp</CODE> method, a JMS
    943      * client enables this potential optimization for all messages sent by
    944      * that message producer. If the JMS provider accepts this
    945      * hint, these messages must have the timestamp set to zero; if the
    946      * provider ignores the hint, the timestamp must be set to its normal
    947      * value.
    948      *
    949      * @return the message timestamp
    950      * @see javax.jms.Message#setJMSTimestamp(long)
    951      * @see javax.jms.MessageProducer#setDisableMessageTimestamp(boolean)
    952      */
    953
    954     public long getJMSTimestamp() {
    955         return jmsTimestamp;
    956     }
    957
    958
    959     /***
    960      * Sets the message timestamp.
    961      * <p/>
    962      * <P>JMS providers set this field when a message is sent. This method
    963      * can be used to change the value for a message that has been received.
    964      *
    965      * @param timestamp the timestamp for this message
    966      * @see javax.jms.Message#getJMSTimestamp()
    967      */
    968
    969     public void setJMSTimestamp(long timestamp) {
    970         this.jmsTimestamp = timestamp;
    971     }
    972
    973
    974     /***
    975      * Gets the correlation ID as an array of bytes for the message.
    976      * <p/>
    977      * <P>The use of a <CODE>byte[]</CODE> value for
    978      * <CODE>JMSCorrelationID</CODE> is non-portable.
    979      *
    980      * @return the correlation ID of a message as an array of bytes
    981      * @see javax.jms.Message#setJMSCorrelationID(String)
    982      * @see javax.jms.Message#getJMSCorrelationID()
    983      * @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[])
    984      */
    985
    986     public byte[] getJMSCorrelationIDAsBytes() {
    987         return this.jmsCorrelationID != null ? this.jmsCorrelationID.getBytes() : null;
    988     }
    989
    990
    991     /***
    992      * Sets the correlation ID as an array of bytes for the message.
    993      * <p/>
    994      * <P>The array is copied before the method returns, so
    995      * future modifications to the array will not alter this message header.
    996      * <p/>
    997      * <P>If a provider supports the native concept of correlation ID, a
    998      * JMS client may need to assign specific <CODE>JMSCorrelationID</CODE>
    999      * values to match those expected by native messaging clients.
    1000      * JMS providers without native correlation ID values are not required to
    1001      * support this method and its corresponding get method; their
    1002      * implementation may throw a
    1003      * <CODE>java.lang.UnsupportedOperationException</CODE>.
    1004      * <p/>
    1005      * <P>The use of a <CODE>byte[]</CODE> value for
    1006      * <CODE>JMSCorrelationID</CODE> is non-portable.
    1007      *
    1008      * @param correlationID the correlation ID value as an array of bytes
    1009      * @see javax.jms.Message#setJMSCorrelationID(String)
    1010      * @see javax.jms.Message#getJMSCorrelationID()
    1011      * @see javax.jms.Message#getJMSCorrelationIDAsBytes()
    1012      */
    1013
    1014     public void setJMSCorrelationIDAsBytes(byte[] correlationID) {
    1015         if (correlationID == null) {
    1016             this.jmsCorrelationID = null;
    1017         }
    1018         else {
    1019             this.jmsCorrelationID = new String(correlationID);
    1020         }
    1021     }
    1022
    1023
    1024     /***
    1025      * Sets the correlation ID for the message.
    1026      * <p/>
    1027      * <P>A client can use the <CODE>JMSCorrelationID</CODE> header field to
    1028      * link one message with another. A typical use is to link a response
    1029      * message with its request message.
    1030      * <p/>
    1031      * <P><CODE>JMSCorrelationID</CODE> can hold one of the following:
    1032      * <UL>
    1033      * <LI>A provider-specific message ID
    1034      * <LI>An application-specific <CODE>String</CODE>
    1035      * <LI>A provider-native <CODE>byte[]</CODE> value
    1036      * </UL>
    1037      * <p/>
    1038      * <P>Since each message sent by a JMS provider is assigned a message ID
    1039      * value, it is convenient to link messages via message ID. All message ID
    1040      * values must start with the <CODE>'ID:'</CODE> prefix.
    1041      * <p/>
    1042      * <P>In some cases, an application (made up of several clients) needs to
    1043      * use an application-specific value for linking messages. For instance,
    1044      * an application may use <CODE>JMSCorrelationID</CODE> to hold a value
    1045      * referencing some external information. Application-specified values
    1046      * must not start with the <CODE>'ID:'</CODE> prefix; this is reserved for
    1047      * provider-generated message ID values.
    1048      * <p/>
    1049      * <P>If a provider supports the native concept of correlation ID, a JMS
    1050      * client may need to assign specific <CODE>JMSCorrelationID</CODE> values
    1051      * to match those expected by clients that do not use the JMS API. A
    1052      * <CODE>byte[]</CODE> value is used for this
    1053      * purpose. JMS providers without native correlation ID values are not
    1054      * required to support <CODE>byte[]</CODE> values. The use of a
    1055      * <CODE>byte[]</CODE> value for <CODE>JMSCorrelationID</CODE> is
    1056      * non-portable.
    1057      *
    1058      * @param correlationID the message ID of a message being referred to
    1059      * @see javax.jms.Message#getJMSCorrelationID()
    1060      * @see javax.jms.Message#getJMSCorrelationIDAsBytes()
    1061      * @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[])
    1062      */
    1063
    1064     public void setJMSCorrelationID(String correlationID) {
    1065         this.jmsCorrelationID = correlationID;
    1066     }
    1067
    1068
    1069     /***
    1070      * Gets the correlation ID for the message.
    1071      * <p/>
    1072      * <P>This method is used to return correlation ID values that are
    1073      * either provider-specific message IDs or application-specific
    1074      * <CODE>String</CODE> values.
    1075      *
    1076      * @return the correlation ID of a message as a <CODE>String</CODE>
    1077      * @see javax.jms.Message#setJMSCorrelationID(String)
    1078      * @see javax.jms.Message#getJMSCorrelationIDAsBytes()
    1079      * @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[])
    1080      */
    1081
    1082     public String getJMSCorrelationID() {
    1083         return this.jmsCorrelationID;
    1084     }
    1085
    1086
    1087     /***
    1088      * Gets the <CODE>Destination</CODE> object to which a reply to this
    1089      * message should be sent.
    1090      *
    1091      * @return <CODE>Destination</CODE> to which to send a response to this
    1092      *         message
    1093      * @see javax.jms.Message#setJMSReplyTo(Destination)
    1094      */
    1095
    1096     public Destination getJMSReplyTo() {
    1097         return this.jmsReplyTo;
    1098
    1099     }
    1100
    1101
    1102     /***
    1103      * Sets the <CODE>Destination</CODE> object to which a reply to this
    1104      * message should be sent.
    1105      * <p/>
    1106      * <P>The <CODE>JMSReplyTo</CODE> header field contains the destination
    1107      * where a reply
    1108      * to the current message should be sent. If it is null, no reply is
    1109      * expected. The destination may be either a <CODE>Queue</CODE> object or
    1110      * a <CODE>Topic</CODE> object.
    1111      * <p/>
    1112      * <P>Messages sent with a null <CODE>JMSReplyTo</CODE> value may be a
    1113      * notification of some event, or they may just be some data the sender
    1114      * thinks is of interest.
    1115      * <p/>
    1116      * <P>Messages with a <CODE>JMSReplyTo</CODE> value typically expect a
    1117      * response. A response is optional; it is up to the client to decide.
    1118      * These messages are called requests. A message sent in response to a
    1119      * request is called a reply.
    1120      * <p/>
    1121      * <P>In some cases a client may wish to match a request it sent earlier
    1122      * with a reply it has just received. The client can use the
    1123      * <CODE>JMSCorrelationID</CODE> header field for this purpose.
    1124      *
    1125      * @param replyTo <CODE>Destination</CODE> to which to send a response to
    1126      *                this message
    1127      * @see javax.jms.Message#getJMSReplyTo()
    1128      */
    1129
    1130     public void setJMSReplyTo(Destination replyTo) {
    1131         this.jmsReplyTo = (ActiveMQDestination) replyTo;
    1132     }
    1133
    1134
    1135     /***
    1136      * Gets the <CODE>Destination</CODE> object for this message.
    1137      * <p/>
    1138      * <P>The <CODE>JMSDestination</CODE> header field contains the
    1139      * destination to which the message is being sent.
    1140      * <p/>
    1141      * <P>When a message is sent, this field is ignored. After completion
    1142      * of the <CODE>send</CODE> or <CODE>publish</CODE> method, the field
    1143      * holds the destination specified by the method.
    1144      * <p/>
    1145      * <P>When a message is received, its <CODE>JMSDestination</CODE> value
    1146      * must be equivalent to the value assigned when it was sent.
    1147      *
    1148      * @return the destination of this message
    1149      * @see javax.jms.Message#setJMSDestination(Destination)
    1150      */
    1151
    1152     public Destination getJMSDestination() {
    1153         return this.jmsDestination;
    1154     }
    1155
    1156
    1157     /***
    1158      * Sets the <CODE>Destination</CODE> object for this message.
    1159      * <p/>
    1160      * <P>JMS providers set this field when a message is sent. This method
    1161      * can be used to change the value for a message that has been received.
    1162      *
    1163      * @param destination the destination for this message
    1164      * @see javax.jms.Message#getJMSDestination()
    1165      */
    1166
    1167     public void setJMSDestination(Destination destination) {
    1168         this.jmsDestination = (ActiveMQDestination) destination;
    1169     }
    1170
    1171
    1172     /***
    1173      * Gets the <CODE>DeliveryMode</CODE> value specified for this message.
    1174      *
    1175      * @return the delivery mode for this message
    1176      * @see javax.jms.Message#setJMSDeliveryMode(int)
    1177      * @see javax.jms.DeliveryMode
    1178      */
    1179
    1180     public int getJMSDeliveryMode() {
    1181         return this.jmsDeliveryMode;
    1182     }
    1183
    1184
    1185     /***
    1186      * Sets the <CODE>DeliveryMode</CODE> value for this message.
    1187      * <p/>
    1188      * <P>JMS providers set this field when a message is sent. This method
    1189      * can be used to change the value for a message that has been received.
    1190      *
    1191      * @param deliveryMode the delivery mode for this message
    1192      * @see javax.jms.Message#getJMSDeliveryMode()
    1193      * @see javax.jms.DeliveryMode
    1194      */
    1195
    1196     public void setJMSDeliveryMode(int deliveryMode) {
    1197         this.jmsDeliveryMode = deliveryMode;
    1198     }
    1199
    1200
    1201     /***
    1202      * Gets an indication of whether this message is being redelivered.
    1203      * <p/>
    1204      * <P>If a client receives a message with the <CODE>JMSRedelivered</CODE>
    1205      * field set,
    1206      * it is likely, but not guaranteed, that this message was delivered
    1207      * earlier but that its receipt was not acknowledged
    1208      * at that time.
    1209      *
    1210      * @return true if this message is being redelivered
    1211      * @see javax.jms.Message#setJMSRedelivered(boolean)
    1212      */
    1213
    1214     public boolean getJMSRedelivered() {
    1215         return this.jmsRedelivered;
    1216     }
    1217
    1218
    1219     /***
    1220      * Specifies whether this message is being redelivered.
    1221      * <p/>
    1222      * <P>This field is set at the time the message is delivered. This
    1223      * method can be used to change the value for a message that has
    1224      * been received.
    1225      *
    1226      * @param redelivered an indication of whether this message is being
    1227      *                    redelivered
    1228      * @see javax.jms.Message#getJMSRedelivered()
    1229      */
    1230
    1231     public void setJMSRedelivered(boolean redelivered) {
    1232         this.jmsRedelivered = redelivered;
    1233     }
    1234
    1235
    1236     /***
    1237      * Gets the message type identifier supplied by the client when the
    1238      * message was sent.
    1239      *
    1240      * @return the message type
    1241      * @see javax.jms.Message#setJMSType(String)
    1242      */
    1243
    1244     public String getJMSType() {
    1245         return this.jmsType;
    1246     }
    1247
    1248     /***
    1249      * Sets the message type.
    1250      * <p/>
    1251      * <P>Some JMS providers use a message repository that contains the
    1252      * definitions of messages sent by applications. The <CODE>JMSType</CODE>
    1253      * header field may reference a message's definition in the provider's
    1254      * repository.
    1255      * <p/>
    1256      * <P>The JMS API does not define a standard message definition repository,
    1257      * nor does it define a naming policy for the definitions it contains.
    1258      * <p/>
    1259      * <P>Some messaging systems require that a message type definition for
    1260      * each application message be created and that each message specify its
    1261      * type. In order to work with such JMS providers, JMS clients should
    1262      * assign a value to <CODE>JMSType</CODE>, whether the application makes
    1263      * use of it or not. This ensures that the field is properly set for those
    1264      * providers that require it.
    1265      * <p/>
    1266      * <P>To ensure portability, JMS clients should use symbolic values for
    1267      * <CODE>JMSType</CODE> that can be configured at installation time to the
    1268      * values defined in the current provider's message repository. If string
    1269      * literals are used, they may not be valid type names for some JMS
    1270      * providers.
    1271      *
    1272      * @param type the message type
    1273      * @see javax.jms.Message#getJMSType()
    1274      */
    1275
    1276     public void setJMSType(String type) {
    1277         this.jmsType = type;
    1278     }
    1279
    1280
    1281     /***
    1282      * Gets the message's expiration value.
    1283      * <p/>
    1284      * <P>When a message is sent, the <CODE>JMSExpiration</CODE> header field
    1285      * is left unassigned. After completion of the <CODE>send</CODE> or
    1286      * <CODE>publish</CODE> method, it holds the expiration time of the
    1287      * message. This is the sum of the time-to-live value specified by the
    1288      * client and the GMT at the time of the <CODE>send</CODE> or
    1289      * <CODE>publish</CODE>.
    1290      * <p/>
    1291      * <P>If the time-to-live is specified as zero, <CODE>JMSExpiration</CODE>
    1292      * is set to zero to indicate that the message does not expire.
    1293      * <p/>
    1294      * <P>When a message's expiration time is reached, a provider should
    1295      * discard it. The JMS API does not define any form of notification of
    1296      * message expiration.
    1297      * <p/>
    1298      * <P>Clients should not receive messages that have expired; however,
    1299      * the JMS API does not guarantee that this will not happen.
    1300      *
    1301      * @return the time the message expires, which is the sum of the
    1302      *         time-to-live value specified by the client and the GMT at the
    1303      *         time of the send
    1304      * @see javax.jms.Message#setJMSExpiration(long)
    1305      */
    1306
    1307     public long getJMSExpiration() {
    1308         return this.jmsExpiration;
    1309     }
    1310
    1311
    1312     /***
    1313      * Sets the message's expiration value.
    1314      * <p/>
    1315      * <P>JMS providers set this field when a message is sent. This method
    1316      * can be used to change the value for a message that has been received.
    1317      *
    1318      * @param expiration the message's expiration time
    1319      * @see javax.jms.Message#getJMSExpiration()
    1320      */
    1321
    1322     public void setJMSExpiration(long expiration) {
    1323         this.jmsExpiration = expiration;
    1324     }
    1325
    1326     /***
    1327      * Gets the message priority level.
    1328      * <p/>
    1329      * <P>The JMS API defines ten levels of priority value, with 0 as the
    1330      * lowest
    1331      * priority and 9 as the highest. In addition, clients should consider
    1332      * priorities 0-4 as gradations of normal priority and priorities 5-9
    1333      * as gradations of expedited priority.
    1334      * <p/>
    1335      * <P>The JMS API does not require that a provider strictly implement
    1336      * priority
    1337      * ordering of messages; however, it should do its best to deliver
    1338      * expedited messages ahead of normal messages.
    1339      *
    1340      * @return the default message priority
    1341      * @see javax.jms.Message#setJMSPriority(int)
    1342      */
    1343
    1344     public int getJMSPriority() {
    1345         return this.jmsPriority;
    1346     }
    1347
    1348
    1349     /***
    1350      * Sets the priority level for this message.
    1351      * <p/>
    1352      * <P>JMS providers set this field when a message is sent. This method
    1353      * can be used to change the value for a message that has been received.
    1354      *
    1355      * @param priority the priority of this message
    1356      * @see javax.jms.Message#getJMSPriority()
    1357      */
    1358
    1359     public void setJMSPriority(int priority) {
    1360         this.jmsPriority = priority;
    1361     }
    1362
    1363     /***
    1364      * Clears a message's properties.
    1365      * <p/>
    1366      * <P>The message's header fields and body are not cleared.
    1367      */
    1368
    1369     public synchronized void clearProperties() {
    1370         if (this.properties != null) {
    1371             this.properties.clear();
    1372         }
    1373         this.readOnlyProperties = false;
    1374     }
    1375
    1376
    1377     /***
    1378      * Indicates whether a property value exists.
    1379      *
    1380      * @param name the name of the property to test
    1381      * @return true if the property exists
    1382      */
    1383
    1384     public boolean propertyExists(String name) {
    1385         return this.properties != null ? this.properties.containsKey(name) : false;
    1386     }
    1387
    1388
    1389     /***
    1390      * Returns the value of the <CODE>boolean</CODE> property with the
    1391      * specified name.
    1392      *
    1393      * @param name the name of the <CODE>boolean</CODE> property
    1394      * @return the <CODE>boolean</CODE> property value for the specified name
    1395      * @throws JMSException           if the JMS provider fails to get the property
    1396      *                                value due to some internal error.
    1397      * @throws MessageFormatException if this type conversion is invalid.
    1398      */
    1399
    1400     public boolean getBooleanProperty(String name) throws JMSException {
    1401         return vanillaToBoolean(this.properties, name);
    1402     }
    1403
    1404
    1405     /***
    1406      * Returns the value of the <CODE>byte</CODE> property with the specified
    1407      * name.
    1408      *
    1409      * @param name the name of the <CODE>byte</CODE> property
    1410      * @return the <CODE>byte</CODE> property value for the specified name
    1411      * @throws JMSException           if the JMS provider fails to get the property
    1412      *                                value due to some internal error.
    1413      * @throws MessageFormatException if this type conversion is invalid.
    1414      */
    1415
    1416     public byte getByteProperty(String name) throws JMSException {
    1417         return vanillaToByte(this.properties, name);
    1418     }
    1419
    1420
    1421     /***
    1422      * Returns the value of the <CODE>short</CODE> property with the specified
    1423      * name.
    1424      *
    1425      * @param name the name of the <CODE>short</CODE> property
    1426      * @return the <CODE>short</CODE> property value for the specified name
    1427      * @throws JMSException           if the JMS provider fails to get the property
    1428      *                                value due to some internal error.
    1429      * @throws MessageFormatException if this type conversion is invalid.
    1430      */
    1431
    1432     public short getShortProperty(String name) throws JMSException {
    1433         return vanillaToShort(this.properties, name);
    1434     }
    1435
    1436
    1437     /***
    1438      * Returns the value of the <CODE>int</CODE> property with the specified
    1439      * name.
    1440      *
    1441      * @param name the name of the <CODE>int</CODE> property
    1442      * @return the <CODE>int</CODE> property value for the specified name
    1443      * @throws JMSException           if the JMS provider fails to get the property
    1444      *                                value due to some internal error.
    1445      * @throws MessageFormatException if this type conversion is invalid.
    1446      */
    1447
    1448     public int getIntProperty(String name) throws JMSException {
    1449         return vanillaToInt(this.properties, name);
    1450     }
    1451
    1452
    1453     /***
    1454      * Returns the value of the <CODE>long</CODE> property with the specified
    1455      * name.
    1456      *
    1457      * @param name the name of the <CODE>long</CODE> property
    1458      * @return the <CODE>long</CODE> property value for the specified name
    1459      * @throws JMSException           if the JMS provider fails to get the property
    1460      *                                value due to some internal error.
    1461      * @throws MessageFormatException if this type conversion is invalid.
    1462      */
    1463
    1464     public long getLongProperty(String name) throws JMSException {
    1465         return vanillaToLong(this.properties, name);
    1466     }
    1467
    1468
    1469     /***
    1470      * Returns the value of the <CODE>float</CODE> property with the specified
    1471      * name.
    1472      *
    1473      * @param name the name of the <CODE>float</CODE> property
    1474      * @return the <CODE>float</CODE> property value for the specified name
    1475      * @throws JMSException           if the JMS provider fails to get the property
    1476      *                                value due to some internal error.
    1477      * @throws MessageFormatException if this type conversion is invalid.
    1478      */
    1479
    1480     public float getFloatProperty(String name) throws JMSException {
    1481         return vanillaToFloat(this.properties, name);
    1482     }
    1483
    1484
    1485     /***
    1486      * Returns the value of the <CODE>double</CODE> property with the specified
    1487      * name.
    1488      *
    1489      * @param name the name of the <CODE>double</CODE> property
    1490      * @return the <CODE>double</CODE> property value for the specified name
    1491      * @throws JMSException           if the JMS provider fails to get the property
    1492      *                                value due to some internal error.
    1493      * @throws MessageFormatException if this type conversion is invalid.
    1494      */
    1495
    1496     public double getDoubleProperty(String name) throws JMSException {
    1497         return vanillaToDouble(this.properties, name);
    1498     }
    1499
    1500
    1501     /***
    1502      * Returns the value of the <CODE>String</CODE> property with the specified
    1503      * name.
    1504      *
    1505      * @param name the name of the <CODE>String</CODE> property
    1506      * @return the <CODE>String</CODE> property value for the specified name;
    1507      *         if there is no property by this name, a null value is returned
    1508      * @throws JMSException           if the JMS provider fails to get the property
    1509      *                                value due to some internal error.
    1510      * @throws MessageFormatException if this type conversion is invalid.
    1511      */
    1512
    1513     public String getStringProperty(String name) throws JMSException {
    1514         return vanillaToString(this.properties, name);
    1515     }
    1516
    1517
    1518     /***
    1519      * Returns the value of the Java object property with the specified name.
    1520      * <p/>
    1521      * <P>This method can be used to return, in objectified format,
    1522      * an object that has been stored as a property in the message with the
    1523      * equivalent <CODE>setObjectProperty</CODE> method call, or its equivalent
    1524      * primitive <CODE>set<I>type</I>Property</CODE> method.
    1525      *
    1526      * @param name the name of the Java object property
    1527      * @return the Java object property value with the specified name, in
    1528      *         objectified format (for example, if the property was set as an
    1529      *         <CODE>int</CODE>, an <CODE>Integer</CODE> is
    1530      *         returned); if there is no property by this name, a null value
    1531      *         is returned
    1532      */
    1533
    1534     public Object getObjectProperty(String name) {
    1535         return this.properties != null ? this.properties.get(name) : null;
    1536     }
    1537
    1538
    1539     /***
    1540      * Returns an <CODE>Enumeration</CODE> of all the property names.
    1541      * <p/>
    1542      * <P>Note that JMS standard header fields are not considered
    1543      * properties and are not returned in this enumeration.
    1544      *
    1545      * @return an enumeration of all the names of property values
    1546      */
    1547
    1548     public Enumeration getPropertyNames() {
    1549         if (this.properties == null) {
    1550             this.properties = new Hashtable();
    1551         }
    1552         return this.properties.keys();
    1553     }
    1554
    1555     /***
    1556      * Retrieve the message properties as a Hashtable
    1557      *
    1558      * @return the Hashtable representing the properties or null if not set or used
    1559      */
    1560
    1561     public Hashtable getProperties() {
    1562         return this.properties;
    1563     }
    1564
    1565     /***
    1566      * Set the Message's properties from an external source
    1567      * No checking on correct types is done by this method
    1568      *
    1569      * @param newProperties
    1570      */
    1571
    1572     void setProperties(Hashtable newProperties) {
    1573         this.properties = newProperties;
    1574     }
    1575
    1576
    1577     /***
    1578      * Sets a <CODE>boolean</CODE> property value with the specified name into
    1579      * the message.
    1580      *
    1581      * @param name  the name of the <CODE>boolean</CODE> property
    1582      * @param value the <CODE>boolean</CODE> property value to set
    1583      * @throws JMSException                 if the JMS provider fails to set the property
    1584      *                                      due to some internal error.
    1585      * @throws IllegalArgumentException     if the name is null or if the name is
    1586      *                                      an empty string.
    1587      * @throws MessageNotWriteableException if properties are read-only
    1588      */
    1589
    1590     public void setBooleanProperty(String name, boolean value) throws JMSException {
    1591         prepareProperty(name);
    1592         this.properties.put(name, (value) ? Boolean.TRUE : Boolean.FALSE);
    1593     }
    1594
    1595
    1596     /***
    1597      * Sets a <CODE>byte</CODE> property value with the specified name into
    1598      * the message.
    1599      *
    1600      * @param name  the name of the <CODE>byte</CODE> property
    1601      * @param value the <CODE>byte</CODE> property value to set
    1602      * @throws JMSException                 if the JMS provider fails to set the property
    1603      *                                      due to some internal error.
    1604      * @throws IllegalArgumentException     if the name is null or if the name is
    1605      *                                      an empty string.
    1606      * @throws MessageNotWriteableException if properties are read-only
    1607      */
    1608
    1609     public void setByteProperty(String name, byte value) throws JMSException {
    1610         prepareProperty(name);
    1611         this.properties.put(name, new Byte(value));
    1612     }
    1613
    1614
    1615     /***
    1616      * Sets a <CODE>short</CODE> property value with the specified name into
    1617      * the message.
    1618      *
    1619      * @param name  the name of the <CODE>short</CODE> property
    1620      * @param value the <CODE>short</CODE> property value to set
    1621      * @throws JMSException                 if the JMS provider fails to set the property
    1622      *                                      due to some internal error.
    1623      * @throws IllegalArgumentException     if the name is null or if the name is
    1624      *                                      an empty string.
    1625      * @throws MessageNotWriteableException if properties are read-only
    1626      */
    1627
    1628     public void setShortProperty(String name, short value) throws JMSException {
    1629         prepareProperty(name);
    1630         this.properties.put(name, new Short(value));
    1631     }
    1632
    1633
    1634     /***
    1635      * Sets an <CODE>int</CODE> property value with the specified name into
    1636      * the message.
    1637      *
    1638      * @param name  the name of the <CODE>int</CODE> property
    1639      * @param value the <CODE>int</CODE> property value to set
    1640      * @throws JMSException                 if the JMS provider fails to set the property
    1641      *                                      due to some internal error.
    1642      * @throws IllegalArgumentException     if the name is null or if the name is
    1643      *                                      an empty string.
    1644      * @throws MessageNotWriteableException if properties are read-only
    1645      */
    1646
    1647     public void setIntProperty(String name, int value) throws JMSException {
    1648         prepareProperty(name);
    1649         this.properties.put(name, new Integer(value));
    1650     }
    1651
    1652
    1653     /***
    1654      * Sets a <CODE>long</CODE> property value with the specified name into
    1655      * the message.
    1656      *
    1657      * @param name  the name of the <CODE>long</CODE> property
    1658      * @param value the <CODE>long</CODE> property value to set
    1659      * @throws JMSException                 if the JMS provider fails to set the property
    1660      *                                      due to some internal error.
    1661      * @throws IllegalArgumentException     if the name is null or if the name is
    1662      *                                      an empty string.
    1663      * @throws MessageNotWriteableException if properties are read-only
    1664      */
    1665
    1666     public void setLongProperty(String name, long value) throws JMSException {
    1667         prepareProperty(name);
    1668         this.properties.put(name, new Long(value));
    1669     }
    1670
    1671
    1672     /***
    1673      * Sets a <CODE>float</CODE> property value with the specified name into
    1674      * the message.
    1675      *
    1676      * @param name  the name of the <CODE>float</CODE> property
    1677      * @param value the <CODE>float</CODE> property value to set
    1678      * @throws JMSException                 if the JMS provider fails to set the property
    1679      *                                      due to some internal error.
    1680      * @throws IllegalArgumentException     if the name is null or if the name is
    1681      *                                      an empty string.
    1682      * @throws MessageNotWriteableException if properties are read-only
    1683      */
    1684
    1685     public void setFloatProperty(String name, float value) throws JMSException {
    1686         prepareProperty(name);
    1687         this.properties.put(name, new Float(value));
    1688
    1689     }
    1690
    1691
    1692     /***
    1693      * Sets a <CODE>double</CODE> property value with the specified name into
    1694      * the message.
    1695      *
    1696      * @param name  the name of the <CODE>double</CODE> property
    1697      * @param value the <CODE>double</CODE> property value to set
    1698      * @throws JMSException                 if the JMS provider fails to set the property
    1699      *                                      due to some internal error.
    1700      * @throws IllegalArgumentException     if the name is null or if the name is
    1701      *                                      an empty string.
    1702      * @throws MessageNotWriteableException if properties are read-only
    1703      */
    1704
    1705     public void setDoubleProperty(String name, double value) throws JMSException {
    1706         prepareProperty(name);
    1707         this.properties.put(name, new Double(value));
    1708     }
    1709
    1710
    1711     /***
    1712      * Sets a <CODE>String</CODE> property value with the specified name into
    1713      * the message.
    1714      *
    1715      * @param name  the name of the <CODE>String</CODE> property
    1716      * @param value the <CODE>String</CODE> property value to set
    1717      * @throws JMSException                 if the JMS provider fails to set the property
    1718      *                                      due to some internal error.
    1719      * @throws IllegalArgumentException     if the name is null or if the name is
    1720      *                                      an empty string.
    1721      * @throws MessageNotWriteableException if properties are read-only
    1722      */
    1723
    1724     public void setStringProperty(String name, String value) throws JMSException {
    1725         prepareProperty(name);
    1726         this.properties.put(name, value);
    1727     }
    1728
    1729
    1730     /***
    1731      * Sets a Java object property value with the specified name into the
    1732      * message.
    1733      * <p/>
    1734      * <P>Note that this method works only for the objectified primitive
    1735      * object types (<CODE>Integer</CODE>, <CODE>Double</CODE>,
    1736      * <CODE>Long</CODE> ...) and <CODE>String</CODE> objects.
    1737      *
    1738      * @param name  the name of the Java object property
    1739      * @param value the Java object property value to set
    1740      * @throws JMSException                 if the JMS provider fails to set the property
    1741      *                                      due to some internal error.
    1742      * @throws IllegalArgumentException     if the name is null or if the name is
    1743      *                                      an empty string.
    1744      * @throws MessageFormatException       if the object is invalid
    1745      * @throws MessageNotWriteableException if properties are read-only
    1746      */
    1747
    1748     public void setObjectProperty(String name, Object value) throws JMSException {
    1749         prepareProperty(name);
    1750         if (value instanceof Number ||
    1751                 value instanceof Character ||
    1752                 value instanceof Boolean ||
    1753                 value instanceof String) {
    1754             this.properties.put(name, value);
    1755         }
    1756         else {
    1757             throw new MessageFormatException("Cannot set property to type: " + value.getClass().getName());
    1758         } // if-else
    1759
    1760     }
    1761
    1762
    1763     /***
    1764      * Acknowledges all consumed messages of the session of this consumed
    1765      * message.
    1766      * <p/>
    1767      * <P>All consumed JMS messages support the <CODE>acknowledge</CODE>
    1768      * method for use when a client has specified that its JMS session's
    1769      * consumed messages are to be explicitly acknowledged.  By invoking
    1770      * <CODE>acknowledge</CODE> on a consumed message, a client acknowledges
    1771      * all messages consumed by the session that the message was delivered to.
    1772      * <p/>
    1773      * <P>Calls to <CODE>acknowledge</CODE> are ignored for both transacted
    1774      * sessions and sessions specified to use implicit acknowledgement modes.
    1775      * <p/>
    1776      * <P>A client may individually acknowledge each message as it is consumed,
    1777      * or it may choose to acknowledge messages as an application-defined group
    1778      * (which is done by calling acknowledge on the last received message of the group,
    1779      * thereby acknowledging all messages consumed by the session.)
    1780      * <p/>
    1781      * <P>Messages that have been received but not acknowledged may be
    1782      * redelivered.
    1783      *
    1784      * @throws JMSException if the JMS provider fails to acknowledge the
    1785      *                      messages due to some internal error.
    1786      * @throws javax.jms.IllegalStateException
    1787      *                      if this method is called on a closed
    1788      *                      session.
    1789      * @see javax.jms.Session#CLIENT_ACKNOWLEDGE
    1790      */
    1791
    1792     public void acknowledge() throws JMSException {
    1793         if (this.messageAcknowledge != null) {
    1794             this.messageAcknowledge.acknowledge();
    1795         }
    1796     }
    1797
    1798
    1799     /***
    1800      * Clears out the message body. Clearing a message's body does not clear
    1801      * its header values or property entries.
    1802      * <p/>
    1803      * <P>If this message body was read-only, calling this method leaves
    1804      * the message body in the same state as an empty body in a newly
    1805      * created message.
    1806      *
    1807      * @throws JMSException if the JMS provider fails to clear the message
    1808      *                      body due to some internal error.
    1809      */
    1810
    1811     public void clearBody() throws JMSException {
    1812         this.readOnlyMessage = false;
    1813         this.bodyAsBytes = null;
    1814     }
    1815
    1816     boolean vanillaToBoolean(Hashtable table, String name) throws JMSException {
    1817         boolean result = false;
    1818         if (table != null) {
    1819             Object value = table.get(name);
    1820             if (value != null) {
    1821                 if (value instanceof Boolean) {
    1822                     result = ((Boolean) value).booleanValue();
    1823                 }
    1824                 else if (value instanceof String) {
    1825                     // will throw a runtime exception if cannot convert
    1826                     result = Boolean.valueOf((String) value).booleanValue();
    1827                 }
    1828                 else {
    1829                     throw new MessageFormatException(name + " not a Boolean type");
    1830                 }
    1831             }
    1832         }
    1833         return result;
    1834     }
    1835
    1836     byte vanillaToByte(Hashtable table, String name) throws JMSException {
    1837         byte result = 0;
    1838         if (table != null) {
    1839             Object value = table.get(name);
    1840             if (value != null) {
    1841                 if (value instanceof Byte) {
    1842                     result = ((Byte) value).byteValue();
    1843                 }
    1844                 else if (value instanceof String) {
    1845                     result = Byte.valueOf((String) value).byteValue();
    1846                 }
    1847                 else {
    1848                     throw new MessageFormatException(name + " not a Byte type");
    1849                 }
    1850             }
    1851             else {
    1852 //object doesn't exist - so treat as a null ..
    1853                 throw new NumberFormatException("Cannot interpret null as a Byte");
    1854             }
    1855         }
    1856         return result;
    1857     }
    1858
    1859     short vanillaToShort(Hashtable table, String name) throws JMSException {
    1860         short result = 0;
    1861         if (table != null) {
    1862             Object value = table.get(name);
    1863             if (value != null) {
    1864                 if (value instanceof Short) {
    1865                     result = ((Short) value).shortValue();
    1866                 }
    1867                 else if (value instanceof String) {
    1868                     return Short.valueOf((String) value).shortValue();
    1869                 }
    1870                 else if (value instanceof Byte) {
    1871                     result = ((Byte) value).byteValue();
    1872                 }
    1873                 else {
    1874                     throw new MessageFormatException(name + " not a Short type");
    1875                 }
    1876             }
    1877             else {
    1878                 throw new NumberFormatException(name + " is null");
    1879             }
    1880         }
    1881         return result;
    1882     }
    1883
    1884     int vanillaToInt(Hashtable table, String name) throws JMSException {
    1885         int result = 0;
    1886         if (table != null) {
    1887             Object value = table.get(name);
    1888             if (value != null) {
    1889                 if (value instanceof Integer) {
    1890                     result = ((Integer) value).intValue();
    1891                 }
    1892                 else if (value instanceof String) {
    1893                     result = Integer.valueOf((String) value).intValue();
    1894                 }
    1895                 else if (value instanceof Byte) {
    1896                     result = ((Byte) value).intValue();
    1897                 }
    1898                 else if (value instanceof Short) {
    1899                     result = ((Short) value).intValue();
    1900                 }
    1901                 else {
    1902                     throw new MessageFormatException(name + " not an Integer type");
    1903                 }
    1904             }
    1905             else {
    1906                 throw new NumberFormatException(name + " is null");
    1907             }
    1908         }
    1909         return result;
    1910     }
    1911
    1912     long vanillaToLong(Hashtable table, String name) throws JMSException {
    1913         long result = 0;
    1914         if (table != null) {
    1915             Object value = table.get(name);
    1916             if (value != null) {
    1917                 if (value instanceof Long) {
    1918                     result = ((Long) value).longValue();
    1919                 }
    1920                 else if (value instanceof String) {
    1921                     // will throw a runtime exception if cannot convert
    1922                     result = Long.valueOf((String) value).longValue();
    1923                 }
    1924                 else if (value instanceof Byte) {
    1925                     result = ((Byte) value).byteValue();
    1926                 }
    1927                 else if (value instanceof Short) {
    1928                     result = ((Short) value).shortValue();
    1929                 }
    1930                 else if (value instanceof Integer) {
    1931                     result = ((Integer) value).intValue();
    1932                 }
    1933                 else {
    1934                     throw new MessageFormatException(name + " not a Long type");
    1935                 }
    1936             }
    1937             else {
    1938                 throw new NumberFormatException(name + " is null");
    1939             }
    1940         }
    1941         return result;
    1942     }
    1943
    1944     float vanillaToFloat(Hashtable table, String name) throws JMSException {
    1945         float result = 0.0f;
    1946         if (table != null) {
    1947             Object value = table.get(name);
    1948             if (value != null) {
    1949                 if (value instanceof Float) {
    1950                     result = ((Float) value).floatValue();
    1951                 }
    1952                 else if (value instanceof String) {
    1953                     result = Float.valueOf((String) value).floatValue();
    1954                 }
    1955                 else {
    1956                     throw new MessageFormatException(name + " not a Float type: " + value.getClass());
    1957                 }
    1958             }
    1959             else {
    1960                 throw new NullPointerException(name + " is null");
    1961             }
    1962         }
    1963         return result;
    1964     }
    1965
    1966     double vanillaToDouble(Hashtable table, String name) throws JMSException {
    1967         double result = 0.0d;
    1968         if (table != null) {
    1969             Object value = table.get(name);
    1970             if (value != null) {
    1971                 if (value instanceof Double) {
    1972                     result = ((Double) value).doubleValue();
    1973                 }
    1974                 else if (value instanceof String) {
    1975                     result = Double.valueOf((String) value).doubleValue();
    1976                 }
    1977                 else if (value instanceof Float) {
    1978                     result = ((Float) value).floatValue();
    1979                 }
    1980                 else {
    1981                     throw new MessageFormatException(name + " not a Double type");
    1982                 }
    1983             }
    1984             else {
    1985                 throw new NullPointerException(name + " is null");
    1986             }
    1987         }
    1988         return result;
    1989     }
    1990
    1991
    1992     String vanillaToString(Hashtable table, String name) throws JMSException {
    1993         String result = null;
    1994         if (table != null) {
    1995             Object value = table.get(name);
    1996             if (value != null) {
    1997                 if (value instanceof String || value instanceof Number || value instanceof Boolean) {
    1998                     result = value.toString();
    1999                 }
    2000                 else {
    2001                     throw new MessageFormatException(name + " not a String type");
    2002                 }
    2003             }
    2004         }
    2005         return result;
    2006     }
    2007
    2008     private void prepareProperty(String name) throws JMSException {
    2009         if (name == null || name.length() == 0) {
    2010             throw new IllegalArgumentException("Invalid name: " + name);
    2011         }
    2012         if (this.readOnlyProperties) {
    2013             throw new MessageNotWriteableException("Properties are read-only");
    2014         }
    2015         if (this.properties == null) {
    2016             this.properties = new Hashtable();
    2017         }
    2018     }
    2019
    2020     /***
    2021      * @return Returns the entryBrokerName.
    2022      */
    2023     public String getEntryBrokerName() {
    2024         return this.entryBrokerName;
    2025     }
    2026
    2027     /***
    2028      * @param newEntryBrokerName The entryBrokerName to set.
    2029      */
    2030     public void setEntryBrokerName(String newEntryBrokerName) {
    2031         this.entryBrokerName = newEntryBrokerName;
    2032     }
    2033
    2034     /***
    2035      * @return Returns the entryClusterName.
    2036      */
    2037     public String getEntryClusterName() {
    2038         return this.entryClusterName;
    2039     }
    2040
    2041     /***
    2042      * @param newEntryClusterName The entryClusterName to set.
    2043      */
    2044     public void setEntryClusterName(String newEntryClusterName) {
    2045         this.entryClusterName = newEntryClusterName;
    2046     }
    2047
    2048     /***
    2049      * @return Returns the consumerNos.
    2050      */
    2051     public int[] getConsumerNos() {
    2052         return this.consumerNos;
    2053     }
    2054
    2055     /***
    2056      * @param newConsumerNos The consumerIDs to set.
    2057      */
    2058     public void setConsumerNos(int[] newConsumerNos) {
    2059         this.consumerNos = newConsumerNos;
    2060     }
    2061
    2062     /***
    2063      * @return Returns the jmsClientID.
    2064      */
    2065     public String getJMSClientID() {
    2066         return this.jmsClientID;
    2067     }
    2068
    2069     /***
    2070      * @param newJmsClientID The jmsClientID to set.
    2071      */
    2072     public void setJMSClientID(String newJmsClientID) {
    2073         this.jmsClientID = newJmsClientID;
    2074     }
    2075
    2076     /***
    2077      * @return Returns the producerID.
    2078      */
    2079     public String getProducerID() {
    2080         return this.producerID;
    2081     }
    2082
    2083     /***
    2084      * @param newProducerID The producerID to set.
    2085      */
    2086     public void setProducerID(String newProducerID) {
    2087         this.producerID = newProducerID;
    2088     }
    2089
    2090
    2091     /***
    2092      * @return Returns true if this message is part of a transaction
    2093      */
    2094
    2095     public boolean isPartOfTransaction() {
    2096         return this.transactionId != null && this.transactionId.length() > 0;
    2097     }
    2098
    2099     /***
    2100      * @return Returns the transactionId.
    2101      */
    2102     public String getTransactionId() {
    2103         return this.transactionId;
    2104     }
    2105
    2106     /***
    2107      * @param newTransactionId The transactionId to set.
    2108      */
    2109     public void setTransactionId(String newTransactionId) {
    2110         this.transactionId = newTransactionId;
    2111     }
    2112
    2113     /***
    2114      * @return Returns the consumerId.
    2115      */
    2116     public String getConsumerId() {
    2117         return consumerId;
    2118     }
    2119
    2120     /***
    2121      * @param consId The consumerId to set.
    2122      */
    2123     public void setConsumerId(String consId) {
    2124         this.consumerId = consId;
    2125     }
    2126
    2127     /***
    2128      * @return Returns the messageConsumed.
    2129      */
    2130     public boolean isMessageConsumed() {
    2131         return messageConsumed;
    2132     }
    2133
    2134     /***
    2135      * @param messageConsumed The messageConsumed to set.
    2136      */
    2137     public void setMessageConsumed(boolean messageConsumed) {
    2138         this.messageConsumed = messageConsumed;
    2139     }
    2140
    2141
    2142     /***
    2143      * Convert the message body to data
    2144      *
    2145      * @throws IOException
    2146      */
    2147     public final void convertBodyToBytes() throws IOException {
    2148         if (bodyAsBytes == null) {
    2149             ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
    2150             DataOutputStream dataOut = new DataOutputStream(bytesOut);
    2151             writeBody(dataOut);
    2152             dataOut.flush();
    2153             bodyAsBytes = bytesOut.toByteArray();
    2154             dataOut.close();
    2155         }
    2156     }
    2157
    2158     /***
    2159      * Builds the message body from data
    2160      *
    2161      * @throws IOException
    2162      */
    2163     public final void buildBodyFromBytes() throws IOException {
    2164         if (bodyAsBytes != null) {
    2165             ByteArrayInputStream bytesIn = new ByteArrayInputStream(bodyAsBytes);
    2166             DataInputStream dataIn = new DataInputStream(bytesIn);
    2167             readBody(dataIn);
    2168             dataIn.close();
    2169         }
    2170     }
    2171
    2172     /***
    2173      * Used serialize the message body to an output stream
    2174      *
    2175      * @param dataOut
    2176      * @throws IOException
    2177      */
    2178
    2179     protected void writeBody(DataOutput dataOut) throws IOException {
    2180
    2181     }
    2182
    2183     /***
    2184      * Used to help build the body from an input stream
    2185      *
    2186      * @param dataIn
    2187      * @throws IOException
    2188      */
    2189
    2190     protected void readBody(DataInput dataIn) throws IOException {
    2191
    2192     }
    2193
    2194     /***
    2195      * @return Returns the bodyAsBytes.
    2196      * @throws IOException
    2197      */
    2198     public byte[] getBodyAsBytes() throws IOException {
    2199         if (bodyAsBytes == null) {
    2200             convertBodyToBytes();
    2201         }
    2202         return bodyAsBytes;
    2203     }
    2204
    2205     /***
    2206      * @param bodyAsBytes The bodyAsBytes to set.
    2207      */
    2208     public void setBodyAsBytes(byte[] bodyAsBytes) {
    2209         this.bodyAsBytes = bodyAsBytes;
    2210     }
    2211
    2212     /***
    2213      * write map properties to an output stream
    2214      *
    2215      * @param table
    2216      * @param dataOut
    2217      * @throws IOException
    2218      */
    2219
    2220     protected void writeMapProperties(Hashtable table, DataOutput dataOut) throws IOException {
    2221         if (table != null) {
    2222             dataOut.writeShort(table.size());
    2223             for (Enumeration iter = table.keys(); iter.hasMoreElements();) {
    2224                 String key = iter.nextElement().toString();
    2225                 dataOut.writeUTF(key);
    2226                 Object value = table.get(key);
    2227
    2228                 if (value instanceof byte[]) {
    2229                     byte[] data = (byte[]) value;
    2230                     dataOut.write(ActiveMQMessage.BYTES);
    2231                     if (data != null) {
    2232                         dataOut.writeInt(data.length);
    2233                         dataOut.write(data);
    2234                     }
    2235                     else {
    2236                         dataOut.writeInt(-1);
    2237                     }
    2238                 }
    2239                 else if (value instanceof Byte) {
    2240                     dataOut.write(ActiveMQMessage.BYTE);
    2241                     Byte v = (Byte) value;
    2242                     dataOut.writeByte(v.byteValue());
    2243                 }
    2244                 else if (value instanceof Boolean) {
    2245                     dataOut.write(ActiveMQMessage.BOOLEAN);
    2246                     Boolean v = (Boolean) value;
    2247                     dataOut.writeBoolean(v.booleanValue());
    2248                 }
    2249                 else if (value instanceof String) {
    2250                     dataOut.write(ActiveMQMessage.STRING);
    2251                     dataOut.writeUTF(value.toString());
    2252                 }
    2253                 else if (value instanceof Character) {
    2254                     dataOut.write(ActiveMQMessage.CHAR);
    2255                     Character v = (Character) value;
    2256                     dataOut.writeChar(v.charValue());
    2257                 }
    2258                 else if (value instanceof Number) {
    2259                     Number v = (Number) value;
    2260
    2261                     if (value instanceof Long) {
    2262                         dataOut.write(ActiveMQMessage.LONG);
    2263                         dataOut.writeLong(v.longValue());
    2264                     }
    2265                     else if (value instanceof Integer) {
    2266                         dataOut.write(ActiveMQMessage.INT);
    2267                         dataOut.writeInt(v.intValue());
    2268                     }
    2269                     else if (value instanceof Short) {
    2270                         dataOut.write(ActiveMQMessage.SHORT);
    2271                         dataOut.writeShort(v.shortValue());
    2272                     }
    2273                     else if (value instanceof Float) {
    2274                         dataOut.write(ActiveMQMessage.FLOAT);
    2275                         dataOut.writeFloat(v.floatValue());
    2276                     }
    2277                     else if (value instanceof Double) {
    2278                         dataOut.write(ActiveMQMessage.DOUBLE);
    2279                         dataOut.writeDouble(v.doubleValue());
    2280                     }
    2281                 }
    2282                 else {
    2283                     throw new RuntimeException("Do not know how to parse value of type: " + value.getClass());
    2284                 }
    2285
    2286             }
    2287         }
    2288         else {
    2289             dataOut.writeShort(-1);
    2290         }
    2291     }
    2292
    2293     /***
    2294      * @param dataIn
    2295      * @return
    2296      * @throws IOException
    2297      */
    2298     protected Hashtable readMapProperties(DataInput dataIn) throws IOException {
    2299         Hashtable result = null;
    2300         int size = dataIn.readShort();
    2301         if (size > -1) {
    2302             result = new Hashtable();
    2303             for (int i = 0; i < size; i++) {
    2304                 String key = dataIn.readUTF();
    2305                 Object value = null;
    2306                 int type = dataIn.readByte();
    2307                 if (type == ActiveMQMessage.BYTES) {
    2308                     byte[] data = null;
    2309                     int dataSize = dataIn.readInt();
    2310                     if (dataSize > -1) {
    2311                         data = new byte[dataSize];
    2312                         dataIn.readFully(data);
    2313                     }
    2314                     value = data;
    2315                 }
    2316                 else if (type == ActiveMQMessage.BYTE) {
    2317                     value = new Byte(dataIn.readByte());
    2318                 }
    2319                 else if (type == ActiveMQMessage.BOOLEAN) {
    2320                     value = (dataIn.readBoolean()) ? Boolean.TRUE : Boolean.FALSE;
    2321                 }
    2322                 else if (type == ActiveMQMessage.STRING) {
    2323                     value = dataIn.readUTF();
    2324                 }
    2325                 else if (type == ActiveMQMessage.CHAR) {
    2326                     value = new Character(dataIn.readChar());
    2327                 }
    2328                 else if (type == ActiveMQMessage.LONG) {
    2329                     value = new Long(dataIn.readLong());
    2330                 }
    2331                 else if (type == ActiveMQMessage.INT) {
    2332                     value = new Integer(dataIn.readInt());
    2333                 }
    2334                 else if (type == ActiveMQMessage.SHORT) {
    2335                     value = new Short(dataIn.readShort());
    2336                 }
    2337                 else if (type == ActiveMQMessage.FLOAT) {
    2338                     value = new Float(dataIn.readFloat());
    2339                 }
    2340                 else if (type == ActiveMQMessage.DOUBLE) {
    2341                     value = new Double(dataIn.readDouble());
    2342                 }
    2343                 else {
    2344                     throw new RuntimeException("Do not know how to parse type: " + type);
    2345                 }
    2346                 result.put(key, value);
    2347             }
    2348         }
    2349         return result;
    2350     }
    2351
    2352     /***
    2353      * @return Returns the xaTransacted.
    2354      */
    2355     public boolean isXaTransacted() {
    2356         return xaTransacted;
    2357     }
    2358
    2359     /***
    2360      * @param xaTransacted The xaTransacted to set.
    2361      */
    2362     public void setXaTransacted(boolean xaTransacted) {
    2363         this.xaTransacted = xaTransacted;
    2364     }
    2365
    2366     public ActiveMQDestination getJMSActiveMQDestination() {
    2367         return jmsDestination;
    2368     }
    2369
    2370     /***
    2371      * @return the message identity, which contains the String messageID
    2372      *         and the lazily populated sequence number
    2373      */
    2374     public MessageIdentity getJMSMessageIdentity() {
    2375         if (messageIdentity == null) {
    2376             messageIdentity = new MessageIdentity(jmsMessageID);
    2377         }
    2378         return messageIdentity;
    2379     }
    2380 }