001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.mq;
023:
024: import java.io.ByteArrayInputStream;
025: import java.io.ByteArrayOutputStream;
026: import java.io.DataInputStream;
027: import java.io.DataOutputStream;
028: import java.io.EOFException;
029: import java.io.IOException;
030: import java.io.Externalizable;
031: import java.io.ObjectInput;
032: import java.io.ObjectOutput;
033: import java.security.PrivilegedAction;
034: import java.security.AccessController;
035: import java.util.ArrayList;
036:
037: import javax.jms.BytesMessage;
038: import javax.jms.JMSException;
039: import javax.jms.MessageEOFException;
040: import javax.jms.MessageFormatException;
041: import javax.jms.MessageNotReadableException;
042: import javax.jms.MessageNotWriteableException;
043:
044: /**
045: * This class implements javax.jms.BytesMessage
046: *
047: * @author Norbert Lataille (Norbert.Lataille@m4x.org)
048: * @author <a href="mailto:adrian@jboss.org">Adrian Brock</a>
049: * @version $Revision: 57198 $
050: */
051: public class SpyBytesMessage extends SpyMessage implements Cloneable,
052: BytesMessage, Externalizable {
053: /** The org.jboss.mq.useWriteUTF boolean system property defines whether the
054: * writeObject(String) call encodes the string using the pre-4.0.3 format
055: * of a series of writeChar calls(=false), or as a writeUTF call(=true).
056: * This defaults to true.
057: */
058: private final static String USE_WRITE_UTF = "org.jboss.mq.useWriteUTF";
059:
060: private static boolean useWriteUTF = true;
061:
062: /**
063: * The org.jboss.mq.chunkUTF boolean system property defines whether
064: * UTF strings greater than 64K are chunked.
065: * The default is true.
066: */
067: private final static String CHUNK_UTF = "org.jboss.mq.chunkUTF";
068:
069: private static boolean chunkUTF = true;
070:
071: /** The chunkSize */
072: private final static int chunkSize = 16384;
073:
074: /** The serialVersionUID */
075: private final static long serialVersionUID = -6572727147964701014L;
076:
077: static {
078: PrivilegedAction action = new PrivilegedAction() {
079: public Object run() {
080: return System.getProperty(USE_WRITE_UTF, "true");
081: }
082: };
083: try {
084: String flag = (String) AccessController
085: .doPrivileged(action);
086: useWriteUTF = Boolean.valueOf(flag).booleanValue();
087: } catch (Throwable ignore) {
088: }
089: action = new PrivilegedAction() {
090: public Object run() {
091: return System.getProperty(CHUNK_UTF, "true");
092: }
093: };
094: try {
095: String flag = (String) AccessController
096: .doPrivileged(action);
097: chunkUTF = Boolean.valueOf(flag).booleanValue();
098: } catch (Throwable ignore) {
099: }
100: }
101:
102: /** The internal representation */
103: byte[] InternalArray = null;
104:
105: private transient ByteArrayOutputStream ostream = null;
106: private transient DataOutputStream p = null;
107: private transient ByteArrayInputStream istream = null;
108: private transient DataInputStream m = null;
109:
110: /**
111: * Create a new SpyBytesMessage
112: */
113: public SpyBytesMessage() {
114: header.msgReadOnly = false;
115: ostream = new ByteArrayOutputStream();
116: p = new DataOutputStream(ostream);
117: }
118:
119: public boolean readBoolean() throws JMSException {
120: checkRead();
121: try {
122: return m.readBoolean();
123: } catch (EOFException e) {
124: throw new MessageEOFException("");
125: } catch (IOException e) {
126: throw new JMSException("IOException");
127: }
128: }
129:
130: public byte readByte() throws JMSException {
131: checkRead();
132: try {
133: return m.readByte();
134: } catch (EOFException e) {
135: throw new MessageEOFException("");
136: } catch (IOException e) {
137: throw new JMSException("IOException");
138: }
139: }
140:
141: public int readUnsignedByte() throws JMSException {
142: checkRead();
143: try {
144: return m.readUnsignedByte();
145: } catch (EOFException e) {
146: throw new MessageEOFException("");
147: } catch (IOException e) {
148: throw new JMSException("IOException");
149: }
150: }
151:
152: public short readShort() throws JMSException {
153: checkRead();
154: try {
155: return m.readShort();
156: } catch (EOFException e) {
157: throw new MessageEOFException("");
158: } catch (IOException e) {
159: throw new JMSException("IOException");
160: }
161: }
162:
163: public int readUnsignedShort() throws JMSException {
164: checkRead();
165: try {
166: return m.readUnsignedShort();
167: } catch (EOFException e) {
168: throw new MessageEOFException("");
169: } catch (IOException e) {
170: throw new JMSException("IOException");
171: }
172: }
173:
174: public char readChar() throws JMSException {
175: checkRead();
176: try {
177: return m.readChar();
178: } catch (EOFException e) {
179: throw new MessageEOFException("");
180: } catch (IOException e) {
181: throw new JMSException("IOException");
182: }
183: }
184:
185: public int readInt() throws JMSException {
186: checkRead();
187: try {
188: return m.readInt();
189: } catch (EOFException e) {
190: throw new MessageEOFException("");
191: } catch (IOException e) {
192: throw new JMSException("IOException");
193: }
194: }
195:
196: public long readLong() throws JMSException {
197: checkRead();
198: try {
199: return m.readLong();
200: } catch (EOFException e) {
201: throw new MessageEOFException("");
202: } catch (IOException e) {
203: throw new JMSException("IOException");
204: }
205: }
206:
207: public float readFloat() throws JMSException {
208: checkRead();
209: try {
210: return m.readFloat();
211: } catch (EOFException e) {
212: throw new MessageEOFException("");
213: } catch (IOException e) {
214: throw new JMSException("IOException");
215: }
216: }
217:
218: public double readDouble() throws JMSException {
219: checkRead();
220: try {
221: return m.readDouble();
222: } catch (EOFException e) {
223: throw new MessageEOFException("");
224: } catch (IOException e) {
225: throw new JMSException("IOException");
226: }
227: }
228:
229: public String readUTF() throws JMSException {
230: checkRead();
231: try {
232: if (chunkUTF == false)
233: return m.readUTF();
234:
235: byte type = m.readByte();
236: if (type == NULL)
237: return null;
238:
239: // apply workaround for string > 64K bug in jdk's 1.3.*
240:
241: // Read the no. of chunks this message is split into, allocate
242: // a StringBuffer that can hold all chunks, read the chunks
243: // into the buffer and set 'content' accordingly
244: int chunksToRead = m.readInt();
245: int bufferSize = chunkSize * chunksToRead;
246:
247: // special handling for single chunk
248: if (chunksToRead == 1) {
249: // The text size is likely to be much smaller than the chunkSize
250: // so set bufferSize to the min of the input stream available
251: // and the maximum buffer size. Since the input stream
252: // available() can be <= 0 we check for that and default to
253: // a small msg size of 256 bytes.
254:
255: int inSize = m.available();
256: if (inSize <= 0) {
257: inSize = 256;
258: }
259:
260: bufferSize = Math.min(inSize, bufferSize);
261: }
262:
263: // read off all of the chunks
264: StringBuffer sb = new StringBuffer(bufferSize);
265:
266: for (int i = 0; i < chunksToRead; i++) {
267: sb.append(m.readUTF());
268: }
269:
270: return sb.toString();
271: } catch (EOFException e) {
272: throw new MessageEOFException("");
273: } catch (IOException e) {
274: throw new JMSException("IOException");
275: }
276: }
277:
278: public int readBytes(byte[] value) throws JMSException {
279: checkRead();
280: try {
281: return m.read(value);
282: } catch (IOException e) {
283: throw new JMSException("IOException");
284: }
285: }
286:
287: public int readBytes(byte[] value, int length) throws JMSException {
288: checkRead();
289: try {
290: return m.read(value, 0, length);
291: } catch (IOException e) {
292: throw new JMSException("IOException");
293: }
294: }
295:
296: public void writeBoolean(boolean value) throws JMSException {
297: if (header.msgReadOnly) {
298: throw new MessageNotWriteableException(
299: "the message body is read-only");
300: }
301: try {
302: p.writeBoolean(value);
303: } catch (IOException e) {
304: throw new JMSException("IOException");
305: }
306: }
307:
308: public void writeByte(byte value) throws JMSException {
309: if (header.msgReadOnly) {
310: throw new MessageNotWriteableException(
311: "the message body is read-only");
312: }
313: try {
314: p.writeByte(value);
315: } catch (IOException e) {
316: throw new JMSException("IOException");
317: }
318: }
319:
320: public void writeShort(short value) throws JMSException {
321: if (header.msgReadOnly) {
322: throw new MessageNotWriteableException(
323: "the message body is read-only");
324: }
325: try {
326: p.writeShort(value);
327: } catch (IOException e) {
328: throw new JMSException("IOException");
329: }
330: }
331:
332: public void writeChar(char value) throws JMSException {
333: if (header.msgReadOnly) {
334: throw new MessageNotWriteableException(
335: "the message body is read-only");
336: }
337: try {
338: p.writeChar(value);
339: } catch (IOException e) {
340: throw new JMSException("IOException");
341: }
342: }
343:
344: public void writeInt(int value) throws JMSException {
345: if (header.msgReadOnly) {
346: throw new MessageNotWriteableException(
347: "the message body is read-only");
348: }
349: try {
350: p.writeInt(value);
351: } catch (IOException e) {
352: throw new JMSException("IOException");
353: }
354: }
355:
356: public void writeLong(long value) throws JMSException {
357: if (header.msgReadOnly) {
358: throw new MessageNotWriteableException(
359: "the message body is read-only");
360: }
361: try {
362: p.writeLong(value);
363: } catch (IOException e) {
364: throw new JMSException("IOException");
365: }
366: }
367:
368: public void writeFloat(float value) throws JMSException {
369: if (header.msgReadOnly) {
370: throw new MessageNotWriteableException(
371: "the message body is read-only");
372: }
373: try {
374: p.writeFloat(value);
375: } catch (IOException e) {
376: throw new JMSException("IOException");
377: }
378: }
379:
380: public void writeDouble(double value) throws JMSException {
381: if (header.msgReadOnly) {
382: throw new MessageNotWriteableException(
383: "the message body is read-only");
384: }
385: try {
386: p.writeDouble(value);
387: } catch (IOException e) {
388: throw new JMSException("IOException");
389: }
390: }
391:
392: public void writeUTF(String value) throws JMSException {
393: if (header.msgReadOnly) {
394: throw new MessageNotWriteableException(
395: "the message body is read-only");
396: }
397: try {
398: if (chunkUTF == false)
399: p.writeUTF(value);
400: else {
401: if (value == null)
402: p.writeByte(NULL);
403: else {
404: // apply workaround for string > 64K bug in jdk's 1.3.*
405:
406: // Split content into chunks of size 'chunkSize' and assemble
407: // the pieces into a List ...
408:
409: // FIXME: could calculate the number of chunks first, then
410: // write as we chunk for efficiency
411:
412: ArrayList v = new ArrayList();
413: int contentLength = value.length();
414:
415: while (contentLength > 0) {
416: int beginCopy = (v.size()) * chunkSize;
417: int endCopy = contentLength <= chunkSize ? beginCopy
418: + contentLength
419: : beginCopy + chunkSize;
420:
421: String theChunk = value.substring(beginCopy,
422: endCopy);
423: v.add(theChunk);
424:
425: contentLength -= chunkSize;
426: }
427:
428: // Write out the type (OBJECT), the no. of chunks and finally
429: // all chunks that have been assembled previously
430: p.writeByte(OBJECT);
431: p.writeInt(v.size());
432:
433: for (int i = 0; i < v.size(); i++) {
434: p.writeUTF((String) v.get(i));
435: }
436: }
437: }
438: } catch (IOException e) {
439: throw new JMSException("IOException");
440: }
441: }
442:
443: public void writeBytes(byte[] value) throws JMSException {
444: if (header.msgReadOnly) {
445: throw new MessageNotWriteableException(
446: "the message body is read-only");
447: }
448: try {
449: p.write(value, 0, value.length);
450: } catch (IOException e) {
451: throw new JMSException("IOException");
452: }
453: }
454:
455: public void writeBytes(byte[] value, int offset, int length)
456: throws JMSException {
457: if (header.msgReadOnly) {
458: throw new MessageNotWriteableException(
459: "the message body is read-only");
460: }
461: try {
462: p.write(value, offset, length);
463: } catch (IOException e) {
464: throw new JMSException("IOException");
465: }
466: }
467:
468: public void writeObject(Object value) throws JMSException {
469: if (header.msgReadOnly) {
470: throw new MessageNotWriteableException(
471: "the message body is read-only");
472: }
473: try {
474: if (value == null) {
475: throw new NullPointerException(
476: "Attempt to write a new value");
477: }
478: if (value instanceof String) {
479: String s = (String) value;
480: if (useWriteUTF == true)
481: writeUTF(s);
482: else
483: p.writeChars(s);
484: } else if (value instanceof Boolean) {
485: p.writeBoolean(((Boolean) value).booleanValue());
486: } else if (value instanceof Byte) {
487: p.writeByte(((Byte) value).byteValue());
488: } else if (value instanceof Short) {
489: p.writeShort(((Short) value).shortValue());
490: } else if (value instanceof Integer) {
491: p.writeInt(((Integer) value).intValue());
492: } else if (value instanceof Long) {
493: p.writeLong(((Long) value).longValue());
494: } else if (value instanceof Float) {
495: p.writeFloat(((Float) value).floatValue());
496: } else if (value instanceof Double) {
497: p.writeDouble(((Double) value).doubleValue());
498: } else if (value instanceof byte[]) {
499: p.write((byte[]) value, 0, ((byte[]) value).length);
500: } else {
501: throw new MessageFormatException(
502: "Invalid object for properties");
503: }
504: } catch (IOException e) {
505: throw new JMSException("IOException");
506: }
507:
508: }
509:
510: public void reset() throws JMSException {
511: try {
512: if (!header.msgReadOnly) {
513: p.flush();
514: InternalArray = ostream.toByteArray();
515: ostream.close();
516: }
517: ostream = null;
518: istream = null;
519: m = null;
520: p = null;
521: header.msgReadOnly = true;
522: } catch (IOException e) {
523: throw new JMSException("IOException");
524: }
525: }
526:
527: public void clearBody() throws JMSException {
528: try {
529: if (!header.msgReadOnly) {
530: ostream.close();
531: } else {
532: // REVIEW: istream is only initialised on a read.
533: // It looks like it is possible to acknowledge
534: // a message without reading it? Guard against
535: // an NPE in this case.
536: if (istream != null)
537: istream.close();
538: }
539: } catch (IOException e) {
540: //don't throw an exception
541: }
542:
543: ostream = new ByteArrayOutputStream();
544: p = new DataOutputStream(ostream);
545: InternalArray = null;
546: istream = null;
547: m = null;
548:
549: super .clearBody();
550: }
551:
552: public SpyMessage myClone() throws JMSException {
553: SpyBytesMessage result = MessagePool.getBytesMessage();
554: this .reset();
555: result.copyProps(this );
556: if (this .InternalArray != null) {
557: result.InternalArray = new byte[this .InternalArray.length];
558: System.arraycopy(this .InternalArray, 0,
559: result.InternalArray, 0, this .InternalArray.length);
560: }
561: return result;
562: }
563:
564: public long getBodyLength() throws JMSException {
565: checkRead();
566: return InternalArray.length;
567: }
568:
569: public void writeExternal(ObjectOutput out) throws IOException {
570: byte[] arrayToSend = null;
571: if (!header.msgReadOnly) {
572: p.flush();
573: arrayToSend = ostream.toByteArray();
574: } else {
575: arrayToSend = InternalArray;
576: }
577: super .writeExternal(out);
578: if (arrayToSend == null) {
579: out.writeInt(0); //pretend to be empty array
580: } else {
581: out.writeInt(arrayToSend.length);
582: out.write(arrayToSend);
583: }
584: }
585:
586: public void readExternal(ObjectInput in) throws IOException,
587: ClassNotFoundException {
588: super .readExternal(in);
589: int length = in.readInt();
590: if (length < 0) {
591: InternalArray = null;
592: } else {
593: InternalArray = new byte[length];
594: in.readFully(InternalArray);
595: }
596: }
597:
598: /**
599: * Check the message is readable
600: *
601: * @throws JMSException when not readable
602: */
603: private void checkRead() throws JMSException {
604: if (!header.msgReadOnly) {
605: throw new MessageNotReadableException(
606: "readByte while the buffer is writeonly");
607: }
608:
609: //We have just received/reset() the message, and the client is trying to
610: // read it
611: if (istream == null || m == null) {
612: istream = new ByteArrayInputStream(InternalArray);
613: m = new DataInputStream(istream);
614: }
615: }
616: }
|