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 }