/******************************************************************************* * Copyright (C) 2013, 2014, International Business Machines Corporation * All Rights Reserved *******************************************************************************/ package com.ibm.streamsx.messaging.jms; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.util.List; import javax.jms.BytesMessage; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageEOFException; import javax.jms.MessageNotReadableException; import javax.jms.Session; import com.ibm.streams.operator.OutputTuple; import com.ibm.streams.operator.Tuple; import com.ibm.streams.operator.Type; import com.ibm.streams.operator.Type.MetaType; import com.ibm.streams.operator.metrics.Metric; import com.ibm.streams.operator.types.Blob; import com.ibm.streams.operator.types.RString; import com.ibm.streams.operator.types.Timestamp; import com.ibm.streams.operator.types.ValueFactory; //This class handles the JMS Bytes message type class BytesMessageHandler extends JMSMessageHandlerImpl { // variable to set the codepage parameter, defaults to UTF-8 private final String codepage; // constructor BytesMessageHandler(List<NativeSchema> nativeSchemaObjects, String cpage) { // call the base class constructor to initialize the native schema // attributes. super(nativeSchemaObjects); // set the codepage parameter if one is specified in the operator model, // if not a default value of UTF-8 is assumed codepage = cpage; } BytesMessageHandler(List<NativeSchema> nativeSchemaObjects, String cpage, Metric nTruncatedInserts) { super(nativeSchemaObjects, nTruncatedInserts); // set the codepage parameter if one is specified in the operator model, // if not a default value of UTF-8 is assumed codepage = cpage; } // For JMSSink operator, convert the incoming tuple to a JMS BytesMessage public Message convertTupleToMessage(Tuple tuple, Session session) throws JMSException, UnsupportedEncodingException { // create a new BytesMessage BytesMessage message; synchronized (session) { message = (BytesMessage) session.createBytesMessage(); } // variable to specify if any of the attributes in the message is // truncated boolean isTruncated = false; for (NativeSchema currentObject : nativeSchemaObjects) { // Iterate through the native schema attributes // extract the name, type and length final String name = currentObject.getName(); final NativeTypes type = currentObject.getType(); final int length = currentObject.getLength(); // handle based on the data-type switch (type) { case Bytes: { // variable to hold the bytes data byte[] bytedata = null; int size; MetaType metaType = tuple.getStreamSchema().getAttribute(name).getType().getMetaType(); switch (metaType) { case DECIMAL32: case DECIMAL64: case DECIMAL128: { bytedata = tuple.getBigDecimal(name).toString().getBytes(codepage); } break; case TIMESTAMP: { bytedata = ((tuple.getTimestamp(name).getTimeAsSeconds()).toString()).getBytes(codepage); } break; case BLOB: Blob bl = tuple.getBlob(name); bytedata = new byte[(int) bl.getLength()]; bl.getByteBuffer(0, (int) bl.getLength()).get(bytedata); break; case RSTRING: bytedata = ((RString) tuple.getObject(name)).getData(); break; case USTRING: bytedata = tuple.getString(name).getBytes(codepage); break; } size = bytedata.length; // check for length in native schema // spl types decimal32, decimal64,decimal128, timestamp have a // default length of -4 if (length == -2) { // truncate if (size > java.lang.Short.MAX_VALUE) { size = java.lang.Short.MAX_VALUE; isTruncated = true; } message.writeShort((short) size); } else if (length == -4) { message.writeInt(size); } else { // if the length of the bytedata is greater than the length // specified in native schema // set the isTruncated to true // truncate the bytedata if (size > length) { isTruncated = true; size = length; } } // Need to truncate or pad as required message.writeBytes(bytedata, 0, size); // variable required to pad the bytedata // for RSTRING and USTRING we pad with space and for BLOB we pad // with null byte topad = ' '; if (tuple.getStreamSchema().getAttribute(name).getType().getMetaType() == Type.MetaType.BLOB) topad = 0; if (length > 0) { for (int i = size; i < length; i++) message.writeByte(topad); } } break; case Byte: message.writeByte(tuple.getByte(name)); break; case Short: message.writeShort(tuple.getShort(name)); break; case Int: message.writeInt(tuple.getInt(name)); break; case Long: message.writeLong(tuple.getLong(name)); break; case Float: message.writeFloat(tuple.getFloat(name)); break; case Double: message.writeDouble(tuple.getDouble(name)); break; case Boolean: message.writeBoolean(tuple.getBoolean(name)); default: break; } } // if the isTruncated boolean is set, increment the metric // nTruncatedInserts if (isTruncated) { nTruncatedInserts.incrementValue(1); } // return the message return message; } // For JMSSource operator, convert the incoming JMS BytesMessage to tuple public MessageAction convertMessageToTuple(Message message, OutputTuple tuple) throws JMSException, UnsupportedEncodingException { if (!(message instanceof BytesMessage)) { // We got a wrong message type so throw an error return MessageAction.DISCARD_MESSAGE_WRONG_TYPE; } BytesMessage bytesMessage = (BytesMessage) message; boolean discard = false; // Iterate through the native schema attributes for (NativeSchema currentObject : nativeSchemaObjects) { // Added the try catch block to catch the MessageEOFException // This exception must be thrown when an unexpected end of stream // has been reached when a BytesMessage is being read. try { // extract the name, type and length final String name = currentObject.getName(); final NativeTypes type = currentObject.getType(); final int length = currentObject.getLength(); // handle based on data-tye // extract the data from the message for each native schema // attrbute and set into the tuple switch (type) { case Byte: byte bt = bytesMessage.readByte(); // we are interested in this currentObject only if it is // present in streams schema if (currentObject.getIsPresentInStreamSchema()) { tuple.setByte(name, bt); } break; case Short: short s = bytesMessage.readShort(); // we are interested in this currentObject only if it is // present in streams schema if (currentObject.getIsPresentInStreamSchema()) { tuple.setShort(name, s); } break; case Int: int i = bytesMessage.readInt(); // we are interested in this currentObject only if it is // present in streams schema if (currentObject.getIsPresentInStreamSchema()) { tuple.setInt(name, i); } break; case Long: long l = bytesMessage.readLong(); // we are interested in this currentObject only if it is // present in streams schema if (currentObject.getIsPresentInStreamSchema()) { tuple.setLong(name, l); } break; case Float: float f = bytesMessage.readFloat(); // we are interested in this currentObject only if it is // present in streams schema if (currentObject.getIsPresentInStreamSchema()) { tuple.setFloat(name, f); } break; case Double: double d = bytesMessage.readDouble(); // we are interested in this currentObject only if it is // present in streams schema if (currentObject.getIsPresentInStreamSchema()) { tuple.setDouble(name, d); } break; case Boolean: boolean bl = bytesMessage.readBoolean(); // we are interested in this currentObject only if it is // present in streams schema if (currentObject.getIsPresentInStreamSchema()) { tuple.setBoolean(name, bl); } break; case Bytes: { int size; // check for length in native schema // if -2 read a short from the message which will have the // length of the attribute if (length == -2) { size = (int) bytesMessage.readShort(); } else if (length == -4) { // if -2 read an int from the message which will have // the length of the attribute size = bytesMessage.readInt(); } else { size = length; } byte b[] = new byte[size]; // read that many bytes from the message int lenRead = bytesMessage.readBytes(b, size); if (lenRead < size) { // When the lenRead is less than size. // Discard message saying message too // short discard = true; } else { // we are interested in this currentObject only if it is // present in streams schema if (currentObject.getIsPresentInStreamSchema()) { switch (tuple.getStreamSchema().getAttribute(name).getType().getMetaType()) { case DECIMAL32: case DECIMAL64: case DECIMAL128: { String stringdata = new String(b, 0, lenRead, codepage); BigDecimal bigDecValue = new BigDecimal(stringdata); tuple.setBigDecimal(name, bigDecValue); } break; case TIMESTAMP: { String timeseriesData = new String(b, 0, lenRead, codepage); BigDecimal bigDecValue = new BigDecimal(timeseriesData); tuple.setTimestamp(name, Timestamp.getTimestamp(bigDecValue)); } break; case RSTRING: { RString rstringData = new RString(b); tuple.setObject(name, rstringData); } break; case USTRING: { String rstringData = new String(b, codepage); tuple.setString(name, rstringData); } break; case BLOB: { Blob blob = ValueFactory.newBlob(b, 0, b.length); tuple.setBlob(name, blob); break; } } } } } break; } if (discard == true) { // The message is too short return MessageAction.DISCARD_MESSAGE_EOF_REACHED; } } catch (MessageEOFException meofEx) { return MessageAction.DISCARD_MESSAGE_EOF_REACHED; } catch (MessageNotReadableException mnrEx) { return MessageAction.DISCARD_MESSAGE_UNREADABLE; } } // the message was read successfully return MessageAction.SUCCESSFUL_MESSAGE; } }