com.jkoolcloud.tnt4j.streams.parsers.ActivityJMSMessageParser Maven / Gradle / Ivy
/*
* Copyright 2014-2022 JKOOL, LLC.
*
* 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.jkoolcloud.tnt4j.streams.parsers;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.jms.*;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import com.jkoolcloud.tnt4j.core.OpLevel;
import com.jkoolcloud.tnt4j.sink.EventSink;
import com.jkoolcloud.tnt4j.streams.configure.JMSParserProperties;
import com.jkoolcloud.tnt4j.streams.fields.StreamFieldType;
import com.jkoolcloud.tnt4j.streams.utils.*;
/**
* Implements an activity data parser that assumes each activity data item is an JMS message data structure. Message
* payload data is put into map entry using key defined in {@link StreamsConstants#ACTIVITY_DATA_KEY}. This parser
* supports JMS messages of those types:
*
* - {@link javax.jms.TextMessage} - activity data is message text
* - {@link javax.jms.BytesMessage} - activity data is message {@code byte[]} or string made from message
* {@code byte[]} depending on property 'ConvertToString' value
* - {@link javax.jms.MapMessage} - activity data is message map entries
* - {@link javax.jms.StreamMessage} - activity data is message {@code byte[]} or string made from message
* {@code byte[]} depending on property 'ConvertToString' value
* - {@link javax.jms.ObjectMessage} - activity data is message serializable object
*
*
* NOTE: Custom messages parsing not implemented and puts just log entry.
*
* This parser resolved data map may contain such entries:
*
* - ActivityData - JMS message payload data. In case of {@link javax.jms.MapMessage} this entry is omitted, because
* message contained map entries are copied to data map.
* - MsgMetadata - JMS message metadata map containing those fields:
*
* - Correlator - message correlation identifier
* - CorrelatorBytes - message correlation identifier bytes value
* - DeliveryMode - message delivery mode number
* - DeliveryTime - message delivery time value
* - Destination - destination name this message was received from
* - Expiration - message's expiration time
* - MessageId - message identifier string
* - Priority - message priority level number
* - Redelivered - indication flag of whether this message is being redelivered
* - ReplyTo - destination name to which a reply to this message should be sent
* - Timestamp - timestamp in milliseconds
* - Type - message type name supplied by the client when the message was sent
* - CustomMsgProps - map of properties accessible over keys enumeration {@link javax.jms.Message#getPropertyNames()}
* and values resolved over {@link javax.jms.Message#getObjectProperty(String)}
*
*
* - ActivityTransport - value is always
* {@value com.jkoolcloud.tnt4j.streams.utils.JMSStreamConstants#TRANSPORT_JMS}.
*
*
* This parser supports the following configuration properties (in addition to those supported by
* {@link AbstractActivityMapParser}):
*
* - ConvertToString - flag indicating whether to convert message payload {@code byte[]} data to string. Applicable to
* {@link javax.jms.BytesMessage} and {@link javax.jms.StreamMessage}. Default value - 'false'. (Optional)
*
*
* @version $Revision: 1 $
*/
public class ActivityJMSMessageParser extends AbstractActivityMapParser {
private static final EventSink LOGGER = LoggerUtils.getLoggerSink(ActivityJMSMessageParser.class);
private static final int BYTE_BUFFER_LENGTH = 1024;
private boolean convertToString = false;
/**
* Constructs a new ActivityJMSMessageParser.
*/
public ActivityJMSMessageParser() {
super();
}
@Override
protected EventSink logger() {
return LOGGER;
}
/**
* Returns whether this parser supports the given format of the activity data. This is used by activity streams to
* determine if the parser can parse the data in the format that the stream has it.
*
* This parser supports the following class types (and all classes extending/implementing any of these):
*
* - {@link javax.jms.Message}
*
*
* @param data
* data object whose class is to be verified
* @return {@code true} if this parser can process data in the specified format, {@code false} - otherwise
*/
@Override
protected boolean isDataClassSupportedByParser(Object data) {
return data instanceof Message;
}
@Override
public void setProperty(String name, String value) {
super.setProperty(name, value);
if (JMSParserProperties.PROP_CONV_TO_STRING.equalsIgnoreCase(name)) {
convertToString = Utils.toBoolean(value);
logger().log(OpLevel.DEBUG, StreamsResources.getBundle(StreamsResources.RESOURCE_BUNDLE_NAME),
"ActivityParser.setting", name, value);
}
}
@Override
public Object getProperty(String name) {
if (JMSParserProperties.PROP_CONV_TO_STRING.equalsIgnoreCase(name)) {
return convertToString;
}
return super.getProperty(name);
}
/**
* Makes map object containing activity object data collected from JMS message payload data.
*
* @param data
* activity object data object - JMS message
*
* @return activity object data map
*/
@Override
protected Map getDataMap(Object data) {
if (data == null) {
return null;
}
Message message = (Message) data;
Map dataMap = new HashMap<>();
dataMap.put(RAW_ACTIVITY_STRING_KEY, message.toString());
try {
Map metadataMap = parseCommonMessage(message);
dataMap.put(JMSStreamConstants.MSG_METADATA_KEY, metadataMap);
if (message instanceof TextMessage) {
parseTextMessage((TextMessage) message, dataMap);
} else if (message instanceof BytesMessage) {
parseBytesMessage((BytesMessage) message, dataMap);
} else if (message instanceof MapMessage) {
parseMapMessage((MapMessage) message, dataMap);
} else if (message instanceof StreamMessage) {
parseStreamMessage((StreamMessage) message, dataMap);
} else if (message instanceof ObjectMessage) {
parseObjectMessage((ObjectMessage) message, dataMap);
} else {
parseCustomMessage(message, dataMap);
}
} catch (JMSException exc) {
Utils.logThrowable(logger(), OpLevel.ERROR,
StreamsResources.getBundle(JMSStreamConstants.RESOURCE_BUNDLE_NAME),
"ActivityJMSMessageParser.payload.data.error", exc);
}
if (!dataMap.isEmpty()) {
dataMap.put(StreamsConstants.TRANSPORT_KEY, JMSStreamConstants.TRANSPORT_JMS);
}
return dataMap;
}
/**
* Collects JMS {@link com.jkoolcloud.tnt4j.core.Message} common fields values into message metadata map.
*
* Common message metadata fields are:
*
* - Correlator - message correlation identifier
* - CorrelatorBytes - message correlation identifier bytes value
* - DeliveryMode - message delivery mode number
* - Destination - destination name this message was received from
* - Expiration - message's expiration time
* - MessageId - message identifier string
* - Priority - message priority level number
* - Redelivered - indication flag of whether this message is being redelivered
* - ReplyTo - destination name to which a reply to this message should be sent
* - Timestamp - timestamp in milliseconds
* - Type - message type name supplied by the client when the message was sent
* - CustomMsgProps - map of properties accessible over keys enumeration
* {@link javax.jms.Message#getPropertyNames()} and values resolved over
* {@link javax.jms.Message#getObjectProperty(String)}
*
*
* @param message
* JMS message instance to parse
* @return map instance, containing resolved message metadata values
* @throws JMSException
* if JMS exception occurs while getting common fields values from message
*/
protected Map parseCommonMessage(Message message) throws JMSException {
Map msgMetaMap = new HashMap<>(10);
msgMetaMap.put(StreamFieldType.Correlator.name(), message.getJMSCorrelationID());
msgMetaMap.put("CorrelatorBytes", message.getJMSCorrelationIDAsBytes()); // NON-NLS
msgMetaMap.put("DeliveryMode", message.getJMSDeliveryMode()); // NON-NLS
msgMetaMap.put("DeliveryTime", message.getJMSDeliveryTime()); // NON-NLS
msgMetaMap.put("Destination", getDestinationName(message.getJMSDestination())); // NON-NLS
msgMetaMap.put("Expiration", message.getJMSExpiration()); // NON-NLS
msgMetaMap.put("MessageId", message.getJMSMessageID()); // NON-NLS
msgMetaMap.put("Priority", message.getJMSPriority()); // NON-NLS
msgMetaMap.put("Redelivered", message.getJMSRedelivered()); // NON-NLS
msgMetaMap.put("ReplyTo", getDestinationName(message.getJMSReplyTo())); // NON-NLS
msgMetaMap.put("Timestamp", message.getJMSTimestamp()); // NON-NLS
msgMetaMap.put("Type", message.getJMSType()); // NON-NLS
@SuppressWarnings("unchecked")
Enumeration propNames = message.getPropertyNames();
Map customPropsMap = new HashMap<>();
if (propNames != null) {
while (propNames.hasMoreElements()) {
String pName = propNames.nextElement();
customPropsMap.put(pName, message.getObjectProperty(pName));
}
}
if (!customPropsMap.isEmpty()) {
msgMetaMap.put("CustomMsgProps", customPropsMap); // NON-NLS
}
return msgMetaMap;
}
/**
* Resolves provided JMS {@link javax.jms.Destination} instance name.
*
* @param dest
* JMS destination instance to resolve value
* @return resolved JMS destination name, or {@code null} if destination is {@code null}
* @throws JMSException
* if JMS exception occurs while getting destination name value
*/
protected static String getDestinationName(Destination dest) throws JMSException {
if (dest instanceof Topic) {
return ((Topic) dest).getTopicName();
} else if (dest instanceof Queue) {
return ((Queue) dest).getQueueName();
} else {
return dest == null ? null : dest.toString();
}
}
/**
* Parse JMS {@link TextMessage} activity info into activity data map.
*
* @param textMessage
* JMS text message
* @param dataMap
* activity data map collected from JMS {@link TextMessage}
* @throws JMSException
* if JMS exception occurs while getting text from message
*/
protected void parseTextMessage(TextMessage textMessage, Map dataMap) throws JMSException {
String text = textMessage.getText();
if (StringUtils.isNotEmpty(text)) {
dataMap.put(StreamsConstants.ACTIVITY_DATA_KEY, text);
}
}
/**
* Parse JMS {@link BytesMessage} activity info into activity data map.
*
* @param bytesMessage
* JMS bytes message
* @param dataMap
* activity data map collected from JMS {@link BytesMessage}
* @throws JMSException
* if JMS exception occurs while reading bytes from message
*/
protected void parseBytesMessage(BytesMessage bytesMessage, Map dataMap) throws JMSException {
byte[] bytes = new byte[(int) bytesMessage.getBodyLength()];
bytesMessage.readBytes(bytes);
if (ArrayUtils.isNotEmpty(bytes)) {
dataMap.put(StreamsConstants.ACTIVITY_DATA_KEY, convertToString ? Utils.getString(bytes) : bytes);
}
}
/**
* Parse JMS {@link MapMessage} activity info into activity data map.
*
* @param mapMessage
* JMS map message
* @param dataMap
* activity data map collected from JMS {@link MapMessage}
* @throws JMSException
* if JMS exception occurs while getting map entries from message
*/
@SuppressWarnings("unchecked")
protected void parseMapMessage(MapMessage mapMessage, Map dataMap) throws JMSException {
Enumeration en = (Enumeration) mapMessage.getMapNames();
while (en.hasMoreElements()) {
String key = en.nextElement();
dataMap.put(key, mapMessage.getObject(key));
}
}
/**
* Parse JMS {@link StreamMessage} activity info into activity data map.
*
* @param streamMessage
* JMS stream message
* @param dataMap
* activity data map collected from JMS {@link StreamMessage}
* @throws JMSException
* if JMS exception occurs while reading bytes from message
*/
protected void parseStreamMessage(StreamMessage streamMessage, Map dataMap) throws JMSException {
streamMessage.reset();
byte[] buffer = new byte[BYTE_BUFFER_LENGTH];
int bytesRead = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream(buffer.length);
try {
do {
bytesRead = streamMessage.readBytes(buffer);
baos.write(buffer);
} while (bytesRead != 0);
} catch (IOException exc) {
Utils.logThrowable(logger(), OpLevel.ERROR,
StreamsResources.getBundle(JMSStreamConstants.RESOURCE_BUNDLE_NAME),
"ActivityJMSMessageParser.bytes.buffer.error", exc);
}
byte[] bytes = baos.toByteArray();
Utils.close(baos);
if (ArrayUtils.isNotEmpty(bytes)) {
dataMap.put(StreamsConstants.ACTIVITY_DATA_KEY, convertToString ? Utils.getString(bytes) : bytes);
}
}
/**
* Parse JMS {@link ObjectMessage} activity info into activity data map.
*
* @param objMessage
* JMS object message
* @param dataMap
* activity data map collected from JMS {@link ObjectMessage}
* @throws JMSException
* if JMS exception occurs while getting {@link Serializable} object from message
*/
protected void parseObjectMessage(ObjectMessage objMessage, Map dataMap) throws JMSException {
Serializable serializableObj = objMessage.getObject();
if (serializableObj != null) {
dataMap.put(StreamsConstants.ACTIVITY_DATA_KEY, serializableObj);
}
}
/**
* Parse custom message activity info into activity data map.
*
* @param message
* custom JMS message
* @param dataMap
* activity data map collected from custom JMS message
* @throws JMSException
* if any JMS exception occurs while parsing message
*/
protected void parseCustomMessage(Message message, Map dataMap) throws JMSException {
logger().log(OpLevel.WARNING, StreamsResources.getBundle(JMSStreamConstants.RESOURCE_BUNDLE_NAME),
"ActivityJMSMessageParser.parsing.custom.jms.message");
}
private static final String[] ACTIVITY_DATA_TYPES = { "JMS MESSAGE" }; // NON-NLS
/**
* Returns type of RAW activity data entries.
*
* @return type of RAW activity data entries - {@code "JMS MESSAGE"}
*/
@Override
protected String[] getActivityDataType() {
return ACTIVITY_DATA_TYPES;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy