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 this.text = string;
131 }
132
133 void setTextPayload(String string) {
134 this.text = string;
135 }
136
137
138 /***
139 * Gets the string containing this message's data. The default
140 * value is null.
141 *
142 * @return the <CODE>String</CODE> containing the message's data
143 * @throws JMSException
144 */
145
146 public String getText() throws JMSException {
147 if (this.text == null) {
148 try {
149 super.buildBodyFromBytes();
150 }
151 catch (IOException ioe) {
152 JMSException jmsEx = new JMSException("failed to build body from bytes");
153 jmsEx.setLinkedException(ioe);
154 throw jmsEx;
155 }
156 }
157 return this.text;
158 }
159
160 /***
161 * Used serialize the message body to an output stream
162 *
163 * @param dataOut
164 * @throws IOException
165 */
166
167 protected void writeBody(DataOutput dataOut) throws IOException {
168 if (text != null) {
169 int strlen = text.length();
170 int utflen = 0;
171 char[] charr = new char[strlen];
172 int c, count = 0;
173
174 text.getChars(0, strlen, charr, 0);
175
176 for (int i = 0; i < strlen; i++) {
177 c = charr[i];
178 if ((c >= 0x0001) && (c <= 0x007F)) {
179 utflen++;
180 }
181 else if (c > 0x07FF) {
182 utflen += 3;
183 }
184 else {
185 utflen += 2;
186 }
187 }
188
189 byte[] bytearr = new byte[utflen + 4];
190 bytearr[count++] = (byte) ((utflen >>> 24) & 0xFF);
191 bytearr[count++] = (byte) ((utflen >>> 16) & 0xFF);
192 bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
193 bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
194 for (int i = 0; i < strlen; i++) {
195 c = charr[i];
196 if ((c >= 0x0001) && (c <= 0x007F)) {
197 bytearr[count++] = (byte) c;
198 }
199 else if (c > 0x07FF) {
200 bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
201 bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
202 bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
203 }
204 else {
205 bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
206 bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
207 }
208 }
209 dataOut.write(bytearr);
210
211 }
212 else {
213 dataOut.writeInt(-1);
214 }
215 }
216
217 /***
218 * Used to help build the body from an input stream
219 *
220 * @param dataIn
221 * @throws IOException
222 */
223
224 protected void readBody(DataInput dataIn) throws IOException {
225 int utflen = dataIn.readInt();
226 if (utflen > -1) {
227 StringBuffer str = new StringBuffer(utflen);
228 byte bytearr[] = new byte[utflen];
229 int c, char2, char3;
230 int count = 0;
231
232 dataIn.readFully(bytearr, 0, utflen);
233
234 while (count < utflen) {
235 c = bytearr[count] & 0xff;
236 switch (c >> 4) {
237 case 0:
238 case 1:
239 case 2:
240 case 3:
241 case 4:
242 case 5:
243 case 6:
244 case 7:
245
246 count++;
247 str.append((char) c);
248 break;
249 case 12:
250 case 13:
251
252 count += 2;
253 if (count > utflen) {
254 throw new UTFDataFormatException();
255 }
256 char2 = bytearr[count - 1];
257 if ((char2 & 0xC0) != 0x80) {
258 throw new UTFDataFormatException();
259 }
260 str.append((char) (((c & 0x1F) << 6) | (char2 & 0x3F)));
261 break;
262 case 14:
263
264 count += 3;
265 if (count > utflen) {
266 throw new UTFDataFormatException();
267 }
268 char2 = bytearr[count - 2];
269 char3 = bytearr[count - 1];
270 if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) {
271 throw new UTFDataFormatException();
272 }
273 str.append((char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)));
274 break;
275 default :
276
277 throw new UTFDataFormatException();
278 }
279 }
280
281 this.text = new String(str);
282 }
283 }
284 }