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