/***********************************************************************************
*
* 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.daemon.connectivity;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.baczkowicz.mqttspy.common.generated.MessageLogEnum;
import pl.baczkowicz.mqttspy.common.generated.SubscriptionDetails;
import pl.baczkowicz.mqttspy.connectivity.BaseMqttConnection;
import pl.baczkowicz.mqttspy.connectivity.BaseMqttSubscription;
import pl.baczkowicz.mqttspy.daemon.configuration.generated.DaemonMqttConnectionDetails;
import pl.baczkowicz.mqttspy.logger.MqttMessageLogger;
import pl.baczkowicz.mqttspy.messages.FormattedMqttMessage;
import pl.baczkowicz.spy.common.generated.FormatterDetails;
import pl.baczkowicz.spy.formatting.FormattingManager;
import pl.baczkowicz.spy.messages.MessageIdGenerator;
import pl.baczkowicz.spy.scripts.BaseScriptManager;
/**
* Callback handler for the MQTT connection.
*/
public class MqttCallbackHandler implements MqttCallback
{
/** Diagnostic logger. */
private final static Logger logger = LoggerFactory.getLogger(MqttCallbackHandler.class);
/** Stores all received messages, so that we don't block the receiving thread. */
private final Queue<FormattedMqttMessage> messageQueue = new LinkedBlockingQueue<FormattedMqttMessage>();
/** Logs all received messages (if configured). */
private final MqttMessageLogger messageLogger;
/** The connection. */
private final BaseMqttConnection connection;
/** Connection details. */
private final DaemonMqttConnectionDetails connectionSettings;
/** Subscription details (as configured). */
private final Map<String, SubscriptionDetails> subscriptionsDetails = new HashMap<String, SubscriptionDetails>();
/** Script manager - for running subscription scripts. */
private final BaseScriptManager scriptManager;
private final FormattingManager formattingManager;
/** Message ID. */
//private long currentId = 1;
/**
* Creates a MqttCallbackHandler.
*
* @param connection The connection to be used
* @param connectionSettings Connection's details
* @param scriptManager Script manager - for running subscription scripts
*/
public MqttCallbackHandler(final BaseMqttConnection connection, final DaemonMqttConnectionDetails connectionSettings,
final BaseScriptManager scriptManager)
{
this.connection = connection;
this.connectionSettings = connectionSettings;
this.scriptManager = scriptManager;
this.formattingManager = new FormattingManager(scriptManager);
this.messageLogger = new MqttMessageLogger("0", messageQueue, connectionSettings.getMessageLog(), false, 10);
for (final SubscriptionDetails subscriptionDetails : connectionSettings.getSubscription())
{
this.subscriptionsDetails.put(subscriptionDetails.getTopic(), subscriptionDetails);
}
new Thread(messageLogger).start();
}
/**
* Handles connection loss.
*
* @param cause Reason of the connection loss
*/
public void connectionLost(final Throwable cause)
{
logger.error("Connection lost", cause);
connection.connectionLost(cause);
}
/**
* Handles received messages.
*
* @param topic Topic on which the message has been received
* @param message The received message
*/
public void messageArrived(final String topic, final MqttMessage message)
{
logger.debug("[{}] Received message on topic \"{}\". Payload = \"{}\"", messageQueue.size(), topic, new String(message.getPayload()));
final long newId = MessageIdGenerator.getNewId();
final FormattedMqttMessage receivedMessage = new FormattedMqttMessage(newId, topic, message, connection);
// Check matching subscriptions
final List<String> matchingSubscriptions = connection.getTopicMatcher().getMatchingSubscriptions(receivedMessage.getTopic());
receivedMessage.setMatchingSubscriptionTopics(matchingSubscriptions);
logger.trace("Matching subscriptions for message on {} = {}", receivedMessage.getTopic(), matchingSubscriptions);
// Before scripts
if (connectionSettings.getMessageLog().isLogBeforeScripts())
{
// Log a copy, so that it cannot be modified
logMessage(new FormattedMqttMessage(receivedMessage));
}
// Format the message if configured
final FormattedMqttMessage formattedMessage = new FormattedMqttMessage(newId, topic, message, connection);
if (connectionSettings.getFormatter() != null)
{
formattingManager.formatMessage(formattedMessage, (FormatterDetails) connectionSettings.getFormatter());
}
for (final String matchingSubscriptionTopic : matchingSubscriptions)
{
// If configured, run scripts for the matching subscriptions
final BaseMqttSubscription subscription = connection.getMqttSubscriptionForTopic(matchingSubscriptionTopic);
if (subscription.isScriptActive())
{
scriptManager.runScriptWithReceivedMessage(subscription.getScript(), receivedMessage);
}
// Store the message (e.g. to be used by test cases; not needed for logging and general scripts)
if (subscription.getStore() != null)
{
subscription.getStore().messageReceived(formattedMessage);
}
}
// After scripts
if (!connectionSettings.getMessageLog().isLogBeforeScripts())
{
logMessage(receivedMessage);
}
}
/**
* Adds the message to the 'to be logged' queue.
*
* @param receivedMessage The received message
*/
public void logMessage(final FormattedMqttMessage receivedMessage)
{
// Add the received message to queue for logging
if (!MessageLogEnum.DISABLED.equals(connectionSettings.getMessageLog().getValue()))
{
messageQueue.add(receivedMessage);
}
}
/**
* Handles completion of message delivery.
*
* @param token Delivery token
*/
public void deliveryComplete(final IMqttDeliveryToken token)
{
logger.trace("Delivery complete for {}", token.getMessageId());
}
/**
* Stops the message logger.
*/
public void stop()
{
messageLogger.stop();
}
}