/***********************************************************************************
*
* Copyright (c) 2014 Kamil Baczkowicz
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
*
* Kamil Baczkowicz - initial API and implementation and/or initial documentation
*
*/
package pl.baczkowicz.mqttspy.logger;
import java.util.regex.Pattern;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.baczkowicz.mqttspy.common.generated.MessageLog;
import pl.baczkowicz.mqttspy.common.generated.MessageLogEnum;
import pl.baczkowicz.mqttspy.messages.FormattedMqttMessage;
import pl.baczkowicz.spy.utils.ConversionUtils;
/**
* Simple message log composer (string buffer based, no JAXB).
*/
public class SimpleMqttMessageLogComposer
{
/** Diagnostic logger. */
private final static Logger logger = LoggerFactory.getLogger(SimpleMqttMessageLogComposer.class);
/** Pattern that is used to decide whether a string should be wrapped in CDATA. */
private static final Pattern XML_CHARS = Pattern.compile("[&<>]");
/**
* Creates a single message log entry for the supplied message object
*
* @param message The message to be logged
* @param messageLogOptions Logging options
*
* @return The log message as string
*/
public static String createReceivedMessageLog(final FormattedMqttMessage message, final MessageLog messageLogOptions)
{
final StringBuffer logMessage = new StringBuffer();
logMessage.append("<MqttMessage");
appendAttribute(logMessage, "id", String.valueOf(message.getId()));
appendAttribute(logMessage, "timestamp", String.valueOf(message.getDate().getTime()));
appendAttribute(logMessage, "topic", message.getTopic());
// Quality of service
if (messageLogOptions.isLogQos())
{
appendAttribute(logMessage, "qos", String.valueOf(message.getQoS()));
}
// Retained flag
if (messageLogOptions.isLogRetained())
{
appendAttribute(logMessage, "retained", String.valueOf(message.isRetained()));
}
// Connection info
if (messageLogOptions.isLogConnection())
{
appendAttribute(logMessage, "connection", message.getConnection().getMqttConnectionDetails().getName());
}
// Subscription (logs the first one only)
if (messageLogOptions.isLogSubscription() && message.getMatchingSubscriptionTopics() != null && message.getMatchingSubscriptionTopics().size() > 0)
{
// Log the first matching subscription
appendAttribute(logMessage, "subscription", message.getMatchingSubscriptionTopics().get(0));
}
populatePayload(logMessage, message, messageLogOptions);
logMessage.append("</MqttMessage>");
return logMessage.toString();
}
/**
* Populates the message log with the message's payload.
*
* @param logMessage The message log to be populated
* @param message The message to be logged
* @param messageLogOptions Logging options
*/
private static void populatePayload(final StringBuffer logMessage, final FormattedMqttMessage message, final MessageLog messageLogOptions)
{
boolean encoded = MessageLogEnum.XML_WITH_ENCODED_PAYLOAD.equals(messageLogOptions.getValue());
final String payload = new String(message.getPayload());
// If the payload contains a new line character, encode it, as it would make the message log invalid (no new lines allowed for a message)
if (!encoded &&
(payload.contains(ConversionUtils.LINE_SEPARATOR_LINUX)
|| payload.contains(ConversionUtils.LINE_SEPARATOR_MAC)
|| payload.contains(ConversionUtils.LINE_SEPARATOR_WIN)))
{
logger.debug("Message on topic {} contains a new line separator, so it needs to be encoded", message.getTopic());
encoded = true;
}
if (encoded)
{
appendAttribute(logMessage, "encoded", "true");
}
logMessage.append(">");
if (encoded)
{
appendValue(logMessage, Base64.encodeBase64String(message.getRawMessage().getPayload()));
}
else
{
final boolean useCData = XML_CHARS.matcher(payload).find();
if (useCData)
{
appendValue(logMessage, "<![CDATA[" + payload + "]]>");
}
else
{
appendValue(logMessage, payload);
}
}
}
/**
* Appends a single attribute to the log.
*
* @param logMessage The message log to be populated
* @param attributeName The attribute name
* @param attributeValue The attribute value
*/
public static void appendAttribute(final StringBuffer logMessage, final String attributeName, final String attributeValue)
{
logMessage.append(" " + attributeName + "=\"" + attributeValue + "\"");
}
/**
* Appends an element value to the log.
*
* @param logMessage The message log to be populated
* @param value The value to be appended
*/
public static void appendValue(final StringBuffer logMessage, final String value)
{
logMessage.append(value);
}
}