/* * -----------------------------------------------------------------------\ * PerfCake *   * Copyright (C) 2010 - 2016 the original author or authors. *   * 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 org.perfcake.message.sender; import org.perfcake.PerfCakeException; import org.perfcake.message.Message; import org.perfcake.reporting.MeasurementUnit; import org.perfcake.util.StringTemplate; import org.perfcake.util.Utils; import org.fusesource.mqtt.client.BlockingConnection; import org.fusesource.mqtt.client.MQTT; import org.fusesource.mqtt.client.QoS; import org.fusesource.mqtt.client.Topic; import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; import java.util.Properties; /** * Sends messages to an MQTT endpoint using Fusesource MQTT client. * * @author <a href="mailto:pavel.macik@gmail.com">Pavel Macík</a> * @author <a href="mailto:marvenec@gmail.com">Martin Večeřa</a> */ public class MqttSender extends AbstractSender { /** * MQTT connection. */ private BlockingConnection mqttConnection; /** * Name of topic where to send messages. */ private String topicName = null; /** * MQTT response. */ private org.fusesource.mqtt.client.Message mqttResponse = null; /** * True if and only if we should wait for response. */ private boolean isResponseExpected = false; /** * MQTT connection for sending responses. */ private BlockingConnection mqttResponseConnection = null; /** * Required quality of service. */ private String qos = QoS.EXACTLY_ONCE.name(); /** * MQTT server user name. */ private String userName = null; /** * MQTT server password. */ private String password = null; /** * Where to read the response from. */ private StringTemplate responseTarget = null; /** * Response quality of service. */ private String responseQos = qos; /** * Response server user name. */ private String responseUserName = null; /** * Response server password. */ private String responsePassword = null; @Override public void doInit(Properties messageAttributes) throws PerfCakeException { try { final URI targetUri = new URI(safeGetTarget(messageAttributes)); final String protocol = targetUri.getScheme(); final String host = targetUri.getHost(); final int port = targetUri.getPort(); topicName = targetUri.getPath().substring(1); try { final MQTT mqttClient = new MQTT(); mqttClient.setHost(protocol + "://" + host + ":" + port); mqttClient.setConnectAttemptsMax(0); mqttClient.setReconnectAttemptsMax(0); if (userName != null) { mqttClient.setUserName(userName); } if (password != null) { mqttClient.setPassword(password); } mqttConnection = mqttClient.blockingConnection(); mqttConnection.connect(); if (responseTarget != null) { isResponseExpected = true; final String responseHost; final Integer responsePort; final String responseTopicName; final String safeResponseTarget = messageAttributes == null ? responseTarget.toString() : responseTarget.toString(messageAttributes); final URI responseTargetUri = new URI(safeResponseTarget); if ((responseHost = responseTargetUri.getHost()) != null) { final String responseProtocol = responseTargetUri.getScheme(); responsePort = responseTargetUri.getPort(); responseTopicName = responseTargetUri.getPath().substring(1); final MQTT mqttResponseClient = new MQTT(); mqttResponseClient.setHost(responseProtocol + "://" + responseHost + ":" + responsePort); mqttResponseClient.setConnectAttemptsMax(0); mqttResponseClient.setReconnectAttemptsMax(0); if (responseUserName != null) { mqttResponseClient.setUserName(responseUserName); } if (responsePassword != null) { mqttResponseClient.setPassword(responsePassword); } mqttResponseConnection = mqttResponseClient.blockingConnection(); mqttResponseConnection.connect(); } else { responseTopicName = safeResponseTarget; mqttResponseConnection = mqttConnection; } final Topic[] responseTopic = { new Topic(responseTopicName, QoS.valueOf(responseQos)) }; mqttResponseConnection.subscribe(responseTopic); } } catch (URISyntaxException e) { throw new PerfCakeException("Broker's host or port is invalid", e); } catch (Exception e) { throw new PerfCakeException("Unable to create MQTT connection.", e); } } catch (URISyntaxException e) { throw new PerfCakeException("Invalid target: \"" + getTarget() + "\".", e); } } @Override public void preSend(final Message message, final Properties messageAttributes) throws Exception { super.preSend(message, messageAttributes); mqttResponse = null; } @Override public Serializable doSend(Message message, MeasurementUnit measurementUnit) throws Exception { String response = null; mqttConnection.publish(topicName, message.getPayload().toString().getBytes(Utils.getDefaultEncoding()), QoS.valueOf(qos.toUpperCase()), false); if (isResponseExpected) { mqttResponse = mqttResponseConnection.receive(); if (mqttResponse != null) { response = new String(mqttResponse.getPayload(), Utils.getDefaultEncoding()); } } return response; } @Override public void postSend(final Message message) throws Exception { super.postSend(message); if (mqttResponse != null) { mqttResponse.ack(); } } @Override public void doClose() throws PerfCakeException { try { if (mqttConnection != null) { mqttConnection.disconnect(); } if (mqttResponseConnection != null) { mqttResponseConnection.disconnect(); } } catch (Exception e) { throw new PerfCakeException("Unable to disconnect.", e); } } /** * Gets the required response quality of service. * @return The required response quality of service. */ public String getResponseQos() { return responseQos; } /** * Sets the required response quality of service. * @param responseQos The required response quality of service. * @return Instance of this to support fluent API. */ public MqttSender setResponseQos(final String responseQos) { this.responseQos = responseQos; return this; } /** * Gets where to read the response from. * @return Where to read the response from. */ public String getResponseTarget() { return responseTarget.toString(); } /** * Sets where to read the response from. * @param responseTarget Where to read the response from. * @return Instance of this to support fluent API. */ public MqttSender setResponseTarget(final String responseTarget) { this.responseTarget = new StringTemplate(responseTarget); return this; } /** * Gets the required quality of service. * @return The required quality of service. */ public String getQos() { return qos; } /** * Sets the required quality of service. * @param qos The required quality of service. * @return Instance of this to support fluent API. */ public MqttSender setQos(final String qos) { this.qos = qos; return this; } /** * Gets the MQTT server user name. * @return the MQTT server user name. */ public String getUserName() { return userName; } /** * Sets the MQTT server user name. * @param userName The MQTT server user name. * @return Instance of this to support fluent API. */ public MqttSender setUserName(final String userName) { this.userName = userName; return this; } /** * Gets the MQTT server password. * @return The MQTT server password. */ public String getPassword() { return password; } /** * Sets the MQTT server password. * @param password The MQTT server password. * @return Instance of this to support fluent API. */ public MqttSender setPassword(final String password) { this.password = password; return this; } /** * Gets the response server user name. * @return The response server user name. */ public String getResponseUserName() { return responseUserName; } /** * Sets the tesponse server user name. * @param responseUserName The response server user name. * @return Instance of this to support fluent API. */ public MqttSender setResponseUserName(final String responseUserName) { this.responseUserName = responseUserName; return this; } /** * Gets the response server password. * @return The response server password. */ public String getResponsePassword() { return responsePassword; } /** * Sets the response server password. * @param responsePassword The response server password. * @return Instance of this to support fluent API. */ public MqttSender setResponsePassword(final String responsePassword) { this.responsePassword = responsePassword; return this; } }