/* * Copyright 2016 Hemika Yasinda Kodikara * * 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.apache.jmeter.protocol.mqtt.sampler; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.protocol.mqtt.client.ClientPool; import org.apache.jmeter.protocol.mqtt.data.objects.Message; import org.apache.jmeter.protocol.mqtt.paho.clients.AsyncClient; import org.apache.jmeter.protocol.mqtt.paho.clients.BaseClient; import org.apache.jmeter.protocol.mqtt.paho.clients.BlockingClient; import org.apache.jmeter.protocol.mqtt.utilities.Constants; import org.apache.jmeter.protocol.mqtt.utilities.Utils; import org.apache.jmeter.samplers.AbstractSampler; import org.apache.jmeter.samplers.Entry; import org.apache.jmeter.samplers.Interruptible; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.testelement.TestStateListener; import org.apache.jmeter.testelement.ThreadListener; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; import org.eclipse.paho.client.mqttv3.MqttException; import java.io.IOException; import java.util.Date; /** * This is MQTT Subscriber sample class. The implementation includes subscriber for MQTT messages with the sample * processing. */ public class SubscriberSampler extends AbstractSampler implements Interruptible, ThreadListener, TestStateListener { private transient BaseClient client; private static final long serialVersionUID = 240L; private static final String lineSeparator = System.getProperty("line.separator"); private MqttException exceptionOccurred = null; private static final String nameLabel = "MQTT Subscriber"; private static final Logger log = LoggingManager.getLoggerForClass(); private transient volatile boolean interrupted = false; private static final String BROKER_URL = "mqtt.broker.url"; private static final String CLIENT_ID = "mqtt.client.id"; private static final String TOPIC_NAME = "mqtt.topic.name"; private static final String CLEAN_SESSION = "mqtt.clean.session"; private static final String KEEP_ALIVE = "mqtt.keep.alive"; private static final String USERNAME = "mqtt.auth.username"; private static final String PASSWORD = "mqtt.auth.password"; private static final String QOS = "mqtt.qos"; private static final String CLIENT_TYPE = "mqtt.client.type"; // Getters public String getBrokerUrl() { return getPropertyAsString(BROKER_URL); } public String getClientId() { return getPropertyAsString(CLIENT_ID); } public String getTopicName() { return getPropertyAsString(TOPIC_NAME); } public boolean isCleanSession() { return getPropertyAsBoolean(CLEAN_SESSION); } public int getKeepAlive() { return getPropertyAsInt(KEEP_ALIVE); } public String getUsername() { return getPropertyAsString(USERNAME); } public String getPassword() { return getPropertyAsString(PASSWORD); } public String getQOS() { return getPropertyAsString(QOS); } public String getClientType() { return getPropertyAsString(CLIENT_TYPE); } public String getNameLabel() { return nameLabel; } // Setters public void setBrokerUrl(String brokerURL) { setProperty(BROKER_URL, brokerURL.trim()); } public void setClientId(String clientID) { setProperty(CLIENT_ID, clientID.trim()); } public void setTopicName(String topicName) { setProperty(TOPIC_NAME, topicName.trim()); } public void setCleanSession(boolean isCleanSession) { setProperty(CLEAN_SESSION, isCleanSession); } public void setKeepAlive(String keepAlive) {setProperty(KEEP_ALIVE, keepAlive);} public void setUsername(String username) { setProperty(USERNAME, username.trim()); } public void setPassword(String password) { setProperty(PASSWORD, password.trim()); } public void setQOS(String qos) { setProperty(QOS, qos.trim()); } public void setClientType(String clientType) { setProperty(CLIENT_TYPE, clientType.trim()); } public SubscriberSampler() { super(); } /** * {@inheritDoc} */ @Override public boolean interrupt() { boolean oldValue = interrupted; interrupted = true; // so we break the loops in SampleWithListener and SampleWithReceive log.debug("Thread ended " + new Date()); try { ClientPool.clearClient(); } catch (IOException e) { e.printStackTrace(); log.error(e.getLocalizedMessage(), e); } return !oldValue; } /** * {@inheritDoc} */ @Override public void testEnded() { log.debug("Thread ended " + new Date()); try { ClientPool.clearClient(); } catch (IOException e) { e.printStackTrace(); log.error(e.getLocalizedMessage(), e); } } /** * {@inheritDoc} */ @Override public void testEnded(String arg0) { testEnded(); } /** * {@inheritDoc} */ @Override public void testStarted() { if (log.isDebugEnabled()) { log.debug("Thread ended " + new Date()); log.debug("MQTT SubscriberSampler: [" + Thread.currentThread().getName() + "], hashCode=[" + hashCode() + "]"); } } /** * {@inheritDoc} */ @Override public void testStarted(String arg0) { testStarted(); } private void logThreadStart() { if (log.isDebugEnabled()) { log.debug("Thread started " + new Date()); log.debug("MQTTSampler: [" + Thread.currentThread().getName() + "], hashCode=[" + hashCode() + "]"); } } /** * {@inheritDoc} */ @Override public void threadStarted() { interrupted = false; logThreadStart(); if (client == null) { try { if (validate()) { initClient(); } else { interrupt(); } } catch (Exception e) { log.error(e.getLocalizedMessage(), e); } } } /** * {@inheritDoc} */ @Override public void threadFinished() { log.debug("Thread ended " + new Date()); try { ClientPool.clearClient(); } catch (IOException e) { e.printStackTrace(); log.error(e.getLocalizedMessage(), e); } } /** * Initializes the MQTT client for subscriber. */ private void initClient() { String brokerURL = getBrokerUrl(); String clientId = getClientId(); String topicName = getTopicName(); boolean isCleanSession = isCleanSession(); int keepAlive = getKeepAlive(); String userName = getUsername(); String password = getPassword(); String clientType = getClientType(); // Generating client ID if empty if (StringUtils.isEmpty(clientId)) { clientId = Utils.UUIDGenerator(); } // Quality int qos = 0; if (Constants.MQTT_AT_MOST_ONCE.equals(getQOS())) { qos = 0; } else if (Constants.MQTT_AT_LEAST_ONCE.equals(getQOS())) { qos = 1; } else if (Constants.MQTT_EXACTLY_ONCE.equals(getQOS())) { qos = 2; } exceptionOccurred = null; try { if (Constants.MQTT_BLOCKING_CLIENT.equals(clientType)) { client = new BlockingClient(brokerURL, clientId, isCleanSession, userName, password, keepAlive); } else if (Constants.MQTT_ASYNC_CLIENT.equals(clientType)) { client = new AsyncClient(brokerURL, clientId, isCleanSession, userName, password, keepAlive); } if (client != null) { client.subscribe(topicName, qos); ClientPool.addClient(client); } } catch (MqttException e) { exceptionOccurred = e; log.error(e.getMessage(), e); } } /** * {@inheritDoc} */ @Override public SampleResult sample(Entry entry) { SampleResult result = new SampleResult(); result.setSampleLabel(getNameLabel()); result.sampleStart(); if (null != exceptionOccurred) { result.setSuccessful(false); result.setResponseMessage("Client is not connected." + lineSeparator + exceptionOccurred.toString()); result.setResponseData(exceptionOccurred.toString().getBytes()); result.sampleEnd(); result.setResponseCode("FAILED"); return result; } Message receivedMessage; while (!interrupted && null != client.getReceivedMessages() && null != client.getReceivedMessageCounter()) { receivedMessage = client.getReceivedMessages().poll(); if (receivedMessage != null) { client.getReceivedMessageCounter().incrementAndGet(); result.sampleEnd(); result.setSuccessful(true); result.setResponseMessage(lineSeparator + "Received " + client.getReceivedMessageCounter().get() + " " + "messages." + lineSeparator + "Current message QOS : " + receivedMessage.getQos() + lineSeparator + "Is current message a duplicate : " + receivedMessage.isDup() + lineSeparator + "Received timestamp of current message : " + receivedMessage.getCurrentTimestamp() + lineSeparator + "Is current message" + " a retained message : " + receivedMessage.isRetained()); result.setBytes(receivedMessage.getPayload().length); result.setResponseData(receivedMessage.getPayload()); result.setResponseCodeOK(); return result; } } result.setSuccessful(false); result.setResponseMessage("Client has been stopped or an error occurred while receiving messages. Received " + " valid messages."); result.sampleEnd(); result.setResponseCode("FAILED"); return result; } /** * Validates parameters * * @return true if valid parameters, else false */ private boolean validate() { if (StringUtils.isBlank(getBrokerUrl())) { log.error("The broker url cannot be empty"); return false; } if (StringUtils.isBlank(getTopicName())) { log.error("The topic name(destination) cannot be empty"); return false; } return true; } }