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 package org.activemq.message; 019 020 import javax.jms.JMSException; 021 import javax.jms.MessageNotWriteableException; 022 import javax.jms.TextMessage; 023 import java.io.DataInput; 024 import java.io.DataOutput; 025 import java.io.IOException; 026 import java.io.UTFDataFormatException; 027 028 /** 029 * A <CODE>TextMessage</CODE> object is used to send a message containing a 030 * <CODE>java.lang.String</CODE>. 031 * It inherits from the <CODE>Message</CODE> interface and adds a text message 032 * body. 033 * <p/> 034 * <P>This message type can be used to transport text-based messages, including 035 * those with XML content. 036 * <p/> 037 * <P>When a client receives a <CODE>TextMessage</CODE>, it is in read-only 038 * mode. If a client attempts to write to the message at this point, a 039 * <CODE>MessageNotWriteableException</CODE> is thrown. If 040 * <CODE>clearBody</CODE> is 041 * called, the message can now be both read from and written to. 042 * 043 * @see javax.jms.Session#createTextMessage() 044 * @see javax.jms.Session#createTextMessage(String) 045 * @see javax.jms.BytesMessage 046 * @see javax.jms.MapMessage 047 * @see javax.jms.Message 048 * @see javax.jms.ObjectMessage 049 * @see javax.jms.StreamMessage 050 * @see java.lang.String 051 */ 052 053 public class ActiveMQTextMessage extends ActiveMQMessage implements TextMessage { 054 private String text; 055 056 057 public String toString() { 058 String payload = null; 059 try { 060 if(!isMessagePart()){ 061 payload = getText(); 062 }else{ 063 payload = "fragmented message"; 064 } 065 } 066 catch (JMSException e) { 067 payload = "could not read payload: " + e.toString(); 068 } 069 return super.toString() + ", text = " + payload; 070 } 071 072 /** 073 * Return the type of Packet 074 * 075 * @return integer representation of the type of Packet 076 */ 077 078 public int getPacketType() { 079 return ACTIVEMQ_TEXT_MESSAGE; 080 } 081 082 /** 083 * @return Returns a shallow copy of the message instance 084 * @throws JMSException 085 */ 086 087 public ActiveMQMessage shallowCopy() throws JMSException { 088 ActiveMQTextMessage other = new ActiveMQTextMessage(); 089 this.initializeOther(other); 090 other.text = this.text; 091 return other; 092 } 093 094 /** 095 * @return Returns a deep copy of the message - note the header fields are only shallow copied 096 * @throws JMSException 097 */ 098 099 public ActiveMQMessage deepCopy() throws JMSException { 100 return shallowCopy(); 101 } 102 103 /** 104 * Clears out the message body. Clearing a message's body does not clear 105 * its header values or property entries. 106 * <p/> 107 * <P>If this message body was read-only, calling this method leaves 108 * the message body in the same state as an empty body in a newly 109 * created message. 110 * 111 * @throws JMSException if the JMS provider fails to clear the message 112 * body due to some internal error. 113 */ 114 115 public void clearBody() throws JMSException { 116 super.clearBody(); 117 this.text = null; 118 } 119 120 /** 121 * Sets the string containing this message's data. 122 * 123 * @param string the <CODE>String</CODE> containing the message's data 124 * @throws JMSException if the JMS provider fails to set the text due to 125 * some internal error. 126 * @throws MessageNotWriteableException if the message is in read-only 127 * mode. 128 */ 129 130 public void setText(String string) throws JMSException { 131 if (super.readOnlyMessage) { 132 throw new MessageNotWriteableException("The message is read only"); 133 } 134 // lets flush the byte memory if available 135 clearBody(); 136 this.text = string; 137 } 138 139 140 /** 141 * Gets the string containing this message's data. The default 142 * value is null. 143 * 144 * @return the <CODE>String</CODE> containing the message's data 145 * @throws JMSException 146 */ 147 148 public String getText() throws JMSException { 149 if (this.text == null) { 150 try { 151 super.buildBodyFromBytes(); 152 } 153 catch (IOException ioe) { 154 JMSException jmsEx = new JMSException("failed to build body from bytes"); 155 jmsEx.setLinkedException(ioe); 156 throw jmsEx; 157 } 158 } 159 return this.text; 160 } 161 162 /** 163 * Used serialize the message body to an output stream 164 * 165 * @param dataOut 166 * @throws IOException 167 */ 168 169 public void writeBody(DataOutput dataOut) throws IOException { 170 if (text != null) { 171 int strlen = text.length(); 172 int utflen = 0; 173 char[] charr = new char[strlen]; 174 int c, count = 0; 175 176 text.getChars(0, strlen, charr, 0); 177 178 for (int i = 0; i < strlen; i++) { 179 c = charr[i]; 180 if ((c >= 0x0001) && (c <= 0x007F)) { 181 utflen++; 182 } 183 else if (c > 0x07FF) { 184 utflen += 3; 185 } 186 else { 187 utflen += 2; 188 } 189 } 190 191 byte[] bytearr = new byte[utflen + 4]; 192 bytearr[count++] = (byte) ((utflen >>> 24) & 0xFF); 193 bytearr[count++] = (byte) ((utflen >>> 16) & 0xFF); 194 bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF); 195 bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF); 196 for (int i = 0; i < strlen; i++) { 197 c = charr[i]; 198 if ((c >= 0x0001) && (c <= 0x007F)) { 199 bytearr[count++] = (byte) c; 200 } 201 else if (c > 0x07FF) { 202 bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); 203 bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F)); 204 bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); 205 } 206 else { 207 bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); 208 bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); 209 } 210 } 211 dataOut.write(bytearr); 212 213 } 214 else { 215 dataOut.writeInt(-1); 216 } 217 } 218 219 /** 220 * Used to help build the body from an input stream 221 * 222 * @param dataIn 223 * @throws IOException 224 */ 225 226 public void readBody(DataInput dataIn) throws IOException { 227 int utflen = dataIn.readInt(); 228 if (utflen > -1) { 229 StringBuffer str = new StringBuffer(utflen); 230 byte bytearr[] = new byte[utflen]; 231 int c, char2, char3; 232 int count = 0; 233 234 dataIn.readFully(bytearr, 0, utflen); 235 236 while (count < utflen) { 237 c = bytearr[count] & 0xff; 238 switch (c >> 4) { 239 case 0: 240 case 1: 241 case 2: 242 case 3: 243 case 4: 244 case 5: 245 case 6: 246 case 7: 247 /* 0xxxxxxx */ 248 count++; 249 str.append((char) c); 250 break; 251 case 12: 252 case 13: 253 /* 110x xxxx 10xx xxxx */ 254 count += 2; 255 if (count > utflen) { 256 throw new UTFDataFormatException(); 257 } 258 char2 = bytearr[count - 1]; 259 if ((char2 & 0xC0) != 0x80) { 260 throw new UTFDataFormatException(); 261 } 262 str.append((char) (((c & 0x1F) << 6) | (char2 & 0x3F))); 263 break; 264 case 14: 265 /* 1110 xxxx 10xx xxxx 10xx xxxx */ 266 count += 3; 267 if (count > utflen) { 268 throw new UTFDataFormatException(); 269 } 270 char2 = bytearr[count - 2]; 271 char3 = bytearr[count - 1]; 272 if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) { 273 throw new UTFDataFormatException(); 274 } 275 str.append((char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0))); 276 break; 277 default : 278 /* 10xx xxxx, 1111 xxxx */ 279 throw new UTFDataFormatException(); 280 } 281 } 282 // The number of chars produced may be less than utflen 283 this.text = new String(str); 284 } 285 } 286 287 /** 288 * dumps the text body as UTF-8 289 * @param dataOut 290 * @throws IOException 291 */ 292 public void writeText(DataOutput dataOut) throws IOException { 293 String theText = text != null ? text : ""; 294 dataOut.writeUTF(theText); 295 } 296 297 /** 298 * read the text as UTF-8 299 * @param dataIn 300 * @throws IOException 301 */ 302 public void readText(DataInput dataIn) throws IOException{ 303 this.text = dataIn.readUTF(); 304 } 305 }