View Javadoc

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