com.datastax.oss.pulsar.jms.messages.PulsarStreamMessage Maven / Gradle / Ivy
/*
* Copyright DataStax, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.datastax.oss.pulsar.jms.messages;
import com.datastax.oss.pulsar.jms.PulsarMessage;
import com.datastax.oss.pulsar.jms.Utils;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.jms.JMSException;
import jakarta.jms.MessageEOFException;
import jakarta.jms.MessageFormatException;
import jakarta.jms.MessageNotReadableException;
import jakarta.jms.MessageNotWriteableException;
import jakarta.jms.StreamMessage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import org.apache.pulsar.client.api.TypedMessageBuilder;
public final class PulsarStreamMessage extends PulsarMessage implements StreamMessage {
@Override
protected String messageType() {
return "stream";
}
protected void checkType(byte type, byte expected) throws JMSException {
if (type != expected) {
throw new MessageFormatException(
"Invalid type " + typeToString(type) + ", expected " + typeToString(expected));
}
}
@Override
public T getBody(Class c) throws JMSException {
throw new MessageFormatException("getBody not available on StreamMessage");
}
protected void writeDataType(byte dataType) throws IOException {
dataOutputStream.writeByte(dataType);
}
protected void writeArrayLen(int len) throws IOException {
dataOutputStream.writeInt(len);
}
protected int readArrayLen() throws IOException {
return dataInputStream.readInt();
}
protected byte readDataType() throws IOException {
return dataInputStream.readByte();
}
/**
* Reads a byte array field from the stream message into the specified {@code byte[]} object (the
* read buffer).
*
* To read the field value, {@code readBytes} should be successively called until it returns a
* value less than the length of the read buffer. The value of the bytes in the buffer following
* the last byte read is undefined.
*
*
If {@code readBytes} returns a value equal to the length of the buffer, a subsequent {@code
* readBytes} call must be made. If there are no more bytes to be read, this call returns -1.
*
*
If the byte array field value is null, {@code readBytes} returns -1.
*
*
If the byte array field value is empty, {@code readBytes} returns 0.
*
*
Once the first {@code readBytes} call on a {@code byte[]} field value has been made, the
* full value of the field must be read before it is valid to read the next field. An attempt to
* read the next field before that has been done will throw a {@code MessageFormatException}.
*
*
To read the byte field value into a new {@code byte[]} object, use the {@code readObject}
* method.
*
* @param value the buffer into which the data is read
* @return the total number of bytes read into the buffer, or -1 if there is no more data because
* the end of the byte field has been reached
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of message stream has been reached.
* @throws MessageFormatException if this type conversion is invalid.
* @throws MessageNotReadableException if the message is in write-only mode.
* @see #readObject()
*/
public int readBytes(byte[] value) throws JMSException {
if (value == null) {
return -1;
}
if (value.length == 0) {
return 0;
}
return readBytes(value, value.length);
}
private void resetStreamAtMark() throws JMSException {
try {
dataInputStream.reset();
} catch (IOException err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Reads an object from the stream message.
*
*
This method can be used to return, in objectified format, an object in the Java programming
* language ("Java object") that has been written to the stream with the equivalent {@code
* writeObject} method call, or its equivalent primitive writetype
method.
*
*
Note that byte values are returned as {@code byte[]}, not {@code Byte[]}.
*
*
An attempt to call {@code readObject} to read a byte field value into a new {@code byte[]}
* object before the full value of the byte field has been read will throw a {@code
* MessageFormatException}.
*
* @return a Java object from the stream message, in objectified format (for example, if the
* object was written as an {@code int}, an {@code Integer} is returned)
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of message stream has been reached.
* @throws MessageFormatException if this type conversion is invalid.
* @throws MessageNotReadableException if the message is in write-only mode.
* @see #readBytes(byte[] value)
*/
@Override
public Object readObject() throws JMSException {
checkReadable();
if (remainingByteArrayLen != Integer.MIN_VALUE) {
throw new MessageFormatException("You must complete the readBytes operation");
}
try {
dataInputStream.mark(100);
byte dataType = readDataType();
switch (dataType) {
case TYPE_NULL:
return null;
case TYPE_BOOLEAN:
return dataInputStream.readBoolean();
case TYPE_DOUBLE:
return dataInputStream.readDouble();
case TYPE_FLOAT:
return dataInputStream.readFloat();
case TYPE_INT:
return dataInputStream.readInt();
case TYPE_LONG:
return dataInputStream.readLong();
case TYPE_SHORT:
return dataInputStream.readShort();
case TYPE_STRING:
return dataInputStream.readUTF();
case TYPE_BYTE:
return dataInputStream.readByte();
case TYPE_CHAR:
return dataInputStream.readChar();
case TYPE_BYTES:
int len = readArrayLen();
byte[] buffer = new byte[len];
dataInputStream.readFully(buffer);
return buffer;
default:
throw new MessageFormatException("Wrong data type: " + dataType);
}
} catch (Exception err) {
resetStreamAtMark();
throw handleExceptionAccordingToMessageSpecs(err);
}
}
protected ByteArrayOutputStream stream;
protected byte[] originalMessage;
protected DataInputStream dataInputStream;
protected DataOutputStream dataOutputStream;
// support for readBytes
protected int remainingByteArrayLen = Integer.MIN_VALUE;
protected static final byte TYPE_BOOLEAN = 1;
protected static final byte TYPE_STRING = 2;
protected static final byte TYPE_INT = 3;
protected static final byte TYPE_SHORT = 4;
protected static final byte TYPE_LONG = 5;
protected static final byte TYPE_FLOAT = 6;
protected static final byte TYPE_DOUBLE = 7;
protected static final byte TYPE_BYTE = 8;
protected static final byte TYPE_CHAR = 9;
protected static final byte TYPE_BYTES = 10;
protected static final byte TYPE_NULL = 11;
private static String typeToString(byte type) {
switch (type) {
case TYPE_BOOLEAN:
return "boolean";
case TYPE_STRING:
return "string";
case TYPE_INT:
return "int";
case TYPE_SHORT:
return "short";
case TYPE_LONG:
return "long";
case TYPE_FLOAT:
return "float";
case TYPE_DOUBLE:
return "double";
case TYPE_BYTE:
return "byte";
case TYPE_CHAR:
return "char";
case TYPE_BYTES:
return "bytes";
case TYPE_NULL:
return "null";
default:
return "?" + type;
}
}
@SuppressFBWarnings("EI_EXPOSE_REP2")
public PulsarStreamMessage(byte[] payload) throws JMSException {
try {
this.dataInputStream = new DataInputStream(new ByteArrayInputStream(payload));
this.originalMessage = payload;
this.stream = null;
this.dataOutputStream = null;
writable = false;
} catch (Exception err) {
throw Utils.handleException(err);
}
}
public PulsarStreamMessage() {
try {
this.dataInputStream = null;
this.stream = new ByteArrayOutputStream();
this.dataOutputStream = new DataOutputStream(stream);
this.originalMessage = null;
this.writable = true;
} catch (Exception err) {
throw new RuntimeException(err);
}
}
/**
* Returns whether the message body is capable of being assigned to the specified type. If this
* method returns true then a subsequent call to the method {@code getBody} on the same message
* with the same type argument would not throw a MessageFormatException.
*
*
If the message is a {@code StreamMessage} then false is always returned. If the message is a
* {@code ObjectMessage} and object deserialization fails then false is returned. If the message
* has no body then any type may be specified and true is returned.
*
* @param c The specified type
* If the message is a {@code TextMessage} then this method will only return true if this
* parameter is set to {@code String.class} or another type to which a {@code String} is
* assignable.
* If the message is a {@code ObjectMessage} then this method will only return true if this
* parameter is set to {@code java.io.Serializable.class} or another class to which the body
* is assignable.
* If the message is a {@code MapMessage} then this method will only return true if this
* parameter is set to {@code java.util.Map.class} (or {@code java.lang.Object.class}).
* If the message is a {@code BytesMessage} then this this method will only return true if
* this parameter is set to {@code byte[].class} (or {@code java.lang.Object.class}).
* If the message is a {@code TextMessage}, {@code ObjectMessage}, {@code MapMessage} or
* {@code BytesMessage} and the message has no body, then the above does not apply and this
* method will return true irrespective of the value of this parameter.
* If the message is a {@code Message} (but not one of its subtypes) then this method will
* return true irrespective of the value of this parameter.
* @return whether the message body is capable of being assigned to the specified type
* @throws JMSException if the JMS provider fails to return a value due to some internal error.
*/
@Override
public boolean isBodyAssignableTo(Class c) throws JMSException {
return byte[].class == c;
}
@Override
protected void prepareForSend(TypedMessageBuilder producer) throws JMSException {
try {
if (stream != null) {
// write mode
dataOutputStream.flush();
dataOutputStream.close();
producer.value(stream.toByteArray());
} else {
// read mode
producer.value(originalMessage);
}
} catch (Exception err) {
throw Utils.handleException(err);
}
}
/**
* Reads a {@code boolean} from the stream message.
*
* @return the {@code boolean} value read
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of message stream has been reached.
* @throws MessageFormatException if this type conversion is invalid.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public boolean readBoolean() throws JMSException {
checkReadable();
try {
byte dataType = readDataType();
switch (dataType) {
case TYPE_BOOLEAN:
return dataInputStream.readBoolean();
case TYPE_STRING:
return Boolean.parseBoolean(dataInputStream.readUTF());
default:
// fail
checkType(dataType, TYPE_BOOLEAN);
return false;
}
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Reads a {@code byte} value from the stream message.
*
* @return the next byte from the stream message as a 8-bit {@code byte}
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of message stream has been reached.
* @throws MessageFormatException if this type conversion is invalid.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public byte readByte() throws JMSException {
checkReadable();
try {
dataInputStream.mark(2);
byte dataType = readDataType();
switch (dataType) {
case TYPE_STRING:
return Byte.parseByte(dataInputStream.readUTF());
case TYPE_BYTE:
return dataInputStream.readByte();
default:
// failing
checkType(dataType, TYPE_BYTE);
return 0;
}
} catch (Exception err) {
resetStreamAtMark();
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Reads a 16-bit integer from the stream message.
*
* @return a 16-bit integer from the stream message
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of message stream has been reached.
* @throws MessageFormatException if this type conversion is invalid.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public short readShort() throws JMSException {
checkReadable();
try {
dataInputStream.mark(5);
byte dataType = readDataType();
switch (dataType) {
case TYPE_SHORT:
return dataInputStream.readShort();
case TYPE_STRING:
return Short.parseShort(dataInputStream.readUTF());
case TYPE_BYTE:
return dataInputStream.readByte();
default:
// failing
checkType(dataType, TYPE_SHORT);
return 0;
}
} catch (Exception err) {
resetStreamAtMark();
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Reads a Unicode character value from the stream message.
*
* @return a Unicode character from the stream message
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of message stream has been reached.
* @throws MessageFormatException if this type conversion is invalid
* @throws MessageNotReadableException if the message is in write-only mode.
*/
@SuppressFBWarnings("DCN_NULLPOINTER_EXCEPTION")
public char readChar() throws JMSException {
checkReadable();
try {
dataInputStream.mark(2);
byte dataType = readDataType();
switch (dataType) {
case TYPE_CHAR:
return dataInputStream.readChar();
case TYPE_NULL:
throw new NullPointerException("invalid conversion");
default:
// failing
checkType(dataType, TYPE_CHAR);
return 0;
}
} catch (NullPointerException err) {
throw err;
} catch (Exception err) {
resetStreamAtMark();
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Reads a 32-bit integer from the stream message.
*
* @return a 32-bit integer value from the stream message, interpreted as an {@code int}
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of message stream has been reached.
* @throws MessageFormatException if this type conversion is invalid.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public int readInt() throws JMSException {
checkReadable();
try {
byte dataType = readDataType();
switch (dataType) {
case TYPE_INT:
return dataInputStream.readInt();
case TYPE_SHORT:
return dataInputStream.readShort();
case TYPE_STRING:
return Integer.parseInt(dataInputStream.readUTF());
case TYPE_BYTE:
return dataInputStream.readByte();
default:
// failing
checkType(dataType, TYPE_INT);
return 0;
}
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Reads a 64-bit integer from the stream message.
*
* @return a 64-bit integer value from the stream message, interpreted as a {@code long}
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of message stream has been reached.
* @throws MessageFormatException if this type conversion is invalid.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public long readLong() throws JMSException {
checkReadable();
try {
dataInputStream.mark(9);
byte dataType = readDataType();
switch (dataType) {
case TYPE_INT:
return dataInputStream.readInt();
case TYPE_LONG:
return dataInputStream.readLong();
case TYPE_SHORT:
return dataInputStream.readShort();
case TYPE_STRING:
return Integer.parseInt(dataInputStream.readUTF());
case TYPE_BYTE:
return dataInputStream.readByte();
default:
// failing
checkType(dataType, TYPE_LONG);
return 0;
}
} catch (Exception err) {
resetStreamAtMark();
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Reads a {@code float} from the stream message.
*
* @return a {@code float} value from the stream message
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of message stream has been reached.
* @throws MessageFormatException if this type conversion is invalid.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public float readFloat() throws JMSException {
checkReadable();
try {
dataInputStream.mark(9);
byte dataType = readDataType();
switch (dataType) {
case TYPE_FLOAT:
return dataInputStream.readFloat();
case TYPE_STRING:
return Float.parseFloat(dataInputStream.readUTF());
default:
// failing
checkType(dataType, TYPE_FLOAT);
return 0;
}
} catch (Exception err) {
resetStreamAtMark();
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Reads a {@code double} from the stream message.
*
* @return a {@code double} value from the stream message
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of message stream has been reached.
* @throws MessageFormatException if this type conversion is invalid.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public double readDouble() throws JMSException {
checkReadable();
try {
dataInputStream.mark(9);
byte dataType = readDataType();
switch (dataType) {
case TYPE_FLOAT:
return dataInputStream.readFloat();
case TYPE_DOUBLE:
return dataInputStream.readDouble();
case TYPE_STRING:
return Double.parseDouble(dataInputStream.readUTF());
default:
// failing
checkType(dataType, TYPE_DOUBLE);
return 0;
}
} catch (Exception err) {
resetStreamAtMark();
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Reads a {@code String} from the stream message.
*
* @return a Unicode string from the stream message
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of message stream has been reached.
* @throws MessageFormatException if this type conversion is invalid.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public String readString() throws JMSException {
checkReadable();
try {
dataInputStream.mark(100);
byte dataType = readDataType();
switch (dataType) {
case TYPE_NULL:
return null;
case TYPE_STRING:
return dataInputStream.readUTF();
case TYPE_BOOLEAN:
return Boolean.toString(dataInputStream.readBoolean());
case TYPE_CHAR:
return Character.toString(dataInputStream.readChar());
case TYPE_BYTE:
return Byte.toString(dataInputStream.readByte());
case TYPE_SHORT:
return Short.toString(dataInputStream.readShort());
case TYPE_LONG:
return Long.toString(dataInputStream.readLong());
case TYPE_INT:
return Integer.toString(dataInputStream.readInt());
case TYPE_FLOAT:
return Float.toString(dataInputStream.readFloat());
case TYPE_DOUBLE:
return Double.toString(dataInputStream.readDouble());
default:
throw new MessageFormatException(
"Cannot read a string out of a " + typeToString(dataType));
}
} catch (Exception err) {
resetStreamAtMark();
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes a {@code boolean} to the stream message. The value {@code true} is written as the value
* {@code (byte)1}; the value {@code false} is written as the value {@code (byte)0}.
*
* @param value the {@code boolean} value to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeBoolean(boolean value) throws JMSException {
checkWritable();
try {
writeDataType(TYPE_BOOLEAN);
dataOutputStream.writeBoolean(value);
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes a {@code byte} to the stream message.
*
* @param value the {@code byte} value to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeByte(byte value) throws JMSException {
checkWritable();
try {
writeDataType(TYPE_BYTE);
dataOutputStream.writeByte(value);
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes a {@code short} to the stream message.
*
* @param value the {@code short} value to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeShort(short value) throws JMSException {
checkWritable();
try {
writeDataType(TYPE_SHORT);
dataOutputStream.writeShort(value);
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes a {@code char} to the stream message.
*
* @param value the {@code char} value to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeChar(char value) throws JMSException {
checkWritable();
try {
writeDataType(TYPE_CHAR);
dataOutputStream.writeChar(value);
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes an {@code int} to the stream message.
*
* @param value the {@code int} value to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeInt(int value) throws JMSException {
checkWritable();
try {
writeDataType(TYPE_INT);
dataOutputStream.writeInt(value);
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes a {@code long} to the stream message.
*
* @param value the {@code long} value to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeLong(long value) throws JMSException {
checkWritable();
try {
writeDataType(TYPE_LONG);
dataOutputStream.writeLong(value);
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes a {@code float} to the stream message.
*
* @param value the {@code float} value to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeFloat(float value) throws JMSException {
checkWritable();
try {
writeDataType(TYPE_FLOAT);
dataOutputStream.writeFloat(value);
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes a {@code double} to the stream message.
*
* @param value the {@code double} value to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeDouble(double value) throws JMSException {
checkWritable();
try {
writeDataType(TYPE_DOUBLE);
dataOutputStream.writeDouble(value);
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes a {@code String} to the stream message.
*
* @param value the {@code String} value to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeString(String value) throws JMSException {
checkWritable();
try {
if (value == null) {
writeDataType(TYPE_NULL);
return;
}
writeDataType(TYPE_STRING);
dataOutputStream.writeUTF(value);
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes a byte array field to the stream message.
*
* The byte array {@code value} is written to the message as a byte array field. Consecutively
* written byte array fields are treated as two distinct fields when the fields are read.
*
* @param value the byte array value to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeBytes(byte[] value) throws JMSException {
writeBytes(value, 0, value.length);
}
/**
* Writes a portion of a byte array as a byte array field to the stream message.
*
*
The a portion of the byte array {@code value} is written to the message as a byte array
* field. Consecutively written byte array fields are treated as two distinct fields when the
* fields are read.
*
* @param value the byte array value to be written
* @param offset the initial offset within the byte array
* @param length the number of bytes to use
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeBytes(byte[] value, int offset, int length) throws JMSException {
checkWritable();
if (value == null) {
throw new NullPointerException();
}
try {
writeDataType(TYPE_BYTES);
writeArrayLen(length);
dataOutputStream.write(value, offset, length);
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes an object to the stream message.
*
*
This method works only for the objectified primitive object types ({@code Integer}, {@code
* Double}, {@code Long} ...), {@code String} objects, and byte arrays.
*
* @param value the Java object to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageFormatException if the object is invalid.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeObject(Object value) throws JMSException {
checkWritable();
try {
if (value == null) {
writeDataType(TYPE_NULL);
return;
}
// see also validateWritableObject
if (value instanceof Integer) {
writeInt((Integer) value);
} else if (value instanceof String) {
writeUTF((String) value);
} else if (value instanceof Short) {
writeShort((Short) value);
} else if (value instanceof Long) {
writeLong((Long) value);
} else if (value instanceof Double) {
writeDouble((Double) value);
} else if (value instanceof Float) {
writeFloat((Float) value);
} else if (value instanceof Byte) {
writeByte((Byte) value);
} else if (value instanceof Character) {
writeChar((Character) value);
} else if (value instanceof Boolean) {
writeBoolean((Boolean) value);
} else if (value instanceof byte[]) {
writeBytes((byte[]) value);
} else {
throw new MessageFormatException("Unsupported type " + value.getClass());
}
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Clears out the message body. Clearing a message's body does not clear its header values or
* property entries.
*
*
If this message body was read-only, calling this method leaves the message body in the same
* state as an empty body in a newly created message.
*
* @throws JMSException if the JMS provider fails to clear the message body due to some internal
* error.
*/
@Override
public void clearBody() throws JMSException {
remainingByteArrayLen = Integer.MIN_VALUE;
this.writable = true;
try {
if (stream != null) {
this.dataInputStream = new DataInputStream(new ByteArrayInputStream(stream.toByteArray()));
this.stream = null;
this.dataOutputStream = null;
} else {
this.stream = new ByteArrayOutputStream();
this.dataOutputStream = new DataOutputStream(stream);
this.originalMessage = null;
this.dataInputStream = null;
}
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Puts the message body in read-only mode and repositions the stream to the beginning.
*
* @throws JMSException if the JMS provider fails to reset the message due to some internal error.
* @throws MessageFormatException if the message has an invalid format.
*/
public void reset() throws JMSException {
remainingByteArrayLen = Integer.MIN_VALUE;
this.writable = false;
try {
if (stream != null) {
this.dataOutputStream.flush();
this.originalMessage = stream.toByteArray();
this.dataInputStream = new DataInputStream(new ByteArrayInputStream(originalMessage));
this.stream = null;
this.dataOutputStream = null;
} else {
this.dataInputStream = new DataInputStream(new ByteArrayInputStream(originalMessage));
}
} catch (Exception err) {
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Gets the number of bytes of the message body when the message is in read-only mode. The value
* returned can be used to allocate a byte array. The value returned is the entire length of the
* message body, regardless of where the pointer for reading the message is currently located.
*
* @return number of bytes in the message
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageNotReadableException if the message is in write-only mode.
* @since JMS 1.1
*/
public long getBodyLength() throws JMSException {
checkReadable();
return originalMessage.length;
}
/**
* Reads an unsigned 8-bit number from the bytes message stream.
*
* @return the next byte from the bytes message stream, interpreted as an unsigned 8-bit number
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of bytes stream has been reached.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public int readUnsignedByte() throws JMSException {
checkReadable();
try {
dataInputStream.mark(2);
checkType(readDataType(), TYPE_BYTE);
return dataInputStream.readUnsignedByte();
} catch (Exception err) {
resetStreamAtMark();
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Reads an unsigned 16-bit number from the bytes message stream.
*
* @return the next two bytes from the bytes message stream, interpreted as an unsigned 16-bit
* integer
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of bytes stream has been reached.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public int readUnsignedShort() throws JMSException {
checkReadable();
try {
dataInputStream.mark(5);
checkType(readDataType(), TYPE_SHORT);
return dataInputStream.readUnsignedShort();
} catch (Exception err) {
resetStreamAtMark();
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Reads a string that has been encoded using a modified UTF-8 format from the bytes message
* stream.
*
*
For more information on the UTF-8 format, see "File System Safe UCS Transformation Format
* (FSS_UTF)", X/Open Preliminary Specification, X/Open Company Ltd., Document Number: P316. This
* information also appears in ISO/IEC 10646, Annex P.
*
* @return a Unicode string from the bytes message stream
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageEOFException if unexpected end of bytes stream has been reached.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public String readUTF() throws JMSException {
return readString();
}
/**
* Reads a portion of the bytes message stream.
*
*
If the length of array {@code value} is less than the number of bytes remaining to be read
* from the stream, the array should be filled. A subsequent call reads the next increment, and so
* on.
*
*
If the number of bytes remaining in the stream is less than the length of array {@code
* value}, the bytes should be read into the array. The return value of the total number of bytes
* read will be less than the length of the array, indicating that there are no more bytes left to
* be read from the stream. The next read of the stream returns -1.
*
*
If {@code length} is negative, or {@code length} is greater than the length of the array
* {@code value}, then an {@code IndexOutOfBoundsException} is thrown. No bytes will be read from
* the stream for this exception case.
*
* @param value the buffer into which the data is read
* @param length the number of bytes to read; must be less than or equal to {@code value.length}
* @return the total number of bytes read into the buffer, or -1 if there is no more data because
* the end of the stream has been reached
* @throws JMSException if the JMS provider fails to read the message due to some internal error.
* @throws MessageNotReadableException if the message is in write-only mode.
*/
public int readBytes(byte[] value, int length) throws JMSException {
checkReadable();
if (length < 0 || (value != null && length > value.length)) {
throw new IndexOutOfBoundsException();
}
try {
if (remainingByteArrayLen == Integer.MIN_VALUE) { // start of field
dataInputStream.mark(length);
checkType(readDataType(), TYPE_BYTES);
remainingByteArrayLen = readArrayLen();
if (value == null) {
return -1;
}
int read = dataInputStream.read(value, 0, length);
remainingByteArrayLen = remainingByteArrayLen - read;
return read;
} else if (remainingByteArrayLen > 0) {
if (value == null) {
return -1;
}
int read = dataInputStream.read(value, 0, length);
remainingByteArrayLen = remainingByteArrayLen - read;
return read;
} else {
// end of field
remainingByteArrayLen = Integer.MIN_VALUE;
return -1;
}
} catch (EOFException err) {
return -1;
} catch (Exception err) {
resetStreamAtMark();
throw handleExceptionAccordingToMessageSpecs(err);
}
}
/**
* Writes a string to the bytes message stream using UTF-8 encoding in a machine-independent
* manner.
*
*
For more information on the UTF-8 format, see "File System Safe UCS Transformation Format
* (FSS_UTF)", X/Open Preliminary Specification, X/Open Company Ltd., Document Number: P316. This
* information also appears in ISO/IEC 10646, Annex P.
*
* @param value the {@code String} value to be written
* @throws JMSException if the JMS provider fails to write the message due to some internal error.
* @throws MessageNotWriteableException if the message is in read-only mode.
*/
public void writeUTF(String value) throws JMSException {
writeString(value);
}
}