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