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