1 /***
2 *
3 * Copyright 2004 Protique Ltd
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 **/
18 package org.codehaus.activemq.message;
19
20 import javax.jms.JMSException;
21 import javax.jms.MessageNotWriteableException;
22 import javax.jms.TextMessage;
23 import java.io.DataInput;
24 import java.io.DataOutput;
25 import java.io.IOException;
26 import java.io.UTFDataFormatException;
27
28 /***
29 * A <CODE>TextMessage</CODE> object is used to send a message containing a
30 * <CODE>java.lang.String</CODE>.
31 * It inherits from the <CODE>Message</CODE> interface and adds a text message
32 * body.
33 * <p/>
34 * <P>This message type can be used to transport text-based messages, including
35 * those with XML content.
36 * <p/>
37 * <P>When a client receives a <CODE>TextMessage</CODE>, it is in read-only
38 * mode. If a client attempts to write to the message at this point, a
39 * <CODE>MessageNotWriteableException</CODE> is thrown. If
40 * <CODE>clearBody</CODE> is
41 * called, the message can now be both read from and written to.
42 *
43 * @see javax.jms.Session#createTextMessage()
44 * @see javax.jms.Session#createTextMessage(String)
45 * @see javax.jms.BytesMessage
46 * @see javax.jms.MapMessage
47 * @see javax.jms.Message
48 * @see javax.jms.ObjectMessage
49 * @see javax.jms.StreamMessage
50 * @see java.lang.String
51 */
52
53 public class ActiveMQTextMessage extends ActiveMQMessage implements TextMessage {
54 private String text;
55
56
57 public String toString() {
58 String payload = null;
59 try {
60 payload = getText();
61 }
62 catch (JMSException e) {
63 payload = "could not read payload: " + e.toString();
64 }
65 return super.toString() + ", text = " + payload;
66 }
67
68 /***
69 * Return the type of Packet
70 *
71 * @return integer representation of the type of Packet
72 */
73
74 public int getPacketType() {
75 return ACTIVEMQ_TEXT_MESSAGE;
76 }
77
78 /***
79 * @return Returns a shallow copy of the message instance
80 * @throws JMSException
81 */
82
83 public ActiveMQMessage shallowCopy() throws JMSException {
84 ActiveMQTextMessage other = new ActiveMQTextMessage();
85 this.initializeOther(other);
86 other.text = this.text;
87 return other;
88 }
89
90 /***
91 * @return Returns a deep copy of the message - note the header fields are only shallow copied
92 * @throws JMSException
93 */
94
95 public ActiveMQMessage deepCopy() throws JMSException {
96 return shallowCopy();
97 }
98
99 /***
100 * Clears out the message body. Clearing a message's body does not clear
101 * its header values or property entries.
102 * <p/>
103 * <P>If this message body was read-only, calling this method leaves
104 * the message body in the same state as an empty body in a newly
105 * created message.
106 *
107 * @throws JMSException if the JMS provider fails to clear the message
108 * body due to some internal error.
109 */
110
111 public void clearBody() throws JMSException {
112 super.clearBody();
113 this.text = null;
114 }
115
116 /***
117 * Sets the string containing this message's data.
118 *
119 * @param string the <CODE>String</CODE> containing the message's data
120 * @throws JMSException if the JMS provider fails to set the text due to
121 * some internal error.
122 * @throws MessageNotWriteableException if the message is in read-only
123 * mode.
124 */
125
126 public void setText(String string) throws JMSException {
127 if (super.readOnlyMessage) {
128 throw new MessageNotWriteableException("The message is read only");
129 }
130
131 clearBody();
132 this.text = string;
133 }
134
135
136 /***
137 * Gets the string containing this message's data. The default
138 * value is null.
139 *
140 * @return the <CODE>String</CODE> containing the message's data
141 * @throws JMSException
142 */
143
144 public String getText() throws JMSException {
145 if (this.text == null) {
146 try {
147 super.buildBodyFromBytes();
148 }
149 catch (IOException ioe) {
150 JMSException jmsEx = new JMSException("failed to build body from bytes");
151 jmsEx.setLinkedException(ioe);
152 throw jmsEx;
153 }
154 }
155 return this.text;
156 }
157
158 /***
159 * Used serialize the message body to an output stream
160 *
161 * @param dataOut
162 * @throws IOException
163 */
164
165 protected void writeBody(DataOutput dataOut) throws IOException {
166 if (text != null) {
167 int strlen = text.length();
168 int utflen = 0;
169 char[] charr = new char[strlen];
170 int c, count = 0;
171
172 text.getChars(0, strlen, charr, 0);
173
174 for (int i = 0; i < strlen; i++) {
175 c = charr[i];
176 if ((c >= 0x0001) && (c <= 0x007F)) {
177 utflen++;
178 }
179 else if (c > 0x07FF) {
180 utflen += 3;
181 }
182 else {
183 utflen += 2;
184 }
185 }
186
187 byte[] bytearr = new byte[utflen + 4];
188 bytearr[count++] = (byte) ((utflen >>> 24) & 0xFF);
189 bytearr[count++] = (byte) ((utflen >>> 16) & 0xFF);
190 bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
191 bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
192 for (int i = 0; i < strlen; i++) {
193 c = charr[i];
194 if ((c >= 0x0001) && (c <= 0x007F)) {
195 bytearr[count++] = (byte) c;
196 }
197 else if (c > 0x07FF) {
198 bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
199 bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
200 bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
201 }
202 else {
203 bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
204 bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
205 }
206 }
207 dataOut.write(bytearr);
208
209 }
210 else {
211 dataOut.writeInt(-1);
212 }
213 }
214
215 /***
216 * Used to help build the body from an input stream
217 *
218 * @param dataIn
219 * @throws IOException
220 */
221
222 protected void readBody(DataInput dataIn) throws IOException {
223 int utflen = dataIn.readInt();
224 if (utflen > -1) {
225 StringBuffer str = new StringBuffer(utflen);
226 byte bytearr[] = new byte[utflen];
227 int c, char2, char3;
228 int count = 0;
229
230 dataIn.readFully(bytearr, 0, utflen);
231
232 while (count < utflen) {
233 c = bytearr[count] & 0xff;
234 switch (c >> 4) {
235 case 0:
236 case 1:
237 case 2:
238 case 3:
239 case 4:
240 case 5:
241 case 6:
242 case 7:
243
244 count++;
245 str.append((char) c);
246 break;
247 case 12:
248 case 13:
249
250 count += 2;
251 if (count > utflen) {
252 throw new UTFDataFormatException();
253 }
254 char2 = bytearr[count - 1];
255 if ((char2 & 0xC0) != 0x80) {
256 throw new UTFDataFormatException();
257 }
258 str.append((char) (((c & 0x1F) << 6) | (char2 & 0x3F)));
259 break;
260 case 14:
261
262 count += 3;
263 if (count > utflen) {
264 throw new UTFDataFormatException();
265 }
266 char2 = bytearr[count - 2];
267 char3 = bytearr[count - 1];
268 if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) {
269 throw new UTFDataFormatException();
270 }
271 str.append((char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)));
272 break;
273 default :
274
275 throw new UTFDataFormatException();
276 }
277 }
278
279 this.text = new String(str);
280 }
281 }
282 }