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