├── .gitignore ├── images ├── Message.png ├── Receive.png ├── Flag_Header.png ├── MQTT_Publisher.png ├── MQTT_Subscriber.png ├── Publisher_Text.png ├── Publisher_result.png ├── Random_Byte_Array.png ├── Subscriber_result.png ├── Flag_Header_Example.png ├── Publisher_Subscriber.png ├── Main_Interface_Jmeter.png ├── Publisher_fixed_value.png ├── One_connection_per_topic.png └── Publisher_generated_value.png ├── ressource ├── cacert.jks ├── client.p12 └── mqtt_res.properties ├── script ├── stopgetmetrics.sh ├── getmetrics.sh ├── generate.sh ├── execute.sh └── TestPlan.tmpl.jmx ├── src └── main │ └── java │ ├── org │ └── apache │ │ └── jmeter │ │ └── protocol │ │ └── mqtt │ │ ├── client │ │ ├── CallbackforDisconnect.java │ │ ├── CallbackforUnsubscribe.java │ │ ├── CallbackforSubscribe.java │ │ ├── SSLUtil.java │ │ ├── ListenerforSubscribe.java │ │ ├── CallbackforConnect.java │ │ ├── MqttSubscriber.java │ │ └── MqttPublisher.java │ │ ├── sampler │ │ ├── BaseMQTTSampler.java │ │ ├── SubscriberSampler.java │ │ └── PublisherSampler.java │ │ └── control │ │ └── gui │ │ ├── MQTTSubscriberGui.java │ │ └── MQTTPublisherGui.java │ └── net │ └── xmeter │ ├── emqtt │ └── samplers │ │ ├── DataEntry.java │ │ ├── Constants.java │ │ ├── Util.java │ │ ├── DataEntryUtil.java │ │ ├── ConnectionSampler.java │ │ ├── PublishSampler.java │ │ └── SubscriptionSampler.java │ └── functions │ └── PayloadFunc.java ├── repack_jmeter_core.xml ├── Build_JMeter_MQTT.xml ├── pom.xml ├── README.md └── LICENCE /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings/ 4 | .idea/ 5 | .metadata/ 6 | /target/ 7 | -------------------------------------------------------------------------------- /images/Message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Message.png -------------------------------------------------------------------------------- /images/Receive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Receive.png -------------------------------------------------------------------------------- /ressource/cacert.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/ressource/cacert.jks -------------------------------------------------------------------------------- /ressource/client.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/ressource/client.p12 -------------------------------------------------------------------------------- /images/Flag_Header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Flag_Header.png -------------------------------------------------------------------------------- /images/MQTT_Publisher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/MQTT_Publisher.png -------------------------------------------------------------------------------- /images/MQTT_Subscriber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/MQTT_Subscriber.png -------------------------------------------------------------------------------- /images/Publisher_Text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Publisher_Text.png -------------------------------------------------------------------------------- /images/Publisher_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Publisher_result.png -------------------------------------------------------------------------------- /images/Random_Byte_Array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Random_Byte_Array.png -------------------------------------------------------------------------------- /images/Subscriber_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Subscriber_result.png -------------------------------------------------------------------------------- /images/Flag_Header_Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Flag_Header_Example.png -------------------------------------------------------------------------------- /images/Publisher_Subscriber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Publisher_Subscriber.png -------------------------------------------------------------------------------- /images/Main_Interface_Jmeter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Main_Interface_Jmeter.png -------------------------------------------------------------------------------- /images/Publisher_fixed_value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Publisher_fixed_value.png -------------------------------------------------------------------------------- /images/One_connection_per_topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/One_connection_per_topic.png -------------------------------------------------------------------------------- /images/Publisher_generated_value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XMeterSaaSService/mqtt-jmeter/HEAD/images/Publisher_generated_value.png -------------------------------------------------------------------------------- /script/stopgetmetrics.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pid_cpu=$(cat pid_cpu.log) 3 | kill $pid_cpu 4 | pid_memo=$(cat pid_memo.log) 5 | kill $pid_memo 6 | pid_io=$(cat pid_io.log) 7 | kill $pid_io 8 | echo "Killed getmetrics process whose pid $pid_cpu $pid_memo $pid_io" 9 | rm pid_cpu.log pid_memo.log pid_io.log 10 | -------------------------------------------------------------------------------- /script/getmetrics.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sar -u 1 > cpuload.log & 4 | pid_cpu=$! 5 | echo $pid_cpu > pid_cpu.log 6 | sar -r 1 > memo.log & 7 | pid_memo=$! 8 | echo $pid_memo > pid_memo.log 9 | sar -b 1 > io.log & 10 | pid_io=$! 11 | echo $pid_io > pid_io.log 12 | echo "Getting metrics with process id : $pid_cpu $pid_memo $pid_io" 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/client/CallbackforDisconnect.java: -------------------------------------------------------------------------------- 1 | package org.apache.jmeter.protocol.mqtt.client; 2 | 3 | import org.fusesource.mqtt.client.Callback; 4 | 5 | public class CallbackforDisconnect implements Callback { 6 | 7 | @Override 8 | public void onSuccess(Void value) { 9 | System.out.println("Disconnect sucessfully "); 10 | } 11 | 12 | @Override 13 | public void onFailure(Throwable value) { 14 | System.out.println("Did not disconnect sucessfully "); 15 | System.out.println(value); 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/client/CallbackforUnsubscribe.java: -------------------------------------------------------------------------------- 1 | package org.apache.jmeter.protocol.mqtt.client; 2 | 3 | import org.fusesource.mqtt.client.Callback; 4 | 5 | public class CallbackforUnsubscribe implements Callback { 6 | 7 | @Override 8 | public void onSuccess(Void value) { 9 | System.out.println("Unsubscribe sucessfully "); 10 | 11 | } 12 | 13 | @Override 14 | public void onFailure(Throwable value) { 15 | System.out.println("Did not unsubscribe sucessfully "); 16 | System.out.println(value); 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/client/CallbackforSubscribe.java: -------------------------------------------------------------------------------- 1 | package org.apache.jmeter.protocol.mqtt.client; 2 | 3 | import org.fusesource.mqtt.client.Callback; 4 | 5 | public class CallbackforSubscribe implements Callback { 6 | 7 | 8 | 9 | 10 | @Override 11 | public void onSuccess(byte[] qoses) { 12 | System.out.println("Subscribe sucessfully "); 13 | } 14 | 15 | @Override 16 | public void onFailure(Throwable value) { 17 | System.out.println("Did not subscribe"); 18 | System.out.println(value); 19 | 20 | 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/xmeter/emqtt/samplers/DataEntry.java: -------------------------------------------------------------------------------- 1 | package net.xmeter.emqtt.samplers; 2 | 3 | public class DataEntry { 4 | private int dockerNum; 5 | private int threadNum; 6 | private int loopCount; 7 | private long time; 8 | private long elapsedTime; 9 | 10 | public int getDockerNum() { 11 | return dockerNum; 12 | } 13 | public void setDockerNum(int dockerNum) { 14 | this.dockerNum = dockerNum; 15 | } 16 | public int getThreadNum() { 17 | return threadNum; 18 | } 19 | public void setThreadNum(int threadNum) { 20 | this.threadNum = threadNum; 21 | } 22 | public int getLoopCount() { 23 | return loopCount; 24 | } 25 | public void setLoopCount(int loopCount) { 26 | this.loopCount = loopCount; 27 | } 28 | public long getTime() { 29 | return time; 30 | } 31 | public void setTime(long time) { 32 | this.time = time; 33 | } 34 | public long getElapsedTime() { 35 | return elapsedTime; 36 | } 37 | public void setElapsedTime(long elapsedTime) { 38 | this.elapsedTime = elapsedTime; 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/xmeter/emqtt/samplers/Constants.java: -------------------------------------------------------------------------------- 1 | package net.xmeter.emqtt.samplers; 2 | 3 | public interface Constants { 4 | public static final String SERVER = "SERVER"; 5 | public static final String PORT = "PORT"; 6 | public static final String KEEP_ALIVE = "KEEP_ALIVE"; 7 | public static final String CLIENT_ID_PREFIX = "CLIENT_ID_PREFIX"; 8 | public static final String CONN_TIMEOUT = "CONN_TIMEOUT"; 9 | 10 | public static final String CONN_ELAPSED_TIME = "CONN_ELAPSED_TIME"; 11 | public static final String CONN_CLIENT_AUTH = "CONN_CLIENT_AUTH"; 12 | 13 | public static final String TOPIC_NAME = "TOPIC_NAME"; 14 | public static final String QOS_LEVEL = "QOS_LEVEL"; 15 | public static final String PAYLOAD_SIZE = "PAYLOAD_SIZE"; 16 | 17 | public static final String TIME_STAMP = "TIME_STAMP"; 18 | public static final String TIME_STAMP_SEP_FLAG = "xm_ts"; 19 | 20 | public static final String DEBUG_RESPONSE = "DEBUG_RESPONSE"; 21 | 22 | public static final int QOS_0 = 0; 23 | public static final int QOS_1 = 1; 24 | public static final int QOS_2 = 2; 25 | 26 | public static final int MAX_CLIENT_ID_LENGTH = 23; 27 | } 28 | -------------------------------------------------------------------------------- /script/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | client_id=$(hostname -I) #client id of publisher 3 | target="tcp:\/\/10.0.0.7:1883" # target for testing 4 | loop_count=1 5 | ramp_time=1 6 | number_sample=1 7 | i=0 # loop count number 8 | step=200 # step for number of clients 9 | for ((c=100;c<=10000;c=c+$step)) 10 | do 11 | ANUMCLIENT[$i]=$c; 12 | i=$i+1; 13 | done 14 | AQOS=(0 1 2) 15 | ARETAIN=(true false) 16 | qos[0]=mqtt_at_most_once 17 | qos[1]=mqtt_at_least_once 18 | qos[2]=mqtt_extactly_once 19 | if [ -d "TestPlans" ]; then 20 | rm -r TestPlans 21 | fi 22 | mkdir TestPlans 23 | for NUMCLIENT in ${ANUMCLIENT[*]} 24 | do 25 | for QOS in ${AQOS[*]} 26 | do 27 | for RETAIN in ${ARETAIN[*]} 28 | do 29 | 30 | SUFFIX=$NUMCLIENT.$QOS.$RETAIN 31 | echo "generating testplan.$SUFFIX.jmx" 32 | sed "s/__NUMCLIENT__/$NUMCLIENT/g" TestPlan.tmpl.jmx | sed "s/__QOS__/${qos[$QOS]}/g" | sed "s/__RETAIN__/$RETAIN/g" | sed "s/__RAMP_TIME__/$ramp_time/g" | sed "s/__LOOP_COUNT__/$loop_count/g" | sed "s/__NUMBER_SAMPLE__/$number_sample/g"| sed "s/__CLIENT_ID__/$client_id/g" | sed "s/__URL__/$target/g" > TestPlans/testplan.$SUFFIX.jmx 33 | done 34 | done 35 | done 36 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/client/SSLUtil.java: -------------------------------------------------------------------------------- 1 | package org.apache.jmeter.protocol.mqtt.client; 2 | 3 | import java.security.SecureRandom; 4 | import java.security.cert.X509Certificate; 5 | 6 | import javax.net.ssl.SSLContext; 7 | import javax.net.ssl.TrustManager; 8 | import javax.net.ssl.X509TrustManager; 9 | 10 | import org.apache.jorphan.logging.LoggingManager; 11 | import org.apache.log.Logger; 12 | 13 | public class SSLUtil { 14 | private static final Logger log = LoggingManager.getLoggerForClass(); 15 | public static SSLContext getContext(){ 16 | try { 17 | SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); 18 | // set up a TrustManager that trusts everything 19 | sslContext.init(null, new TrustManager[] { new X509TrustManager() { 20 | public X509Certificate[] getAcceptedIssuers() { 21 | return null; 22 | } 23 | 24 | public void checkClientTrusted(X509Certificate[] certs, String authType) { 25 | } 26 | 27 | public void checkServerTrusted(X509Certificate[] certs, String authType) { 28 | } 29 | } }, new SecureRandom()); 30 | return sslContext; 31 | } catch(Exception ex) { 32 | log.error(ex.getMessage(), ex); 33 | } 34 | return null; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/client/ListenerforSubscribe.java: -------------------------------------------------------------------------------- 1 | package org.apache.jmeter.protocol.mqtt.client; 2 | 3 | 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | import java.util.concurrent.atomic.AtomicLong; 6 | 7 | import org.fusesource.hawtbuf.Buffer; 8 | import org.fusesource.hawtbuf.UTF8Buffer; 9 | import org.fusesource.mqtt.client.Listener; 10 | 11 | public class ListenerforSubscribe implements Listener { 12 | 13 | public static AtomicLong count= new AtomicLong(0); 14 | private AtomicInteger size = new AtomicInteger(0); 15 | private Throwable exception; 16 | @Override 17 | public void onConnected() { 18 | System.out.println("Subscriber is listening"); 19 | 20 | } 21 | 22 | @Override 23 | public void onDisconnected() { 24 | System.out.println("Subscriber disabled listening"); 25 | 26 | } 27 | 28 | @Override 29 | public void onPublish(UTF8Buffer topic, Buffer body, Runnable ack) { 30 | String message = new String(body.getData()); 31 | //System.out.println("Received: " + message); 32 | size.addAndGet(message.length()); 33 | count.getAndIncrement(); 34 | ack.run(); 35 | } 36 | 37 | @Override 38 | public void onFailure(Throwable value) { 39 | System.out.println("Subscriber couldn't set up listener"); 40 | this.exception = value; 41 | System.out.println(value); 42 | } 43 | 44 | public Throwable getException() { 45 | return exception; 46 | } 47 | 48 | public int getSize() { 49 | return size.getAndSet(0); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/client/CallbackforConnect.java: -------------------------------------------------------------------------------- 1 | package org.apache.jmeter.protocol.mqtt.client; 2 | 3 | import org.fusesource.mqtt.client.Callback; 4 | import org.fusesource.mqtt.client.CallbackConnection; 5 | import org.fusesource.mqtt.client.QoS; 6 | import org.fusesource.mqtt.client.Topic; 7 | 8 | public class CallbackforConnect implements Callback { 9 | 10 | private String topics; 11 | private CallbackConnection connection; 12 | private CallbackforSubscribe cbs; 13 | private QoS qos; 14 | private int size; 15 | 16 | public CallbackforConnect(String topics, CallbackConnection connection, 17 | CallbackforSubscribe cbs, QoS qos, int size) { 18 | super(); 19 | this.topics = topics; 20 | this.connection = connection; 21 | this.cbs = cbs; 22 | this.qos = qos; 23 | this.size=size; 24 | } 25 | 26 | @Override 27 | public void onSuccess(Void value) { 28 | 29 | if(size==1) { 30 | Topic[] Tp = new Topic[1]; 31 | Tp[0] = new Topic(topics, qos); 32 | connection.subscribe(Tp, cbs); 33 | System.out.println("Connect sucessfully with only one topic " + topics); 34 | } 35 | else if (size>1){ 36 | String[] topicArray = topics.split("\\s*,\\s*"); 37 | Topic[] Tp = new Topic[topicArray.length]; 38 | for (int i = 0; i < topicArray.length; i++) 39 | Tp[i] = new Topic(topicArray[i], qos); 40 | connection.subscribe(Tp, cbs); 41 | System.out.println("Connect sucessfully with these topics " + topics); 42 | } 43 | 44 | 45 | } 46 | 47 | @Override 48 | public void onFailure(Throwable value) { 49 | System.out.println(value); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/net/xmeter/functions/PayloadFunc.java: -------------------------------------------------------------------------------- 1 | package net.xmeter.functions; 2 | 3 | import java.security.SecureRandom; 4 | import java.util.Collection; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | 8 | import org.apache.jmeter.engine.util.CompoundVariable; 9 | import org.apache.jmeter.functions.AbstractFunction; 10 | import org.apache.jmeter.functions.InvalidVariableException; 11 | import org.apache.jmeter.samplers.SampleResult; 12 | import org.apache.jmeter.samplers.Sampler; 13 | 14 | public class PayloadFunc extends AbstractFunction{ 15 | private static final List desc = new LinkedList(); 16 | private int MIN_PARA_COUNT = 1; 17 | private int MAX_PARA_COUNT = 1; 18 | 19 | static { 20 | desc.add("Get a random payload with specified length."); 21 | } 22 | //function名称 23 | private static final String KEY = "__PayloadFunc"; 24 | 25 | private SecureRandom random = new SecureRandom(); 26 | private static char[] seeds = "abcdefghijklmnopqrstuvwxmy0123456789".toCharArray(); 27 | private Object[] values; 28 | 29 | public List getArgumentDesc() { 30 | return desc; 31 | } 32 | 33 | @Override 34 | public String execute(SampleResult arg0, Sampler arg1) throws InvalidVariableException { 35 | int max = new Integer(((CompoundVariable) values[0]).execute().trim()); 36 | StringBuffer res = new StringBuffer(); 37 | for(int i = 0; i < max; i++) { 38 | res.append(seeds[random.nextInt(seeds.length - 1)]); 39 | } 40 | return res.toString(); 41 | } 42 | 43 | @Override 44 | public String getReferenceKey() { 45 | return KEY; 46 | } 47 | 48 | @Override 49 | public void setParameters(Collection parameters) throws InvalidVariableException { 50 | checkParameterCount(parameters, MIN_PARA_COUNT, MAX_PARA_COUNT); 51 | values = parameters.toArray(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /repack_jmeter_core.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Adding MQTT plugin resources... 12 | 13 | 14 | 15 | 16 | 17 | Unzip original ApacheJMeter_core.jar 18 | 19 | 20 | 21 | 22 | 23 | 24 | Creating new ApacheJMeter_core.jar file 25 | 26 | 27 | 28 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /Build_JMeter_MQTT.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Handling file ${jmeter} 26 | 27 | The property file_downloaded: ${file_downloaded} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Unzip original ApacheJMeter_core.jar 45 | 46 | 47 | Adding MQTT plugin resources... 48 | 49 | 50 | 51 | 52 | Creating new ApacheJMeter_core.jar file 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/main/java/net/xmeter/emqtt/samplers/Util.java: -------------------------------------------------------------------------------- 1 | package net.xmeter.emqtt.samplers; 2 | 3 | import java.io.InputStream; 4 | import java.security.KeyStore; 5 | import java.security.SecureRandom; 6 | import java.security.cert.X509Certificate; 7 | import java.util.UUID; 8 | 9 | import javax.net.ssl.SSLContext; 10 | import javax.net.ssl.TrustManager; 11 | import javax.net.ssl.X509TrustManager; 12 | 13 | import org.apache.http.conn.ssl.TrustSelfSignedStrategy; 14 | import org.apache.http.ssl.SSLContexts; 15 | 16 | public class Util implements Constants { 17 | 18 | private static SecureRandom random = new SecureRandom(); 19 | private static char[] seeds = "abcdefghijklmnopqrstuvwxmy0123456789".toCharArray(); 20 | 21 | public static String generateClientId(String prefix) { 22 | int leng = prefix.length(); 23 | int postLeng = MAX_CLIENT_ID_LENGTH - leng; 24 | UUID uuid = UUID.randomUUID(); 25 | String string = uuid.toString().replace("-", ""); 26 | String post = string.substring(0, postLeng); 27 | return prefix + post; 28 | } 29 | 30 | public static SSLContext getContext(boolean clientAuth) throws Exception { 31 | if (!clientAuth) { 32 | SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); 33 | sslContext.init(null, new TrustManager[] { new X509TrustManager() { 34 | public X509Certificate[] getAcceptedIssuers() { 35 | return null; 36 | } 37 | 38 | public void checkClientTrusted(X509Certificate[] certs, String authType) { 39 | } 40 | 41 | public void checkServerTrusted(X509Certificate[] certs, String authType) { 42 | } 43 | } }, new SecureRandom()); 44 | 45 | return sslContext; 46 | } else { 47 | String CA_KEYSTORE_PASS = "123456"; 48 | String CLIENT_KEYSTORE_PASS = "123456"; 49 | 50 | InputStream is_cacert = Util.class.getResourceAsStream("/cacert.jks"); 51 | InputStream is_client = Util.class.getResourceAsStream("/client.p12"); 52 | 53 | KeyStore tks = KeyStore.getInstance(KeyStore.getDefaultType()); // jks 54 | tks.load(is_cacert, CA_KEYSTORE_PASS.toCharArray()); 55 | 56 | KeyStore cks = KeyStore.getInstance("PKCS12"); 57 | cks.load(is_client, CLIENT_KEYSTORE_PASS.toCharArray()); 58 | 59 | SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(tks, new TrustSelfSignedStrategy()) // use it to customize 60 | .loadKeyMaterial(cks, CLIENT_KEYSTORE_PASS.toCharArray()) // load client certificate 61 | .build(); 62 | return sslContext; 63 | } 64 | } 65 | 66 | public static String generatePayload(int size) { 67 | StringBuffer res = new StringBuffer(); 68 | for(int i = 0; i < size; i++) { 69 | res.append(seeds[random.nextInt(seeds.length - 1)]); 70 | } 71 | return res.toString(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /ressource/mqtt_res.properties: -------------------------------------------------------------------------------- 1 | ############################################################ MQTT ############################################################ 2 | mqtt_clean_session= Clean Session 3 | mqtt_suffix_length= Length 4 | mqtt_suffix_client_id= Random Suffix 5 | mqtt_publisher=MQTT Publisher 6 | mqtt_provider_url=Provider URL 7 | mqtt_topic=List topic 8 | mqtt_dest_setup=Setup 9 | mqtt_dest_setup_static=At startup 10 | mqtt_dest_setup_dynamic=Each sample 11 | mqtt_use_auth=Use Authorization? 12 | mqtt_user=User 13 | mqtt_pwd=Password 14 | mqtt_itertions=Number of samples to aggregate 15 | mqtt_keep_alive=Keep alive timer(s) 16 | mqtt_config=Message source 17 | mqtt_use_file=From file 18 | mqtt_use_random_file=Random File from folder specified below 19 | mqtt_use_text=Textarea 20 | mqtt_message_type=Message Type 21 | mqtt_text_message=Text 22 | mqtt_map_message=Map Message 23 | mqtt_object_message=Object Message 24 | mqtt_bytes_message=Bytes Message 25 | mqtt_file=File 26 | mqtt_random_file=Random folder containing files ending with .dat for bytes messages, .txt or .obj for text and Object messages 27 | mqtt_auth_required=Required 28 | mqtt_text_area=Text Message 29 | mqtt_subscriber_title=MQTT Subscriber 30 | mqtt_durable_subscription_id=Durable Subscription ID 31 | mqtt_client_id=Client ID 32 | mqtt_read_response=Read Response 33 | mqtt_timeout=Timeout (milliseconds) 34 | mqtt_client_type=Client 35 | mqtt_stop_between_samples=Stop between samples? 36 | mqtt_subscriber_on_message=Use MessageListener.onMessage() 37 | mqtt_subscriber_receive=Use MessageConsumer.receive() 38 | mqtt_separator=Separator 39 | mqtt_use_time_stamp= Add TimeStamp 40 | mqtt_use_number_seq= Add Number Sequence 41 | mqtt_generated_value= Generated Value 42 | mqtt_fixed_value= Fixed Value 43 | mqtt_type_of_generated_value = Type of generated value 44 | mqtt_long_value = Long 45 | mqtt_int_value = Integer 46 | mqtt_double_value = Double 47 | mqtt_float_value = Float 48 | mqtt_string_value = String 49 | mqtt_type_of_fixed_value = Type of fixed value 50 | mqtt_min_value = Min 51 | mqtt_max_value = Max 52 | mqtt_value = Value 53 | mqtt_pseudo_random = Pseudo 54 | mqtt_secure_random = Secure 55 | mqtt_type_random = Type of random 56 | mqtt_seed_random = Seed 57 | mqtt_send_as_retained_msg = Retained 58 | mqtt_qos = Quality of service 59 | mqtt_extactly_once = Exactly once [2] 60 | mqtt_at_least_once = At least once [1] 61 | mqtt_at_most_once = At most once [0] 62 | mqtt_client_id= Client Id 63 | mqtt_plain_text= Plain Text 64 | mqtt_base64= Base64 65 | mqtt_binhex= BinHex 66 | mqtt_binary= Binary 67 | mqtt_message_format= Message Format 68 | mqtt_big_volume= Random Byte Array 69 | mqtt_size_array= Size Array 70 | mqtt_no_encoding= No Encoding 71 | mqtt_round_robin= Round Robin 72 | mqtt_random= Random 73 | mqtt_topic_choice= Strategy 74 | mqtt_connection_per_topic= One connection per topic 75 | ################################################################################################################################ -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 22 | 26 | 27 | 4.0.0 28 | 29 | fr.liglab.erods.jmeter.mqtt 30 | mqtt-jmeter 31 | 0.0.1-SNAPSHOT 32 | Apache JMeter :: MQTT Injector 33 | MQTT Injector for Apache JMeter 34 | 35 | 36 | 3.0 37 | 38 | 39 | 40 | 41 | org.apache.jmeter 42 | ApacheJMeter_core 43 | ${jmeter-version} 44 | provided 45 | 46 | 47 | 48 | org.apache.jmeter 49 | ApacheJMeter_java 50 | ${jmeter-version} 51 | provided 52 | 53 | 54 | 55 | org.fusesource.mqtt-client 56 | mqtt-client 57 | 1.14 58 | 59 | 60 | 61 | 62 | ${project.artifactId} 63 | install 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-compiler-plugin 68 | 2.5.1 69 | 70 | 1.6 71 | 1.6 72 | 73 | 74 | 75 | org.apache.maven.plugins 76 | maven-assembly-plugin 77 | 78 | 79 | jar-with-dependencies 80 | 81 | 82 | 83 | 84 | assemble-all 85 | package 86 | 87 | single 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /script/execute.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | target="azureuser@10.0.0.7" # target of the test 4 | master="azureuser@10.0.0.4" # jmeter master of the test 5 | #Generate number of clients (number of connection for each test) 6 | i=0 # loop count number 7 | step=200 # step for number of clients 8 | for ((c=100;c<=10000;c=c+$step)) 9 | do 10 | ANUMCLIENT[$i]=$c; 11 | i=$i+1; 12 | done 13 | 14 | # Quality of services 15 | AQOS=(0 1 2) 16 | ARETAIN=(true false) 17 | 18 | # Jmeter server (slave ) 19 | JMETERSERVERS=10.0.0.5 20 | 21 | if [ ! -z "$JMETERSERVERS" ] 22 | then 23 | REMOTE="-R $JMETERSERVERS" 24 | fi 25 | #----------------------------------Create Directory-----------------------------------------------# 26 | if [ -d ~/METRICS ] 27 | then 28 | rm -r ~/METRICS 29 | fi 30 | mkdir ~/METRICS ~/METRICS/CPU ~/METRICS/IO ~/METRICS/MEMO ~/METRICS/Logs ~/METRICS/Results ~/METRICS/Summarisers ~/METRICS/Summarisers/Total 31 | 32 | #------------------------------------Run The Test-------------------------------------------------# 33 | 34 | for NUMCLIENT in ${ANUMCLIENT[*]} 35 | do 36 | for QOS in ${AQOS[*]} 37 | do 38 | for RETAIN in ${ARETAIN[*]} 39 | do 40 | SUFFIX=$NUMCLIENT.$QOS.$RETAIN 41 | echo "running testplan.$SUFFIX.jmx" 42 | # Start mesuring the metrics 43 | ssh $target ./getmetrics.sh 44 | #-------------------------------------------------/ 45 | ./../jmeter -n -t TestPlans/testplan.$SUFFIX.jmx -p specific.properties -l ~/METRICS/Results/result.$SUFFIX.jtl -j ~/METRICS/Logs/log.$SUFFIX.log $REMOTE 46 | cat Logs/log.$SUFFIX.log | grep 'Summariser' > ~/METRICS/Summarisers/summariser.$SUFFIX.log 47 | cat Logs/log.$SUFFIX.log | grep 'summary =' >> ~/METRICS/Summarisers/Total/total.$QOS.$RETAIN.log 48 | 49 | #Stop mesuring load at the target and get the result to local machine 50 | 51 | ssh $target ./stopgetmetrics.sh 52 | scp $target:cpuload.log ~/METRICS/CPU/cpuload.$SUFFIX.log 53 | scp $target:memo.log ~/METRICS/MEMO/memo.$SUFFIX.log 54 | scp $target:io.log ~/METRICS/IO/io.$SUFFIX.log 55 | #-------------------------------------------------/ 56 | done 57 | done 58 | done 59 | 60 | #echo Please read 16.7 Reducing resource requirements http://jmeter.apache.org/usermanual/best-practices.html 61 | #echo Please read 2.4.3 2.4.7 http://jmeter.apache.org/usermanual/get-started.html 62 | #echo Please read http://jmeter.apache.org/usermanual/jmeter_distributed_testing_step_by_step.pdf 63 | # 2.4.3 Non-GUI Mode (Command Line mode) 64 | 65 | # For non-interactive testing, you may choose to run JMeter without the GUI. To do so, use the following command options: 66 | 67 | # -n This specifies JMeter is to run in non-gui mode 68 | # -t [name of JMX file that contains the Test Plan]. 69 | # -l [name of JTL file to log sample results to]. 70 | # -j [name of JMeter run log file]. 71 | # -r Run the test in the servers specified by the JMeter property "remote_hosts" 72 | # -R [list of remote servers] Run the test in the specified remote servers 73 | 74 | # The script also lets you specify the optional firewall/proxy server information: 75 | 76 | # -H [proxy server hostname or ip address] 77 | # -P [proxy server port] 78 | 79 | # Example : jmeter -n -t my_test.jmx -l log.jtl -H my.proxy.server -P 8000 80 | 81 | # If the property jmeterengine.stopfail.system.exit is set to true (default is false), then JMeter will invoke System.exit(1) if it cannot stop all threads. Normally this is not necessary. 82 | -------------------------------------------------------------------------------- /src/main/java/net/xmeter/emqtt/samplers/DataEntryUtil.java: -------------------------------------------------------------------------------- 1 | package net.xmeter.emqtt.samplers; 2 | 3 | import java.io.FileOutputStream; 4 | import java.io.IOException; 5 | import java.net.InetAddress; 6 | import java.net.UnknownHostException; 7 | import java.util.ArrayList; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | public class DataEntryUtil { 15 | private List allEntries = new ArrayList(); 16 | private static DataEntryUtil util = new DataEntryUtil(); 17 | private static String filePath = "/home/xmeter/DClogs/"; 18 | private static String fileName = "data_entries.log"; 19 | private Object lock = new Object(); 20 | private static String hostName; 21 | private static String foldName = null; 22 | private DataEntryUtil() { 23 | try { 24 | hostName = (InetAddress.getLocalHost()).getHostName(); 25 | } catch (UnknownHostException e) { 26 | e.printStackTrace(); 27 | } 28 | ExecutorService service = Executors.newSingleThreadExecutor(); 29 | service.submit(new Runnable() { 30 | @Override 31 | public void run() { 32 | while(true) { 33 | try { 34 | synchronized (lock) { 35 | if(allEntries.size() > 0) { 36 | Iterator it = allEntries.iterator(); 37 | StringBuffer contents = new StringBuffer(); 38 | while(it.hasNext()) { 39 | DataEntry entry = it.next(); 40 | contents.append(entry.getElapsedTime()); 41 | contents.append(","); 42 | contents.append(entry.getDockerNum()); 43 | contents.append(","); 44 | contents.append(entry.getThreadNum()); 45 | contents.append(","); 46 | contents.append(entry.getLoopCount()); 47 | contents.append(","); 48 | contents.append(entry.getTime()); 49 | contents.append("\n"); 50 | } 51 | saveToFile(contents.toString()); 52 | allEntries.clear(); 53 | } 54 | } 55 | TimeUnit.SECONDS.sleep(3); 56 | } catch(Exception ex) { 57 | ex.printStackTrace(); 58 | } 59 | } 60 | } 61 | }); 62 | service.shutdown(); 63 | } 64 | 65 | private void saveToFile(String contents) { 66 | FileOutputStream fileOutputStream = null; 67 | try { 68 | if(foldName == null || "".equals(foldName.trim())) { 69 | String fullPath = filePath + hostName + "_" + fileName; 70 | fileOutputStream = new FileOutputStream(fullPath, true); 71 | } else { 72 | fileOutputStream = new FileOutputStream(foldName + hostName + "_" + fileName, true); 73 | } 74 | 75 | fileOutputStream.write(contents.getBytes()); 76 | } catch (Exception e) { 77 | e.printStackTrace(); 78 | } finally { 79 | if(fileOutputStream != null) { 80 | try { 81 | fileOutputStream.close(); 82 | } catch (IOException e) { 83 | e.printStackTrace(); 84 | } 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * 91 | * @param theFullPath: The fullpath of packet log. If the para is null or empty, will use the default path. 92 | * @return 93 | */ 94 | public static DataEntryUtil getInstance(String theFoldName) { 95 | if(theFoldName == null || "".equals(theFoldName.trim())) { 96 | System.out.println("Specified empty data log file name, will use default " + filePath + hostName + "_" + fileName); 97 | } else { 98 | foldName = theFoldName; 99 | } 100 | return util; 101 | } 102 | 103 | public void addDataEntry(DataEntry dataEntry) { 104 | synchronized (lock) { 105 | allEntries.add(dataEntry); 106 | } 107 | } 108 | 109 | public void addDataEntries(List dataEntries) { 110 | synchronized (lock) { 111 | allEntries.addAll(dataEntries); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /script/TestPlan.tmpl.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | continue 16 | 17 | false 18 | __LOOP_COUNT__ 19 | 20 | __NUMCLIENT__ 21 | __RAMP_TIME__ 22 | 1397652808000 23 | 1397652808000 24 | false 25 | 26 | 27 | 28 | 29 | 30 | __URL__ 31 | __TOPIC__ 32 | 33 | 34 | 35 | mqtt_generated_value 36 | __NUMBER_SAMPLE__ 37 | false 38 | __QOS__ 39 | __RETAIN__ 40 | mqtt_int_value 41 | 42 | mqtt_pseudo_random 43 | 9999 44 | 9 45 | mqtt_float_value 46 | 5 47 | false 48 | false 49 | __CLIENT_ID__ 50 | mqtt_no_encoding 51 | UTF-8 52 | 53 | mqtt_round_robin 54 | false 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/main/java/net/xmeter/emqtt/samplers/ConnectionSampler.java: -------------------------------------------------------------------------------- 1 | package net.xmeter.emqtt.samplers; 2 | 3 | import java.text.MessageFormat; 4 | import java.util.concurrent.TimeUnit; 5 | import java.util.concurrent.atomic.AtomicBoolean; 6 | 7 | import org.apache.jmeter.config.Arguments; 8 | import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; 9 | import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; 10 | import org.apache.jmeter.samplers.SampleResult; 11 | import org.apache.log.Priority; 12 | import org.fusesource.mqtt.client.Future; 13 | import org.fusesource.mqtt.client.FutureConnection; 14 | import org.fusesource.mqtt.client.MQTT; 15 | import org.fusesource.mqtt.client.QoS; 16 | import org.fusesource.mqtt.client.Topic; 17 | 18 | public class ConnectionSampler extends AbstractJavaSamplerClient implements Constants{ 19 | private MQTT mqtt = new MQTT(); 20 | private FutureConnection connection = null; 21 | private int elpasedTime; 22 | private static AtomicBoolean sleepFlag = new AtomicBoolean(false); 23 | public ConnectionSampler() { 24 | } 25 | 26 | @Override 27 | public Arguments getDefaultParameters() { 28 | Arguments defaultParameters = new Arguments(); 29 | defaultParameters.addArgument(SERVER, "tcp://localhost"); 30 | defaultParameters.addArgument(PORT, "1883"); 31 | defaultParameters.addArgument(KEEP_ALIVE, "5"); 32 | defaultParameters.addArgument(CLIENT_ID_PREFIX, "conn_"); 33 | defaultParameters.addArgument(CONN_TIMEOUT, "10"); 34 | defaultParameters.addArgument(CONN_ELAPSED_TIME, "60"); 35 | defaultParameters.addArgument(CONN_CLIENT_AUTH, "false"); 36 | return defaultParameters; 37 | } 38 | 39 | @Override 40 | public SampleResult runTest(JavaSamplerContext context) { 41 | String serverAddr = context.getParameter(SERVER); 42 | int port = context.getIntParameter(PORT); 43 | 44 | int keepAlive = context.getIntParameter(KEEP_ALIVE); 45 | SampleResult result = new SampleResult(); 46 | this.elpasedTime = context.getIntParameter(CONN_ELAPSED_TIME); 47 | result.sampleStart(); 48 | try { 49 | 50 | if(serverAddr != null && (serverAddr.trim().toLowerCase().startsWith("ssl://"))) { 51 | mqtt.setSslContext(Util.getContext("true".equals(context.getParameter(CONN_CLIENT_AUTH, "false")))); 52 | } 53 | mqtt.setHost(serverAddr + ":" + port); 54 | mqtt.setKeepAlive((short) keepAlive); 55 | String clientId = Util.generateClientId(context.getParameter(CLIENT_ID_PREFIX)); 56 | mqtt.setClientId(clientId); 57 | mqtt.setConnectAttemptsMax(0); 58 | mqtt.setReconnectAttemptsMax(0); 59 | 60 | connection = mqtt.futureConnection(); 61 | 62 | Future f1 = connection.connect(); 63 | f1.await(context.getIntParameter(CONN_TIMEOUT), TimeUnit.SECONDS); 64 | 65 | Topic[] topics = {new Topic("topic_"+ clientId, QoS.AT_LEAST_ONCE)}; 66 | connection.subscribe(topics); 67 | 68 | result.sampleEnd(); 69 | result.setSuccessful(true); 70 | result.setResponseData("Successful.".getBytes()); 71 | result.setResponseMessage(MessageFormat.format("Connection {0} connected successfully.", connection)); 72 | result.setResponseCodeOK(); 73 | } catch (Exception e) { 74 | getLogger().log(Priority.ERROR, e.getMessage(), e); 75 | result.sampleEnd(); 76 | result.setSuccessful(false); 77 | result.setResponseMessage(MessageFormat.format("Connection {0} connected failed.", connection)); 78 | result.setResponseData("Failed.".getBytes()); 79 | result.setResponseCode("500"); 80 | } 81 | return result; 82 | } 83 | 84 | @Override 85 | public void teardownTest(JavaSamplerContext context) { 86 | try { 87 | if(!sleepFlag.get()) { 88 | TimeUnit.SECONDS.sleep(elpasedTime); 89 | sleepFlag.set(true); 90 | } 91 | 92 | if(this.connection != null) { 93 | this.connection.disconnect(); 94 | getLogger().log(Priority.INFO, MessageFormat.format("The connection {0} disconneted successfully.", connection)); 95 | } 96 | } catch (InterruptedException e) { 97 | getLogger().log(Priority.ERROR, e.getMessage(), e); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/net/xmeter/emqtt/samplers/PublishSampler.java: -------------------------------------------------------------------------------- 1 | package net.xmeter.emqtt.samplers; 2 | 3 | import java.text.MessageFormat; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import org.apache.jmeter.config.Arguments; 7 | import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; 8 | import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; 9 | import org.apache.jmeter.samplers.SampleResult; 10 | import org.apache.log.Priority; 11 | import org.fusesource.mqtt.client.Future; 12 | import org.fusesource.mqtt.client.FutureConnection; 13 | import org.fusesource.mqtt.client.MQTT; 14 | import org.fusesource.mqtt.client.QoS; 15 | 16 | 17 | 18 | public class PublishSampler extends AbstractJavaSamplerClient implements Constants /*, TestStateListener */{ 19 | 20 | private MQTT mqtt = new MQTT(); 21 | private FutureConnection connection = null; 22 | private String serverAddr = null; 23 | private int port = 0; 24 | private int keepAlive = 0; 25 | private String clientId = null; 26 | 27 | private int qos = 0; 28 | private QoS qos_enum; 29 | private int payload_size = 0; 30 | private String payload = null; 31 | private boolean addTimestamp = false; 32 | 33 | @Override 34 | public Arguments getDefaultParameters() { 35 | Arguments defaultParameters = new Arguments(); 36 | defaultParameters.addArgument(SERVER, "tcp://localhost"); 37 | defaultParameters.addArgument(PORT, "1883"); 38 | defaultParameters.addArgument(KEEP_ALIVE, "300"); 39 | defaultParameters.addArgument(CLIENT_ID_PREFIX, "pub_"); 40 | defaultParameters.addArgument(CONN_TIMEOUT, "10"); 41 | defaultParameters.addArgument(CONN_CLIENT_AUTH, "false"); 42 | defaultParameters.addArgument(QOS_LEVEL, String.valueOf(QOS_0)); 43 | defaultParameters.addArgument(TOPIC_NAME, "xmeter"); 44 | defaultParameters.addArgument(PAYLOAD_SIZE, "256"); 45 | defaultParameters.addArgument(TIME_STAMP, "false"); 46 | return defaultParameters; 47 | } 48 | 49 | @Override 50 | public void setupTest(JavaSamplerContext context) { 51 | serverAddr = context.getParameter(SERVER); 52 | port = context.getIntParameter(PORT); 53 | keepAlive = context.getIntParameter(KEEP_ALIVE); 54 | clientId = Util.generateClientId(context.getParameter(CLIENT_ID_PREFIX)); 55 | addTimestamp = Boolean.parseBoolean(context.getParameter(TIME_STAMP)); 56 | 57 | qos = context.getIntParameter(QOS_LEVEL, 0); 58 | if (qos==0) { 59 | qos_enum = QoS.AT_MOST_ONCE; 60 | } else if (qos==1) { 61 | qos_enum = QoS.AT_LEAST_ONCE; 62 | } else if (qos==2) { 63 | qos_enum = QoS.EXACTLY_ONCE; 64 | } 65 | 66 | payload_size = context.getIntParameter(PAYLOAD_SIZE); 67 | payload = Util.generatePayload(payload_size); 68 | 69 | try { 70 | mqtt.setHost(serverAddr + ":" + port); 71 | mqtt.setKeepAlive((short) keepAlive); 72 | if(serverAddr != null && (serverAddr.trim().toLowerCase().startsWith("ssl://"))) { 73 | boolean flag = "true".equals(context.getParameter(CONN_CLIENT_AUTH, "false")); 74 | getLogger().info("****setSslContext: " + flag); 75 | mqtt.setSslContext(Util.getContext(flag)); 76 | } 77 | //To avoid reconnect 78 | mqtt.setConnectAttemptsMax(0); 79 | mqtt.setReconnectAttemptsMax(0); 80 | 81 | mqtt.setClientId(clientId); 82 | 83 | connection = mqtt.futureConnection(); 84 | Future f1 = connection.connect(); 85 | f1.await(context.getIntParameter(CONN_TIMEOUT), TimeUnit.SECONDS); 86 | 87 | } catch (Exception e) { 88 | getLogger().log(Priority.ERROR, e.getMessage(), e); 89 | } 90 | } 91 | 92 | @Override 93 | public SampleResult runTest(JavaSamplerContext context) { 94 | SampleResult result = new SampleResult(); 95 | result.sampleStart(); 96 | 97 | try { 98 | String topicName = context.getParameter(TOPIC_NAME); 99 | String actualPayload = payload; 100 | if(addTimestamp) { 101 | actualPayload = (System.currentTimeMillis() + TIME_STAMP_SEP_FLAG) + payload; 102 | } 103 | Future pub = connection.publish(topicName, actualPayload.getBytes(), qos_enum, false); 104 | pub.await(); 105 | 106 | result.sampleEnd(); 107 | result.setSuccessful(true); 108 | result.setResponseData((MessageFormat.format("Publish Successful by {0}.", clientId)).getBytes()); 109 | result.setResponseMessage(MessageFormat.format("publish successfully via Connection {0}.", connection)); 110 | result.setResponseCodeOK(); 111 | } catch (Exception e) { 112 | getLogger().error(e.getMessage(), e); 113 | result.sampleEnd(); 114 | result.setSuccessful(false); 115 | result.setResponseData((MessageFormat.format("Publish failed by {0}.", clientId)).getBytes()); 116 | result.setResponseMessage(MessageFormat.format("publish failed via Connection {0}", connection)); 117 | result.setResponseCode("500"); 118 | } 119 | return result; 120 | } 121 | 122 | 123 | @Override 124 | public void teardownTest(JavaSamplerContext context) { 125 | if(this.connection != null) { 126 | this.connection.disconnect(); 127 | getLogger().info(MessageFormat.format("The connection {0} disconneted successfully.", connection)); 128 | } 129 | } 130 | 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/sampler/BaseMQTTSampler.java: -------------------------------------------------------------------------------- 1 | /** 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | Copyright 2014 University Joseph Fourier, LIG Laboratory, ERODS Team 20 | 21 | */ 22 | 23 | package org.apache.jmeter.protocol.mqtt.sampler; 24 | import org.apache.jmeter.samplers.AbstractSampler; 25 | import org.apache.jmeter.samplers.Entry; 26 | import org.apache.jmeter.samplers.SampleResult; 27 | import org.apache.jmeter.util.JMeterUtils; 28 | 29 | public abstract class BaseMQTTSampler extends AbstractSampler { 30 | private static final long serialVersionUID = 240L; 31 | private static final String PROVIDER_URL = "mqtt.provider_url"; // $NON-NLS-1$ 32 | private static final String DEST = "mqtt.topic"; // $NON-NLS-1$ 33 | private static final String PRINCIPAL = "mqtt.security_principle"; // $NON-NLS-1$ 34 | private static final String CREDENTIALS = "mqtt.security_credentials"; // $NON-NLS-1$ 35 | private static final String ITERATIONS = "mqtt.iterations"; // $NON-NLS-1$ 36 | private static final String KEEPALIVE = "mqtt.keep_alive"; // $NON-NLS-1$ 37 | private static final String USE_AUTH = "mqtt.authenticate"; // $NON-NLS-1$ 38 | private static final String REQUIRED = JMeterUtils.getResString("mqtt_auth_required"); // $NON-NLS-1$ 39 | 40 | 41 | /** 42 | * Constructor 43 | */ 44 | public BaseMQTTSampler() { 45 | } 46 | 47 | @Override 48 | public SampleResult sample(Entry e) { 49 | return this.sample(); 50 | } 51 | public abstract SampleResult sample() ; 52 | 53 | // ------------- get/set properties ----------------------// 54 | 55 | /** 56 | * 57 | * @param url the provider URL 58 | */ 59 | public void setProviderUrl(String url) { 60 | setProperty(PROVIDER_URL, url); 61 | } 62 | 63 | /** 64 | * 65 | * @return the provider URL 66 | */ 67 | public String getProviderUrl() { 68 | return getPropertyAsString(PROVIDER_URL); 69 | } 70 | /** 71 | * set the destination (topic or queue name) 72 | * 73 | * @param dest the destination 74 | */ 75 | public void setDestination(String dest) { 76 | setProperty(DEST, dest); 77 | } 78 | /** 79 | * return the destination (topic or queue name) 80 | * 81 | * @return the destination 82 | */ 83 | public String getDestination() { 84 | return getPropertyAsString(DEST); 85 | } 86 | 87 | /** 88 | * set the username to login into the mqtt server if needed 89 | * 90 | * @param user 91 | */ 92 | public void setUsername(String user) { 93 | setProperty(PRINCIPAL, user); 94 | } 95 | 96 | /** 97 | * return the username used to login to the mqtt server 98 | * 99 | * @return the username used to login to the mqtt server 100 | */ 101 | public String getUsername() { 102 | return getPropertyAsString(PRINCIPAL); 103 | } 104 | /** 105 | * Set the password to login to the mqtt server 106 | * 107 | * @param pwd 108 | */ 109 | public void setPassword(String pwd) { 110 | setProperty(CREDENTIALS, pwd); 111 | } 112 | 113 | /** 114 | * return the password used to login to the mqtt server 115 | * 116 | * @return the password used to login to the mqtt server 117 | */ 118 | public String getPassword() { 119 | return getPropertyAsString(CREDENTIALS); 120 | } 121 | /** 122 | * set the number of iterations the sampler should aggregate 123 | * 124 | * @param count 125 | */ 126 | public void setIterations(String count) { 127 | setProperty(ITERATIONS, count); 128 | } 129 | 130 | /** 131 | * get the iterations as string 132 | * 133 | * @return the number of iterations 134 | */ 135 | public String getIterations() { 136 | return getPropertyAsString(ITERATIONS); 137 | } 138 | 139 | 140 | public void setKeepAlive(String alive) { 141 | setProperty(KEEPALIVE, alive); 142 | } 143 | 144 | public int getKeepAlive() { 145 | int defaultKeepAlive = 60; // in seconds 146 | String alive = getPropertyAsString(KEEPALIVE); 147 | if(alive == null || "".equals(alive.trim())) { 148 | return defaultKeepAlive; 149 | } 150 | try { 151 | return Integer.parseInt(alive); 152 | }catch(Exception ex) { 153 | return defaultKeepAlive; 154 | } 155 | } 156 | /** 157 | * return the number of iterations as int instead of string 158 | * 159 | * @return the number of iterations as int instead of string 160 | */ 161 | public int getIterationCount() { 162 | return getPropertyAsInt(ITERATIONS); 163 | } 164 | 165 | /** 166 | * Set whether authentication is required for mqtt server 167 | * 168 | * @param useAuth 169 | */ 170 | public void setUseAuth(boolean useAuth) { 171 | setProperty(USE_AUTH, useAuth); 172 | } 173 | /** 174 | * 175 | * 176 | * @return whether mqtt server requires authentication 177 | */ 178 | public boolean isUseAuth() { 179 | final String useAuth = getPropertyAsString(USE_AUTH); 180 | return useAuth.equalsIgnoreCase("true") || useAuth.equals(REQUIRED); // $NON-NLS-1$ 181 | } 182 | 183 | 184 | } 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mqtt-jmeter 2 | =========== 3 | 4 | This is the plugin for Jmeter to Test MQTT protocol. 5 | 6 | **Update:** The live project is moved to https://github.com/emqtt/mqtt-jmeter 7 | 8 | 9 | # Introduction 10 | 11 | The MQTT Plugin in Jmeter is used for the injection testing of MQTT server. It permits the complete 12 | test correspond many scenarios, which depend on type of messages, type of connections. Thanks to it's 13 | interface graphic, the fact of testing mqtt protocol is taken easily. 14 | 15 | 16 | # How to install MQTT plugin in Jmeter 17 | 18 | From the repository: https://github.com/tuanhiep/mqtt-jmeter 19 | Get the source code, go to mqtt-jemeter folder and and use the command maven in terminal (Ubuntu): 20 | 21 | mvn clean install package 22 | 23 | to obtain the file **mqtt-jmeter.jar** in **mqtt-jemeter/target**. 24 | Put the **mqtt-jemeter.jar** in the folder **lib/ext** of Jmeter 25 | (to be downloaded on http://jmeter.apache.org/download_jmeter.cgi ). 26 | 27 | Remind that, it's necessary to update the file **ApacheJMeter_core.jar** in the repository lib/ext of Jmeter. 28 | Update the file messages.properties in the folder :/org/apache/jmeter/resources/ 29 | in **ApacheJMeter_core.jar** by new file messages.properties from 30 | https://github.com/tuanhiep/mqtt-jmeter/tree/master/ressource 31 | 32 | # How to use MQTT plugin in Jmeter 33 | 34 | ## MQTT Publisher 35 | 36 | The interface graphic of Jmeter: 37 | 38 | ![Alt text](images/Main_Interface_Jmeter.png) 39 | 40 | Right-click “Thread” and choose : Add → Sampler → MQTT Publisher 41 | 42 | ![Alt text](images/MQTT_Publisher.png) 43 | 44 | In the principal interface of MQTT Publisher we have the fields: 45 | *Connection Info* 46 | 47 | **Name:** Name of the MQTT Publisher 48 | **Comments:** Your comments 49 | **Provider URL:** the address of MQTT server example: tcp://localhost:1883 50 | **Client Id:** Your Id in the session with MQTT server example: Noel De Palma 51 | **List Topic:** The list of topic's names you want to publish to 52 | The topic's names are separated by a comma "," 53 | For example: List Topic: GRENOBLE/LIG,GRENOBLE/UJF-LIG 54 | This means, you'll publish to 2 topics: GRENOBLE/LIG and GRENOBLE/UJF-LIG 55 | You can choose the option **One connection per topic** : It means that for each topic in the list above, the plugin will create one correspondant 56 | connection. Note that, if the client Id is "Noel De Palma", for example, and you have 2 topics in the list, so the plugin will create 2 connections with 2 Client 57 | Id : "Noel De Palma 0" and "Noel De Palma 1" 58 | The plugin provide two strategies to publish: 59 | **1: Round Robin** : You'll publish to the topics in equal portions and in circular order 60 | **2: Random** : You'll publish to a random topic in the list above 61 | If the list of topic has only one topic, so regardless the strategies, you'll publish to the topic for sure. 62 | **Use Authorization check box:** Necessary in the case the connection needs the username and 63 | password 64 | **User:** Your username 65 | **Password:** Your password 66 | **Number of samples to aggregate:** In other way, the number of messages you want to publish to 67 | the MQTT sever in this MQTT Publisher thread, with the value like the configuration below. 68 | **Message Type:** You can choose : Text, Generated Value, Fixed Value, Random Byte Array (more detail below) 69 | 70 | ![Alt text](images/Publisher_Text.png) 71 | 72 | *Encoding* 73 | 74 | **Message Format** : The type of encoding that you'll encode your data before publish .You can choose Binary Codec, Base64, BinHex or Plain Text 75 | If you choose Plain Text, you can choose 6 types of charsets : UTF-8, UTF-16, US-ASCII,UTF-16LE, UTF-16BE, ISO-8859-1. 76 | Of course, you can choose to no encoding too. 77 | 78 | *Option* 79 | 80 | **Add TimeStamp check box:** Add the timestamps to the message. The timestamps is 8 bytes 81 | **Add Number Sequence check box:** Add the number sequence to the message. Example: if you 82 | publish 100 messages in your session, the message is numbered from 0 to 99. The number sequence 83 | field in the message is 4 bytes. 84 | **Retained check box:** You publish the messages as retained messages or not. The retain flag for an 85 | MQTT message is set to false by default. This means that a broker will not hold onto the message 86 | so that any subscribers arriving after the message was sent will not see the message. By setting 87 | the retain flag, the message is held onto by the broker, so when the late arrivers connect to the 88 | broker or clients create a new subscription they get all the relevant retained messages” 89 | **Quality of service:** Three levels: 90 | 0 : At most once 91 | 1 : At least once 92 | 2 : Exactly once 93 | Each message in MQTT can have its quality of service and retain flag set. The quality of service 94 | advises the code if and how it should ensure the message arrives. There are three options, 0 (At Most Once), 95 | 1 (At Least Once) and 2 (Exactly Once). By default, a new message instance is set to "At Least Once",a Quality 96 | of Service (QoS) of 1, which means the sender will deliver the message at least once and, if there's no acknowledgement 97 | of it, it will keep sending it with a duplicate flag set until an acknowledgement turns up, at which point the 98 | client removes the message from its persisted set of messages. 99 | A QoS of 0, "At Most Once", is the fastest mode, where the client doesn't wait for an 100 | acknowledgement. This means, of course, that if there’s a disconnection or server failure, a message 101 | may be lost. At the other end of the scale is a QoS of 2, "Exactly Once", which uses two pairs of 102 | exchanges, first to transfer the message and then to ensure only one copy has been received and is 103 | being processed. This does make Exactly Once the slower but most reliable QoS setting. 104 | 105 | With MQTT Publisher in Jmeter, three type of messages can be sent (Message Type): 106 | **Text:** The text message, without flag header and the server MQTT can deliver it like a normal 107 | text if you choose *No Encoding* or *Plain Text*. 108 | 109 | ![Alt text](images/Publisher_Text.png) 110 | 111 | ![Alt text](images/Receive.png) 112 | 113 | 1 byte “flag header” for the messages of type: Generated value, Fixed value 114 | 115 | ![Alt text](images/Flag_Header.png) 116 | In the flag header, if one field is set to 1, it means, we use the header in the message. 117 | For example: With this flag header 118 | 119 | ![Alt text](images/Flag_Header_Example.png) 120 | It means that, in the message, we have : 121 | ![Alt text](images/Message.png) 122 | 123 | **Generated Value:** 124 | The generated value can be of type: Integer, Long, Float, Double within the range [Min,Max] . 125 | The type of random can be: Pseudo random or Secure random. In the two cases, we can set the Seed 126 | for the generator. 127 | 128 | ![Alt text](images/Publisher_generated_value.png) 129 | 130 | **Fixed Value:** 131 | The fixed value can be of type: Integer, Long, Float, Double, String within the range [Min,Max]. 132 | 133 | ![Alt text](images/Publisher_fixed_value.png) 134 | **Random Byte Array:** 135 | 136 | The data in form of random byte array with the size array as an input. 137 | For example, if you type 9 in the field size array, so without encoding, time header, number sequence, the message has 9 bytes of content(random data) and 1 byte of flag header 138 | and so, 10 bytes to publish. 139 | The images below show when you publish with option : One Connection Per Topic and the data is type of Random Byte Array. 140 | In the terminal,you see that there are two connections of 2 client Id : "Didier Donsez 0" and "Didier Donsez 1" 141 | 142 | ![Alt text](images/One_connection_per_topic.png) 143 | 144 | ![Alt text](images/Random_Byte_Array.png) 145 | 146 | 147 | For mesuring, thanks to Jmeter, we can add some listeners: 148 | 149 | ![Alt text](images/Publisher_result.png) 150 | 151 | ## MQTT Subscriber 152 | 153 | 154 | ![Alt text](images/MQTT_Subscriber.png) 155 | 156 | 157 | 158 | *Name:* Name of the MQTT Subscriber 159 | *Comments:* Your comments 160 | *Provider URL:* The address of MQTT server 161 | *Client Id:* Your Id in the session 162 | *Topic:* The topic you want to subscribe. 163 | *Use Authorization :* Necessary in the case the connection need username and password 164 | *User:* your username 165 | *Password:* your password 166 | *Number of samples to aggregate:* In other way, the number of message you want to receive from 167 | the topic in one session 168 | *Time out (milliseconds):* Timeout for the connection to receive message from the topic 169 | 170 | ![Alt text](images/Subscriber_result.png) 171 | 172 | 173 | ![Alt text](images/Publisher_Subscriber.png) 174 | 175 | 176 | 177 | Grenoble, France 14/03/2014, 178 | 179 | ERODS Team 180 | 181 | http://www.liglab.fr/erods?lang=fr&var_mode=calcul 182 | 183 | 184 | -------------------------------------------------------------------------------- /src/main/java/net/xmeter/emqtt/samplers/SubscriptionSampler.java: -------------------------------------------------------------------------------- 1 | package net.xmeter.emqtt.samplers; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.text.MessageFormat; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import org.apache.jmeter.config.Arguments; 10 | import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; 11 | import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; 12 | import org.apache.jmeter.samplers.SampleResult; 13 | import org.apache.log.Priority; 14 | import org.fusesource.hawtbuf.Buffer; 15 | import org.fusesource.hawtbuf.UTF8Buffer; 16 | import org.fusesource.mqtt.client.Callback; 17 | import org.fusesource.mqtt.client.CallbackConnection; 18 | import org.fusesource.mqtt.client.Listener; 19 | import org.fusesource.mqtt.client.MQTT; 20 | import org.fusesource.mqtt.client.QoS; 21 | import org.fusesource.mqtt.client.Topic; 22 | 23 | @SuppressWarnings("deprecation") 24 | public class SubscriptionSampler extends AbstractJavaSamplerClient implements Constants { 25 | private MQTT mqtt = new MQTT(); 26 | private CallbackConnection connection = null; 27 | 28 | private boolean connectFailed = false; 29 | private boolean subFailed = false; 30 | private boolean receivedMsgFailed = false; 31 | 32 | private int receivedMessageSize = 0; 33 | private int receivedCount = 0; 34 | private double avgElapsedTime = 0f; 35 | 36 | private boolean debugResponse = false; 37 | private List contents = new ArrayList(); 38 | private boolean useTimestamp = false; 39 | private boolean printFlag = false; 40 | 41 | private Object lock = new Object(); 42 | 43 | private String connAuth = "false"; 44 | private int qos = QOS_0; 45 | 46 | @Override 47 | public Arguments getDefaultParameters() { 48 | Arguments defaultParameters = new Arguments(); 49 | defaultParameters.addArgument(SERVER, "tcp://localhost"); 50 | defaultParameters.addArgument(PORT, "1883"); 51 | defaultParameters.addArgument(KEEP_ALIVE, "300"); 52 | defaultParameters.addArgument(CLIENT_ID_PREFIX, "sub_"); 53 | defaultParameters.addArgument(CONN_TIMEOUT, "10"); 54 | defaultParameters.addArgument(CONN_ELAPSED_TIME, "60"); 55 | defaultParameters.addArgument(CONN_CLIENT_AUTH, "false"); 56 | defaultParameters.addArgument(QOS_LEVEL, String.valueOf(QOS_0)); 57 | defaultParameters.addArgument(DEBUG_RESPONSE, "false"); 58 | defaultParameters.addArgument(TOPIC_NAME, "xmeter"); 59 | defaultParameters.addArgument(TIME_STAMP, "false"); 60 | return defaultParameters; 61 | } 62 | 63 | private void createCallbackConn(String serverAddr, int port, int keepAlive, String clientId, final String topicName) { 64 | try { 65 | mqtt.setHost(serverAddr + ":" + port); 66 | mqtt.setKeepAlive((short) keepAlive); 67 | mqtt.setClientId(clientId); 68 | 69 | if(serverAddr != null && (serverAddr.trim().toLowerCase().startsWith("ssl://"))) { 70 | boolean flag = "true".equals(this.connAuth); 71 | mqtt.setSslContext(Util.getContext(flag)); 72 | } 73 | //To avoid reconnect 74 | mqtt.setConnectAttemptsMax(0); 75 | mqtt.setReconnectAttemptsMax(0); 76 | 77 | connection = mqtt.callbackConnection(); 78 | connection.listener(new Listener() { 79 | @Override 80 | public void onPublish(UTF8Buffer topic, Buffer body, Runnable ack) { 81 | try { 82 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 83 | body.writeTo(baos); 84 | String msg = baos.toString(); 85 | ack.run(); 86 | synchronized (lock) { 87 | if(useTimestamp) { 88 | long now = System.currentTimeMillis(); 89 | int index = msg.indexOf(TIME_STAMP_SEP_FLAG); 90 | if(index == -1 && (!printFlag)) { 91 | getLogger().info("Payload does not include timestamp: " + msg); 92 | printFlag = true; 93 | } else if(index != -1) { 94 | long start = Long.parseLong(msg.substring(0, index)); 95 | long elapsed = now - start; 96 | avgElapsedTime = (avgElapsedTime * receivedCount + elapsed) / (receivedCount + 1); 97 | } 98 | } 99 | if(debugResponse) { 100 | contents.add(msg); 101 | } 102 | receivedMessageSize += msg.length(); 103 | receivedCount++; 104 | 105 | //getLogger().info("Received:" + msg); 106 | } 107 | } catch (IOException e) { 108 | e.printStackTrace(); 109 | } 110 | 111 | } 112 | @Override 113 | public void onFailure(Throwable value) { 114 | connectFailed = true; 115 | connection.kill(null); 116 | } 117 | @Override 118 | public void onDisconnected() { 119 | } 120 | @Override 121 | public void onConnected() { 122 | } 123 | }); 124 | 125 | connection.connect(new Callback() { 126 | @Override 127 | public void onSuccess(Void value) { 128 | Topic[] topics = new Topic[1]; 129 | if(qos == QOS_0) { 130 | topics[0] = new Topic(topicName, QoS.AT_MOST_ONCE); 131 | } else if(qos == QOS_1) { 132 | topics[0] = new Topic(topicName, QoS.AT_LEAST_ONCE); 133 | } else { 134 | topics[0] = new Topic(topicName, QoS.EXACTLY_ONCE); 135 | } 136 | 137 | connection.subscribe(topics, new Callback() { 138 | @Override 139 | public void onSuccess(byte[] value) { 140 | getLogger().info("sub successful: " + new String(value)); 141 | } 142 | @Override 143 | public void onFailure(Throwable value) { 144 | subFailed = true; 145 | connection.kill(null); 146 | } 147 | }); 148 | } 149 | @Override 150 | public void onFailure(Throwable value) { 151 | connectFailed = true; 152 | } 153 | }); 154 | } catch (Exception e) { 155 | getLogger().log(Priority.ERROR, e.getMessage(), e); 156 | } 157 | } 158 | 159 | @Override 160 | public SampleResult runTest(JavaSamplerContext context) { 161 | String serverAddr = context.getParameter(SERVER); 162 | int port = context.getIntParameter(PORT); 163 | int keepAlive = context.getIntParameter(KEEP_ALIVE); 164 | String clientId = Util.generateClientId(context.getParameter(CLIENT_ID_PREFIX)); 165 | String topicName = context.getParameter(TOPIC_NAME); 166 | this.connAuth = context.getParameter(CONN_CLIENT_AUTH, "false"); 167 | String qos = context.getParameter(QOS_LEVEL, String.valueOf(QOS_0)); 168 | this.qos = Integer.parseInt(qos); 169 | this.useTimestamp = Boolean.parseBoolean(context.getParameter(TIME_STAMP, "false")); 170 | 171 | if(connection == null) { 172 | createCallbackConn(serverAddr, port, keepAlive, clientId, topicName); 173 | } 174 | 175 | if("true".equalsIgnoreCase(context.getParameter(DEBUG_RESPONSE, "false"))) { 176 | debugResponse = true; 177 | } 178 | 179 | SampleResult result = new SampleResult(); 180 | 181 | long startTime = System.currentTimeMillis(); 182 | result.sampleStart(); 183 | if(connectFailed) { 184 | return fillFailedResult(result, MessageFormat.format("Connection {0} connected failed.", connection)); 185 | } else if(subFailed) { 186 | return fillFailedResult(result, "Failed to subscribe to topic."); 187 | } else if(receivedMsgFailed) { 188 | return fillFailedResult(result, "Failed to receive message."); 189 | } 190 | synchronized (lock) { 191 | String message = MessageFormat.format("Received {0} of message\n.", receivedCount); 192 | //getLogger().info(message); 193 | StringBuffer content = new StringBuffer(""); 194 | if(debugResponse) { 195 | for(int i = 0; i < contents.size(); i++) { 196 | content.append(contents.get(i) + " \n"); 197 | } 198 | } 199 | //System.out.println(MessageFormat.format("receivedMessageSize {0} with receivedCount {1}.", receivedMessageSize, receivedCount)); 200 | int avgSize = 0; 201 | if(receivedCount != 0) { 202 | avgSize = receivedMessageSize / receivedCount; 203 | } 204 | result = fillOKResult(result, avgSize, message, content.toString()); 205 | result.setEndTime(startTime + (long)this.avgElapsedTime); 206 | result.setSampleCount(receivedCount); 207 | 208 | receivedMessageSize = 0; 209 | receivedCount = 0; 210 | avgElapsedTime = 0f; 211 | contents.clear(); 212 | 213 | return result; 214 | } 215 | } 216 | 217 | private SampleResult fillFailedResult(SampleResult result, String message) { 218 | result.setResponseCode("500"); 219 | result.setSuccessful(false); 220 | result.setResponseMessage(message); 221 | result.setResponseData("Failed.".getBytes()); 222 | result.sampleEnd(); 223 | return result; 224 | } 225 | 226 | private SampleResult fillOKResult(SampleResult result, int size, String message, String contents) { 227 | result.setResponseCode("200"); 228 | result.setSuccessful(true); 229 | result.setResponseMessage(message); 230 | result.setBodySize(size); 231 | result.setBytes(size); 232 | result.setResponseData(contents.getBytes()); 233 | result.sampleEnd(); 234 | return result; 235 | } 236 | 237 | @Override 238 | public void teardownTest(JavaSamplerContext context) { 239 | this.connection.disconnect(new Callback() { 240 | @Override 241 | public void onSuccess(Void value) { 242 | getLogger().info(MessageFormat.format("Connection {0} disconnect successfully.", connection)); 243 | } 244 | @Override 245 | public void onFailure(Throwable value) { 246 | getLogger().info(MessageFormat.format("Connection {0} failed to disconnect.", connection)); 247 | } 248 | }); 249 | } 250 | 251 | 252 | } 253 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/sampler/SubscriberSampler.java: -------------------------------------------------------------------------------- 1 | /** 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | Copyright 2014 University Joseph Fourier, LIG Laboratory, ERODS Team 20 | 21 | */ 22 | 23 | package org.apache.jmeter.protocol.mqtt.sampler; 24 | 25 | import java.util.Date; 26 | 27 | import org.apache.jmeter.config.Arguments; 28 | import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; 29 | import org.apache.jmeter.protocol.mqtt.client.ListenerforSubscribe; 30 | import org.apache.jmeter.protocol.mqtt.client.MqttSubscriber; 31 | import org.apache.jmeter.protocol.mqtt.control.gui.MQTTPublisherGui; 32 | import org.apache.jmeter.samplers.Interruptible; 33 | import org.apache.jmeter.samplers.SampleResult; 34 | import org.apache.jmeter.testelement.TestStateListener; 35 | import org.apache.jmeter.testelement.ThreadListener; 36 | import org.apache.jorphan.logging.LoggingManager; 37 | import org.apache.log.Logger; 38 | 39 | public class SubscriberSampler extends BaseMQTTSampler implements 40 | Interruptible, ThreadListener, TestStateListener { 41 | 42 | private static final long serialVersionUID = 240L; 43 | private static final Logger log = LoggingManager.getLoggerForClass(); 44 | private static final String DURABLE_SUBSCRIPTION_ID = "mqtt.durableSubscriptionId"; // $NON-NLS-1$ 45 | private static final String DURABLE_SUBSCRIPTION_ID_DEFAULT = ""; 46 | private static final String CLIENT_ID = "mqtt.clientId"; // $NON-NLS-1$ 47 | private static final String CLIENT_ID_DEFAULT = ""; // $NON-NLS-1$ 48 | private static final String TIMEOUT = "mqtt.timeout"; // $NON-NLS-1$ 49 | private static final String TIMEOUT_DEFAULT = "30000"; // $NON-NLS-1$ 50 | private static final String QUALITY = "mqtt.quality"; //$NON-NLS-1$ 51 | private static String OneConnectionPerTopic = "mqtt.one_connection_per_topic"; //$NON-NLS-1$ 52 | public transient MqttSubscriber subscriber = null; 53 | private JavaSamplerContext context = null; 54 | private static String Length = "mqtt.suffix.length";//$NON-NLS-1$ 55 | private static String RandomSuffix = "mqtt.random_suffix_client_id";//$NON-NLS-1$ 56 | private static String STRATEGY = "mqtt.strategy"; //$NON-NLS-1$ 57 | private static String CLEAN_SESSION="mqtt.clean.session";//$NON-NLS-1$ 58 | 59 | public SubscriberSampler() { 60 | super(); 61 | } 62 | 63 | public String getCLEANSESSION() { 64 | return getPropertyAsString(CLEAN_SESSION); 65 | } 66 | 67 | public void setCLEANSESSION(boolean cLEANSESSION) { 68 | 69 | if(cLEANSESSION) { 70 | setProperty(CLEAN_SESSION, "true"); 71 | } 72 | else 73 | setProperty(CLEAN_SESSION, "false"); 74 | } 75 | 76 | public void setOneConnectionPerTopic(boolean oneConnectionPerTopic) { 77 | setProperty(OneConnectionPerTopic, oneConnectionPerTopic); 78 | } 79 | 80 | public boolean isOneConnectionPerTopic() { 81 | String perTopic = getPropertyAsString(OneConnectionPerTopic); 82 | if ("TRUE".equalsIgnoreCase(perTopic)) { 83 | return true; 84 | } else { 85 | return false; 86 | } 87 | 88 | } 89 | 90 | public void setQuality(String quality) { 91 | setProperty(QUALITY, quality); 92 | } 93 | 94 | private String getQuality() { 95 | return getPropertyAsString(QUALITY); 96 | } 97 | 98 | public String getSTRATEGY() { 99 | return getPropertyAsString(STRATEGY); 100 | 101 | } 102 | 103 | public void setSTRATEGY(String sTRATEGY) { 104 | setProperty(STRATEGY, sTRATEGY); 105 | 106 | } 107 | 108 | public void setDurableSubscriptionId(String durableSubscriptionId) { 109 | setProperty(DURABLE_SUBSCRIPTION_ID, durableSubscriptionId, 110 | DURABLE_SUBSCRIPTION_ID_DEFAULT); 111 | } 112 | 113 | public void setClientID(String clientId) { 114 | setProperty(CLIENT_ID, clientId, CLIENT_ID_DEFAULT); 115 | 116 | } 117 | 118 | public void setTimeout(String timeout) { 119 | setProperty(TIMEOUT, timeout, TIMEOUT_DEFAULT); 120 | } 121 | 122 | public String getDurableSubscriptionId() { 123 | return getPropertyAsString(DURABLE_SUBSCRIPTION_ID); 124 | } 125 | 126 | public String getClientId() { 127 | return getPropertyAsString(CLIENT_ID, CLIENT_ID_DEFAULT); 128 | } 129 | 130 | public String getTimeout() { 131 | return getPropertyAsString(TIMEOUT, TIMEOUT_DEFAULT); 132 | } 133 | 134 | public void setRandomSuffix(boolean randomSuffix) { 135 | setProperty(RandomSuffix, randomSuffix); 136 | 137 | } 138 | 139 | public boolean useRandomSuffix() { 140 | String randomSuffix = getPropertyAsString(RandomSuffix); 141 | if ("TRUE".equalsIgnoreCase(randomSuffix)) { 142 | return true; 143 | } else { 144 | return false; 145 | } 146 | } 147 | 148 | public void setLength(String length) { 149 | setProperty(Length, length); 150 | } 151 | 152 | public String getLength() { 153 | return getPropertyAsString(Length); 154 | } 155 | 156 | @Override 157 | public boolean interrupt() { 158 | 159 | System.out.println("Hello interrupt"); 160 | System.out.println("Received " + ListenerforSubscribe.count.get() +" messages"); 161 | log.debug("Thread ended " + new Date()); 162 | if (this.subscriber != null) { 163 | try { 164 | this.subscriber.close(context); 165 | 166 | } catch (Exception e) { 167 | e.printStackTrace(); 168 | log.warn(e.getLocalizedMessage(), e); 169 | } 170 | 171 | } 172 | return false; 173 | } 174 | 175 | @Override 176 | public void testEnded() { 177 | System.out.println("Hello testended"); 178 | log.debug("Thread ended " + new Date()); 179 | System.out.println("Received " + ListenerforSubscribe.count.get() +" messages"); 180 | if (this.subscriber != null) { 181 | try { 182 | this.subscriber.close(context); 183 | 184 | } catch (Exception e) { 185 | e.printStackTrace(); 186 | log.warn(e.getLocalizedMessage(), e); 187 | } 188 | 189 | } 190 | } 191 | 192 | @Override 193 | public void testEnded(String arg0) { 194 | testEnded(); 195 | } 196 | 197 | @Override 198 | public void testStarted() { 199 | } 200 | 201 | @Override 202 | public void testStarted(String arg0) { 203 | testStarted(); 204 | } 205 | 206 | // ------------------------------ For Thread---------------------------------// 207 | 208 | private void logThreadStart() { 209 | if (log.isDebugEnabled()) { 210 | log.debug("Thread started " + new Date()); 211 | log.debug("MQTTSampler: [" + Thread.currentThread().getName() 212 | + "], hashCode=[" + hashCode() + "]"); 213 | } 214 | } 215 | 216 | @Override 217 | public void threadStarted() { 218 | logThreadStart(); 219 | if (subscriber == null) { 220 | try { 221 | subscriber = new MqttSubscriber(); 222 | } catch (Exception e) { 223 | log.warn(e.getLocalizedMessage(), e); 224 | } 225 | } 226 | String host = getProviderUrl(); 227 | String list_topic = getDestination(); 228 | String aggregate = "" + getIterationCount(); 229 | String clientId = getClientId(); 230 | String timeout = this.getTimeout(); 231 | Arguments parameters = new Arguments(); 232 | parameters.addArgument("HOST", host); 233 | parameters.addArgument("CLIENT_ID", clientId); 234 | parameters.addArgument("TOPIC", list_topic); 235 | // ------------------------Strategy-----------------------------------// 236 | if (MQTTPublisherGui.ROUND_ROBIN.equals(this.getSTRATEGY())) { 237 | parameters.addArgument("STRATEGY", "ROUND_ROBIN"); 238 | } else { 239 | parameters.addArgument("STRATEGY", "RANDOM"); 240 | } 241 | parameters.addArgument("AGGREGATE", aggregate); 242 | String quality = getQuality(); 243 | parameters.addArgument("QOS", quality); 244 | parameters.addArgument("DURABLE",this.getCLEANSESSION()); 245 | parameters.addArgument("TIMEOUT", timeout); 246 | 247 | if (this.isUseAuth()) { 248 | parameters.addArgument("AUTH", "TRUE"); 249 | parameters.addArgument("USER", getUsername()); 250 | parameters.addArgument("PASSWORD", getPassword()); 251 | } else 252 | parameters.addArgument("AUTH", "FALSE"); 253 | // -------------------------List Topic Or Not-------------------------// 254 | 255 | String[] topics = list_topic.split("\\s*,\\s*"); 256 | if (topics.length <= 1) { 257 | parameters.addArgument("LIST_TOPIC", "FALSE"); 258 | } else { 259 | parameters.addArgument("LIST_TOPIC", "TRUE"); 260 | } 261 | // ------------------------Connection per topic--------------------// 262 | 263 | if (this.isOneConnectionPerTopic()) { 264 | parameters.addArgument("PER_TOPIC", "TRUE"); 265 | } else { 266 | parameters.addArgument("PER_TOPIC", "FALSE"); 267 | } 268 | if (this.useRandomSuffix()) { 269 | parameters.addArgument("RANDOM_SUFFIX", "TRUE"); 270 | parameters.addArgument("SUFFIX_LENGTH", this.getLength()); 271 | } else { 272 | parameters.addArgument("RANDOM_SUFFIX", "FALSE"); 273 | } 274 | parameters.addArgument("LABEL", this.getName()); 275 | parameters.addArgument("KEEP_ALIVE", String.valueOf(this.getKeepAlive())); 276 | 277 | context = new JavaSamplerContext(parameters); 278 | subscriber.setupTest(context); 279 | } 280 | 281 | @Override 282 | public void threadFinished() { 283 | 284 | log.debug("Thread ended " + new Date()); 285 | System.out.println("Received " + ListenerforSubscribe.count.get() +" messages"); 286 | if (this.subscriber != null) { 287 | try { 288 | this.subscriber.close(context); 289 | 290 | } catch (Exception e) { 291 | e.printStackTrace(); 292 | log.warn(e.getLocalizedMessage(), e); 293 | } 294 | 295 | } 296 | } 297 | 298 | @Override 299 | public SampleResult sample() { 300 | 301 | return this.subscriber.runTest(context); 302 | } 303 | 304 | } 305 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/control/gui/MQTTSubscriberGui.java: -------------------------------------------------------------------------------- 1 | /** 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | Copyright 2014 University Joseph Fourier, LIG Laboratory, ERODS Team 20 | 21 | */ 22 | 23 | package org.apache.jmeter.protocol.mqtt.control.gui; 24 | import java.awt.BorderLayout; 25 | import java.awt.Color; 26 | import java.awt.Component; 27 | 28 | import javax.swing.BorderFactory; 29 | import javax.swing.Box; 30 | import javax.swing.BoxLayout; 31 | import javax.swing.JCheckBox; 32 | import javax.swing.JPanel; 33 | import javax.swing.event.ChangeEvent; 34 | import javax.swing.event.ChangeListener; 35 | 36 | import org.apache.jmeter.gui.util.JLabeledRadioI18N; 37 | import org.apache.jmeter.gui.util.VerticalPanel; 38 | import org.apache.jmeter.protocol.mqtt.sampler.SubscriberSampler; 39 | import org.apache.jmeter.samplers.gui.AbstractSamplerGui; 40 | import org.apache.jmeter.testelement.TestElement; 41 | import org.apache.jmeter.util.JMeterUtils; 42 | import org.apache.jorphan.gui.JLabeledPasswordField; 43 | import org.apache.jorphan.gui.JLabeledTextField; 44 | 45 | /** 46 | * This is the GUI for mqtt Subscriber
47 | * 48 | */ 49 | public class MQTTSubscriberGui extends AbstractSamplerGui implements ChangeListener { 50 | 51 | private static final long serialVersionUID = 240L; 52 | public static final String AT_MOST_ONCE = "mqtt_at_most_once";// $NON-NLS-1$ 53 | public static final String EXACTLY_ONCE = "mqtt_extactly_once";// $NON-NLS-1$ 54 | public static final String AT_LEAST_ONCE = "mqtt_at_least_once";// $NON-NLS-1$ 55 | private static final String[] QTYPES_ITEMS = {AT_MOST_ONCE,AT_LEAST_ONCE,EXACTLY_ONCE}; 56 | public static final String ROUND_ROBIN = "mqtt_round_robin";// $NON-NLS-1$ 57 | public static final String RANDOM = "mqtt_random";// $NON-NLS-1$ 58 | private static final String[] TOPIC_CHOICES={ROUND_ROBIN,RANDOM}; 59 | private final JLabeledTextField urlField = new JLabeledTextField(JMeterUtils.getResString("mqtt_provider_url")); // $NON-NLS-1$ 60 | private final JLabeledTextField mqttDestination = new JLabeledTextField(JMeterUtils.getResString("mqtt_topic")); // $NON-NLS-1$ 61 | private final JLabeledTextField mqttUser = new JLabeledTextField(JMeterUtils.getResString("mqtt_user")); // $NON-NLS-1$ 62 | private final JLabeledTextField mqttPwd = new JLabeledPasswordField(JMeterUtils.getResString("mqtt_pwd")); // $NON-NLS-1$ 63 | private final JCheckBox useAuth = new JCheckBox(JMeterUtils.getResString("mqtt_use_auth"), false); //$NON-NLS-1$ 64 | private final JLabeledTextField timeout = new JLabeledTextField(JMeterUtils.getResString("mqtt_timeout")); //$NON-NLS-1$ 65 | private final JLabeledTextField separator = new JLabeledTextField(JMeterUtils.getResString("mqtt_separator")); //$NON-NLS-1$ 66 | private final JCheckBox suffixClientId = new JCheckBox(JMeterUtils.getResString("mqtt_suffix_client_id"),true); // $NON-NLS-1$ 67 | private final JLabeledTextField suffixLength = new JLabeledTextField(JMeterUtils.getResString("mqtt_suffix_length")); //$NON-NLS-1$ 68 | private final JCheckBox connectionPerTopic = new JCheckBox(JMeterUtils.getResString("mqtt_connection_per_topic"), false); // $NON-NLS-1$ 69 | private final JLabeledRadioI18N topicChoice = new JLabeledRadioI18N("mqtt_topic_choice", TOPIC_CHOICES,ROUND_ROBIN); //$NON-NLS-1$ 70 | private final JCheckBox stopBetweenSamples = new JCheckBox(JMeterUtils.getResString("mqtt_stop_between_samples"), true); // $NON-NLS-1$ 71 | private final JLabeledTextField clientId = new JLabeledTextField(JMeterUtils.getResString("mqtt_client_id")); //$NON-NLS-1$ 72 | private final JLabeledRadioI18N typeQoSValue = new JLabeledRadioI18N("mqtt_qos", QTYPES_ITEMS,AT_MOST_ONCE); //$NON-NLS-1$ 73 | private final JCheckBox cleanSession = new JCheckBox(JMeterUtils.getResString("mqtt_clean_session"), false); // $NON-NLS-1$ 74 | 75 | private final JLabeledTextField keepAlive = new JLabeledTextField(JMeterUtils.getResString("mqtt_keep_alive")); // $NON-NLS-1$ 76 | public MQTTSubscriberGui() { 77 | init(); 78 | } 79 | 80 | @Override 81 | public String getLabelResource() { 82 | return "mqtt_subscriber_title"; // $NON-NLS-1$ 83 | } 84 | 85 | /** 86 | * @see org.apache.jmeter.gui.JMeterGUIComponent#createTestElement() 87 | */ 88 | @Override 89 | public TestElement createTestElement() { 90 | SubscriberSampler sampler = new SubscriberSampler(); 91 | modifyTestElement(sampler); 92 | return sampler; 93 | } 94 | 95 | /** 96 | * Modifies a given TestElement to mirror the data in the gui components. 97 | * 98 | * @see org.apache.jmeter.gui.JMeterGUIComponent#modifyTestElement(TestElement) 99 | */ 100 | @Override 101 | public void modifyTestElement(TestElement s) { 102 | SubscriberSampler sampler = (SubscriberSampler) s; 103 | this.configureTestElement(sampler); 104 | sampler.setProviderUrl(urlField.getText()); 105 | sampler.setDestination(mqttDestination.getText()); 106 | sampler.setClientID(clientId.getText()); 107 | sampler.setUsername(mqttUser.getText()); 108 | sampler.setPassword(mqttPwd.getText()); 109 | sampler.setUseAuth(useAuth.isSelected()); 110 | sampler.setKeepAlive(keepAlive.getText()); 111 | sampler.setTimeout(timeout.getText()); 112 | sampler.setRandomSuffix(this.suffixClientId.isSelected()); 113 | sampler.setLength(this.suffixLength.getText()); 114 | sampler.setOneConnectionPerTopic(this.connectionPerTopic.isSelected()); 115 | sampler.setSTRATEGY(this.topicChoice.getText()); 116 | sampler.setQuality(typeQoSValue.getText()); 117 | sampler.setCLEANSESSION(cleanSession.isSelected()); 118 | 119 | } 120 | 121 | private void init() { 122 | setLayout(new BorderLayout()); 123 | setBorder(makeBorder()); 124 | add(makeTitlePanel(), BorderLayout.NORTH); 125 | JPanel mainPanel = new VerticalPanel(); 126 | add(mainPanel, BorderLayout.CENTER); 127 | JPanel DPanel = new JPanel(); 128 | DPanel.setLayout(new BoxLayout(DPanel, BoxLayout.X_AXIS)); 129 | DPanel.add(urlField); 130 | DPanel.add(clientId); 131 | DPanel.add(suffixClientId); 132 | DPanel.add(suffixLength); 133 | JPanel ControlPanel = new VerticalPanel(); 134 | ControlPanel.add(DPanel); 135 | ControlPanel.add(createDestinationPane()); 136 | ControlPanel.add(cleanSession); 137 | ControlPanel.add(createAuthPane()); 138 | ControlPanel.add(keepAlive); 139 | ControlPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray),"Connection Info")); 140 | mainPanel.add(ControlPanel); 141 | JPanel TPanel = new VerticalPanel(); 142 | TPanel.setLayout(new BoxLayout(TPanel, BoxLayout.X_AXIS)); 143 | timeout.setLayout(new BoxLayout(timeout, BoxLayout.X_AXIS)); 144 | typeQoSValue.setLayout(new BoxLayout(typeQoSValue, BoxLayout.X_AXIS)); 145 | TPanel.add(typeQoSValue); 146 | TPanel.add(timeout); 147 | TPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray),"Option")); 148 | mainPanel.add(TPanel); 149 | useAuth.addChangeListener(this); 150 | suffixClientId.addChangeListener(this); 151 | 152 | } 153 | /** 154 | * 155 | * @return JPanel Panel with checkbox to choose user and password 156 | */ 157 | private Component createAuthPane() { 158 | JPanel panel = new JPanel(); 159 | panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); 160 | panel.add(useAuth); 161 | panel.add(Box.createHorizontalStrut(10)); 162 | panel.add(mqttUser); 163 | panel.add(Box.createHorizontalStrut(10)); 164 | panel.add(mqttPwd); 165 | return panel; 166 | } 167 | /** 168 | * the implementation loads the URL and the soap action for the request. 169 | */ 170 | @Override 171 | public void configure(TestElement el) { 172 | super.configure(el); 173 | SubscriberSampler sampler = (SubscriberSampler) el; 174 | urlField.setText(sampler.getProviderUrl()); 175 | mqttDestination.setText(sampler.getDestination()); 176 | clientId.setText(sampler.getClientId()); 177 | suffixLength.setText(sampler.getLength()); 178 | mqttUser.setText(sampler.getUsername()); 179 | mqttPwd.setText(sampler.getPassword()); 180 | keepAlive.setText(String.valueOf(sampler.getKeepAlive())); 181 | useAuth.setSelected(sampler.isUseAuth()); 182 | mqttUser.setEnabled(useAuth.isSelected()); 183 | mqttPwd.setEnabled(useAuth.isSelected()); 184 | timeout.setText(sampler.getTimeout()); 185 | } 186 | 187 | @Override 188 | public void clearGui(){ 189 | super.clearGui(); 190 | urlField.setText(""); // $NON-NLS-1$ 191 | mqttDestination.setText(""); // $NON-NLS-1$ 192 | clientId.setText(""); // $NON-NLS-1$ 193 | mqttUser.setText(""); // $NON-NLS-1$ 194 | mqttPwd.setText(""); // $NON-NLS-1$ 195 | timeout.setText(""); // $NON-NLS-1$ 196 | separator.setText(""); // $NON-NLS-1$ 197 | keepAlive.setText("60"); 198 | useAuth.setSelected(false); 199 | mqttUser.setEnabled(false); 200 | mqttPwd.setEnabled(false); 201 | stopBetweenSamples.setSelected(false); 202 | 203 | } 204 | 205 | /** 206 | * When the state of a widget changes, it will notify the gui. the method 207 | * then enables or disables certain parameters. 208 | */ 209 | @Override 210 | public void stateChanged(ChangeEvent event) { 211 | if (event.getSource() == useAuth) { 212 | mqttUser.setEnabled(useAuth.isSelected()); 213 | mqttPwd.setEnabled(useAuth.isSelected()); 214 | } 215 | else if(event.getSource()==suffixClientId){ 216 | updateChoice("Suffix="+String.valueOf(this.suffixClientId.isSelected())); 217 | } 218 | } 219 | 220 | private void updateChoice(String command) { 221 | if("suffix=true".equalsIgnoreCase(command)){ 222 | this.suffixLength.setVisible(true); 223 | } 224 | else if("suffix=false".equalsIgnoreCase(command)){ 225 | this.suffixLength.setVisible(false); 226 | } 227 | validate(); 228 | } 229 | 230 | private JPanel createDestinationPane() { 231 | JPanel panel = new VerticalPanel(); //new BorderLayout(3, 0) 232 | this.mqttDestination.setLayout((new BoxLayout(mqttDestination, BoxLayout.X_AXIS))); 233 | panel.add(mqttDestination); 234 | JPanel TPanel = new JPanel(); 235 | TPanel.setLayout(new BoxLayout(TPanel,BoxLayout.X_AXIS)); 236 | this.connectionPerTopic.setLayout(new BoxLayout(connectionPerTopic,BoxLayout.X_AXIS)); 237 | this.connectionPerTopic.setAlignmentX(CENTER_ALIGNMENT); 238 | TPanel.add(connectionPerTopic); 239 | TPanel.add(Box.createHorizontalStrut(100)); 240 | this.topicChoice.setLayout(new BoxLayout(topicChoice,BoxLayout.X_AXIS)); 241 | TPanel.add(topicChoice); 242 | panel.add(TPanel); 243 | return panel; 244 | } 245 | } -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2014 University Joseph Fourier, LIG Laboratory, ERODS Team 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/client/MqttSubscriber.java: -------------------------------------------------------------------------------- 1 | /** 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | Copyright 2014 University Joseph Fourier, LIG Laboratory, ERODS Team 20 | 21 | */ 22 | 23 | package org.apache.jmeter.protocol.mqtt.client; 24 | 25 | import java.io.Serializable; 26 | import java.net.URISyntaxException; 27 | import java.util.Random; 28 | 29 | import javax.net.ssl.SSLContext; 30 | 31 | import org.apache.jmeter.config.Arguments; 32 | import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; 33 | import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; 34 | import org.apache.jmeter.protocol.mqtt.control.gui.MQTTSubscriberGui; 35 | import org.apache.jmeter.samplers.SampleResult; 36 | import org.apache.jmeter.threads.JMeterContext; 37 | import org.apache.jmeter.threads.JMeterContextService; 38 | import org.fusesource.hawtbuf.UTF8Buffer; 39 | import org.fusesource.mqtt.client.CallbackConnection; 40 | import org.fusesource.mqtt.client.MQTT; 41 | import org.fusesource.mqtt.client.QoS; 42 | import org.fusesource.mqtt.client.Topic; 43 | 44 | public class MqttSubscriber extends AbstractJavaSamplerClient implements 45 | Serializable { 46 | private static final long serialVersionUID = 1L; 47 | private CallbackConnection[] connectionArray; 48 | private ListenerforSubscribe[] lisenters; 49 | private static final String mycharset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 50 | private String label = ""; 51 | 52 | public static String getClientId(String clientPrefix, int suffixLength) { 53 | Random rand = new Random(System.nanoTime() * System.currentTimeMillis()); 54 | StringBuilder sb = new StringBuilder(); 55 | sb.append(clientPrefix); 56 | for (int i = 0; i < suffixLength; i++) { 57 | int pos = rand.nextInt(mycharset.length()); 58 | sb.append(mycharset.charAt(pos)); 59 | } 60 | return sb.toString(); 61 | } 62 | 63 | @Override 64 | public Arguments getDefaultParameters() { 65 | Arguments defaultParameters = new Arguments(); 66 | defaultParameters.addArgument("HOST", "tcp://localhost:1883"); 67 | defaultParameters.addArgument("CLIENT_ID", 68 | "${__time(YMDHMS)}${__threadNum}"); 69 | defaultParameters.addArgument("TOPIC", "TEST.MQTT"); 70 | defaultParameters.addArgument("AGGREGATE", "100"); 71 | defaultParameters.addArgument("DURABLE", "false"); 72 | return defaultParameters; 73 | } 74 | 75 | public void setupTest(JavaSamplerContext context) { 76 | label = context.getParameter("LABEL"); 77 | 78 | String host = context.getParameter("HOST"); 79 | String clientId = context.getParameter("CLIENT_ID"); 80 | if ("TRUE".equalsIgnoreCase(context.getParameter("RANDOM_SUFFIX"))) { 81 | clientId = MqttPublisher.getClientId(clientId, 82 | Integer.parseInt(context.getParameter("SUFFIX_LENGTH"))); 83 | } 84 | if ("FALSE".equals(context.getParameter("PER_TOPIC"))) { 85 | String topic = context.getParameter("TOPIC"); 86 | if ("TRUE".equals(context.getParameter("AUTH"))) { 87 | setupTest(host, clientId, topic, context.getParameter("USER"), 88 | context.getParameter("PASSWORD"), 1, 89 | Boolean.parseBoolean(context.getParameter("DURABLE")), 90 | context.getParameter("QOS"),context.getParameter("STRATEGY"), context.getIntParameter("KEEP_ALIVE")); 91 | 92 | } else { 93 | setupTest(host, clientId, topic, 1, 94 | Boolean.parseBoolean(context.getParameter("DURABLE")), 95 | context.getParameter("QOS"),context.getParameter("STRATEGY"), context.getIntParameter("KEEP_ALIVE")); 96 | } 97 | } else if ("TRUE".equals(context.getParameter("PER_TOPIC"))) { 98 | String topics = context.getParameter("TOPIC"); 99 | String[] topicArray = topics.split("\\s*,\\s*"); 100 | int size = topicArray.length; 101 | if ("TRUE".equals(context.getParameter("AUTH"))) { 102 | setupTest(host, clientId, topics, context.getParameter("USER"), 103 | context.getParameter("PASSWORD"), size, 104 | Boolean.parseBoolean(context.getParameter("DURABLE")), 105 | context.getParameter("QOS"),context.getParameter("STRATEGY"), context.getIntParameter("KEEP_ALIVE")); 106 | } else { 107 | setupTest(host, clientId, topics, size, 108 | Boolean.parseBoolean(context.getParameter("DURABLE")), 109 | context.getParameter("QOS"),context.getParameter("STRATEGY"), context.getIntParameter("KEEP_ALIVE")); 110 | } 111 | } 112 | } 113 | 114 | private void setupTest(String host, String clientId, String topic, 115 | String user, String password, int size, boolean durable, 116 | String quality,String strategy, int keepAlive) { 117 | try { 118 | // Quality 119 | QoS qos = null; 120 | if (MQTTSubscriberGui.EXACTLY_ONCE.equals(quality)) { 121 | qos = QoS.EXACTLY_ONCE; 122 | } else if (MQTTSubscriberGui.AT_LEAST_ONCE.equals(quality)) { 123 | qos = QoS.AT_LEAST_ONCE; 124 | } else if (MQTTSubscriberGui.AT_MOST_ONCE.equals(quality)) { 125 | qos = QoS.AT_MOST_ONCE; 126 | } 127 | 128 | this.connectionArray = new CallbackConnection[size]; 129 | this.lisenters = new ListenerforSubscribe[size]; 130 | JMeterContext jmcx = JMeterContextService.getContext(); 131 | if (size == 1) { 132 | this.connectionArray[0] = createConnection(host, clientId 133 | + jmcx.getThreadNum(), durable, user, password, keepAlive); 134 | this.lisenters[0] = new ListenerforSubscribe(); 135 | this.connectionArray[0].listener(this.lisenters[0]); 136 | CallbackforSubscribe cbs = new CallbackforSubscribe(); 137 | CallbackforConnect cbc = new CallbackforConnect(topic, 138 | connectionArray[0], cbs, qos,1); 139 | this.connectionArray[0].connect(cbc); 140 | 141 | } else if (size > 1) { 142 | 143 | if("ROUND_ROBIN".equals(strategy)){ 144 | for (int j = 0; j < size; j++) { 145 | this.connectionArray[j] = createConnection(host, clientId 146 | + jmcx.getThreadNum() + j, durable, user, password, keepAlive); 147 | this.lisenters[j] = new ListenerforSubscribe(); 148 | this.connectionArray[j].listener(this.lisenters[j]); 149 | CallbackforSubscribe cbs = new CallbackforSubscribe(); 150 | CallbackforConnect cbc = new CallbackforConnect(topic, 151 | connectionArray[j], cbs, qos,size); 152 | this.connectionArray[j].connect(cbc); 153 | 154 | } 155 | 156 | } 157 | else if("RANDOM".equals(strategy)){ 158 | Random rand = new Random(); 159 | String[] topicArray = topic.split("\\s*,\\s*"); 160 | int r = rand.nextInt(size); 161 | this.connectionArray[r] = createConnection(host, clientId 162 | + jmcx.getThreadNum() + r, durable, user, password, keepAlive); 163 | this.lisenters[r] = new ListenerforSubscribe(); 164 | this.connectionArray[r].listener(this.lisenters[r]); 165 | CallbackforSubscribe cbs = new CallbackforSubscribe(); 166 | CallbackforConnect cbc = new CallbackforConnect(topicArray[r], 167 | connectionArray[r], cbs, qos,1); 168 | this.connectionArray[r].connect(cbc); 169 | 170 | } 171 | 172 | } 173 | } catch (Exception e) { 174 | getLogger().error(e.getMessage()); 175 | } 176 | } 177 | 178 | private void setupTest(String host, String clientId, String topic, 179 | int size, boolean durable, String quality,String strategy, int keepAlive) { 180 | try { 181 | // Quality 182 | QoS qos = null; 183 | if (MQTTSubscriberGui.EXACTLY_ONCE.equals(quality)) { 184 | qos = QoS.EXACTLY_ONCE; 185 | } else if (MQTTSubscriberGui.AT_LEAST_ONCE.equals(quality)) { 186 | qos = QoS.AT_LEAST_ONCE; 187 | } else if (MQTTSubscriberGui.AT_MOST_ONCE.equals(quality)) { 188 | qos = QoS.AT_MOST_ONCE; 189 | } 190 | this.connectionArray = new CallbackConnection[size]; 191 | this.lisenters = new ListenerforSubscribe[size]; 192 | JMeterContext jmcx = JMeterContextService.getContext(); 193 | if (size == 1) { 194 | this.connectionArray[0] = createConnection(host, clientId 195 | + jmcx.getThreadNum(), durable, keepAlive); 196 | this.lisenters[0] = new ListenerforSubscribe(); 197 | this.connectionArray[0].listener(this.lisenters[0]); 198 | CallbackforSubscribe cbs = new CallbackforSubscribe(); 199 | CallbackforConnect cbc = new CallbackforConnect(topic, 200 | connectionArray[0], cbs, qos,1); 201 | this.connectionArray[0].connect(cbc); 202 | 203 | } else if (size > 1) { 204 | if("ROUND_ROBIN".equals(strategy)){ 205 | for (int j = 0; j < size; j++) { 206 | this.connectionArray[j] = createConnection(host, clientId 207 | + jmcx.getThreadNum() + j, durable, keepAlive); 208 | this.lisenters[j] = new ListenerforSubscribe(); 209 | this.connectionArray[j].listener(this.lisenters[j]); 210 | CallbackforSubscribe cbs = new CallbackforSubscribe(); 211 | CallbackforConnect cbc = new CallbackforConnect(topic, 212 | connectionArray[j], cbs, qos,size); 213 | this.connectionArray[j].connect(cbc); 214 | } 215 | }else if("RANDOM".equals(strategy)){ 216 | Random rand = new Random(); 217 | int r = rand.nextInt(size); 218 | String[] topicArray = topic.split("\\s*,\\s*"); 219 | this.connectionArray[r] = createConnection(host, clientId 220 | + jmcx.getThreadNum() + r, durable, keepAlive); 221 | this.lisenters[r] = new ListenerforSubscribe(); 222 | this.connectionArray[r].listener(this.lisenters[r]); 223 | CallbackforSubscribe cbs = new CallbackforSubscribe(); 224 | CallbackforConnect cbc = new CallbackforConnect(topicArray[r], 225 | connectionArray[r], cbs, qos,1); 226 | this.connectionArray[r].connect(cbc); 227 | } 228 | 229 | } 230 | } catch (Exception e) { 231 | getLogger().error(e.getMessage()); 232 | } 233 | } 234 | 235 | private CallbackConnection createConnection(String host, String clientId, 236 | boolean durable, int keepAlive) throws URISyntaxException { 237 | 238 | MQTT client = new MQTT(); 239 | client.setHost(host); 240 | client.setClientId(clientId); 241 | client.setCleanSession(!durable); 242 | client.setKeepAlive((short) keepAlive); 243 | if(host != null && (host.trim().toLowerCase().startsWith("ssl://"))) { 244 | SSLContext context = SSLUtil.getContext(); 245 | if(context != null) { 246 | client.setSslContext(context); 247 | } 248 | } 249 | return client.callbackConnection(); 250 | } 251 | 252 | private CallbackConnection createConnection(String host, String clientId, 253 | boolean durable, String user, String password, int keepAlive) 254 | throws URISyntaxException { 255 | MQTT client = new MQTT(); 256 | client.setHost(host); 257 | client.setClientId(clientId); 258 | client.setUserName(user); 259 | client.setPassword(password); 260 | client.setCleanSession(!durable); 261 | System.out.println("KeepAlive:" + keepAlive); 262 | client.setKeepAlive((short) keepAlive); 263 | if(host != null && (host.trim().toLowerCase().startsWith("ssl://"))) { 264 | SSLContext context = SSLUtil.getContext(); 265 | if(context != null) { 266 | client.setSslContext(context); 267 | } 268 | } 269 | return client.callbackConnection(); 270 | } 271 | 272 | public SampleResult runTest(JavaSamplerContext context) { 273 | SampleResult result = new SampleResult(); 274 | result.setSampleLabel(label); 275 | try { 276 | result.sampleStart(); // start stopwatch 277 | int size = 0; 278 | for(ListenerforSubscribe listener : this.lisenters) { 279 | if(listener.getException() != null) { 280 | throw new Exception(listener.getException().getMessage()); 281 | } 282 | size += listener.getSize(); 283 | } 284 | if(size == 0) { 285 | System.out.println("The size is zero, sleep for a while..." ); 286 | Thread.sleep(Long.parseLong(context.getParameter("TIMEOUT"))); 287 | for(ListenerforSubscribe listener : this.lisenters) { 288 | size += listener.getSize(); 289 | } 290 | } 291 | 292 | result.sampleEnd(); // stop stopwatch 293 | result.setSuccessful(true); 294 | result.setResponseMessage("Received " + context.getParameter("AGGREGATE") + " messages"); 295 | result.setResponseCodeOK(); 296 | result.setBytes(size); 297 | } catch (Exception e) { 298 | result.sampleEnd(); // stop stopwatch 299 | result.setSuccessful(false); 300 | result.setResponseMessage("Exception: " + e); 301 | // get stack trace as a String to return as document data 302 | java.io.StringWriter stringWriter = new java.io.StringWriter(); 303 | e.printStackTrace(new java.io.PrintWriter(stringWriter)); 304 | result.setResponseData(stringWriter.toString(), null); 305 | result.setDataType(org.apache.jmeter.samplers.SampleResult.TEXT); 306 | result.setResponseCode("FAILED"); 307 | } 308 | return result; 309 | } 310 | 311 | public void close(JavaSamplerContext context) { 312 | 313 | // Quality 314 | QoS qos = null; 315 | if (MQTTSubscriberGui.EXACTLY_ONCE.equals(context.getParameter("QOS"))) { 316 | qos = QoS.EXACTLY_ONCE; 317 | } else if (MQTTSubscriberGui.AT_LEAST_ONCE.equals(context 318 | .getParameter("QOS"))) { 319 | qos = QoS.AT_LEAST_ONCE; 320 | } else if (MQTTSubscriberGui.AT_MOST_ONCE.equals(context 321 | .getParameter("QOS"))) { 322 | qos = QoS.AT_MOST_ONCE; 323 | } 324 | // Topic 325 | String topics = context.getParameter("TOPIC"); 326 | String[] topicArray = topics.split("\\s*,\\s*"); 327 | Topic[] Tp = new Topic[topicArray.length]; 328 | UTF8Buffer[] Topics = new UTF8Buffer[topicArray.length]; 329 | for (int i = 0; i < topicArray.length; i++) { 330 | Tp[i] = new Topic(topicArray[i], qos); 331 | Topics[i] = new UTF8Buffer(Tp[i].toString()); 332 | } 333 | if (this.connectionArray != null) { 334 | for (int p = 0; p < this.connectionArray.length; p++) { 335 | if (this.connectionArray[p] != null) { 336 | this.connectionArray[p].unsubscribe(Topics, 337 | new CallbackforUnsubscribe()); 338 | this.connectionArray[p].disconnect(new CallbackforDisconnect()); 339 | } 340 | 341 | this.connectionArray[p] = null; 342 | } 343 | } 344 | this.connectionArray = null; 345 | } 346 | 347 | } 348 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/sampler/PublisherSampler.java: -------------------------------------------------------------------------------- 1 | /** 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | Copyright 2014 University Joseph Fourier, LIG Laboratory, ERODS Team 20 | 21 | */ 22 | 23 | package org.apache.jmeter.protocol.mqtt.sampler; 24 | import java.io.IOException; 25 | import java.util.Date; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | import org.apache.jmeter.config.Arguments; 28 | import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; 29 | import org.apache.jmeter.protocol.mqtt.client.MqttPublisher; 30 | import org.apache.jmeter.protocol.mqtt.control.gui.MQTTPublisherGui; 31 | import org.apache.jmeter.samplers.SampleResult; 32 | import org.apache.jmeter.testelement.TestStateListener; 33 | import org.apache.jmeter.testelement.ThreadListener; 34 | import org.apache.jorphan.logging.LoggingManager; 35 | import org.apache.log.Logger; 36 | 37 | public class PublisherSampler extends BaseMQTTSampler implements ThreadListener,TestStateListener { 38 | 39 | private static final long serialVersionUID = 233L; 40 | private static final Logger log = LoggingManager.getLoggerForClass(); 41 | private static final String TEXT_MSG = "mqtt.text_message"; //$NON-NLS-1$ 42 | private static final String CONFIG_CHOICE = "mqtt.config_choice"; //$NON-NLS-1$ 43 | private static final String MESSAGE_CHOICE = "mqtt.config_msg_type"; //$NON-NLS-1$ 44 | private static final String QUALITY = "mqtt.quality"; //$NON-NLS-1$ 45 | private static final String TYPE_FIXED_VALUE = "mqtt.type_fixed_value"; //$NON-NLS-1$ 46 | private static String CLIENT_ID = "mqtt.clientid"; //$NON-NLS-1$ 47 | private static String RETAIN = "mqtt.retain"; //$NON-NLS-1$ 48 | private static String USE_TIMESTAMP = "mqtt.use_timestamp"; //$NON-NLS-1$ 49 | private static String USE_NUMBER_SEQUENCE = "mqtt.use_number_sequence"; //$NON-NLS-1$ 50 | private static String FIXED_VALUE = "mqtt.fixed_value"; //$NON-NLS-1$ 51 | private static String TYPE_RANDOM_VALUE = "mqtt.type_random_value"; //$NON-NLS-1$ 52 | private static String MIN_RANDOM_VALUE = "mqtt.min_random_value"; //$NON-NLS-1$ 53 | private static String MAX_RANDOM_VALUE = "mqtt.max_random_value"; //$NON-NLS-1$ 54 | private static String TYPE_GENERATED_VALUE = "mqtt.type_generated_value"; //$NON-NLS-1$ 55 | private static String SEED = "mqtt.seed"; //$NON-NLS-1$ 56 | private static String FORMAT = "mqtt.format"; //$NON-NLS-1$ 57 | private static String CHARSET = "mqtt.charset"; //$NON-NLS-1$ 58 | private static String SIZE_ARRAY = "mqtt.size_array"; //$NON-NLS-1$ 59 | private static String STRATEGY = "mqtt.strategy"; //$NON-NLS-1$ 60 | private static String OneConnectionPerTopic = "mqtt.one_connection_per_topic"; //$NON-NLS-1$ 61 | private static String RandomSuffix="mqtt.random_suffix_client_id";//$NON-NLS-1$ 62 | private static String Length="mqtt.suffix.length";//$NON-NLS-1$ 63 | public transient MqttPublisher producer = null; 64 | public static AtomicInteger numberOfConnection= new AtomicInteger(0); 65 | private JavaSamplerContext context = null; 66 | 67 | /** 68 | * Constructor 69 | */ 70 | 71 | public PublisherSampler() { 72 | } 73 | 74 | // ---------------------Get/Set Property--------------------------// 75 | 76 | public void setTYPE_FIXED_VALUE(String type) { 77 | setProperty(TYPE_FIXED_VALUE, type); 78 | } 79 | public String getLength() { 80 | return getPropertyAsString(Length); 81 | } 82 | public void setLength(String length) { 83 | setProperty(Length,length); 84 | } 85 | 86 | public boolean useRandomSuffix() { 87 | 88 | String randomSuffix = getPropertyAsString(RandomSuffix); 89 | if("TRUE".equalsIgnoreCase(randomSuffix)){ 90 | return true; 91 | } 92 | else { 93 | return false; 94 | } 95 | 96 | } 97 | 98 | public void setRandomSuffix(boolean randomSuffix) { 99 | setProperty(RandomSuffix,randomSuffix); 100 | 101 | } 102 | 103 | public boolean isOneConnectionPerTopic() { 104 | 105 | String perTopic = getPropertyAsString(OneConnectionPerTopic); 106 | if("TRUE".equalsIgnoreCase(perTopic)){ 107 | return true; 108 | } 109 | else { 110 | return false; 111 | } 112 | 113 | } 114 | public void setOneConnectionPerTopic(boolean oneConnectionPerTopic) { 115 | 116 | setProperty(OneConnectionPerTopic, oneConnectionPerTopic); 117 | } 118 | 119 | public String getSTRATEGY() { 120 | return getPropertyAsString(STRATEGY); 121 | 122 | } 123 | 124 | public void setSTRATEGY(String sTRATEGY) { 125 | setProperty(STRATEGY,sTRATEGY); 126 | 127 | } 128 | 129 | public String getSIZE_ARRAY() { 130 | return getPropertyAsString(SIZE_ARRAY); 131 | } 132 | 133 | public void setSIZE_ARRAY(String sIZE_ARRAY) { 134 | setProperty(SIZE_ARRAY,sIZE_ARRAY); 135 | } 136 | 137 | public String getCHARSET() { 138 | return getPropertyAsString(CHARSET); 139 | } 140 | 141 | public void setCHARSET(String cHARSET) { 142 | setProperty(CHARSET,cHARSET); 143 | } 144 | 145 | public String getFORMAT() { 146 | return getPropertyAsString(FORMAT); 147 | } 148 | 149 | public void setFORMAT(String fORMAT) { 150 | setProperty(FORMAT,fORMAT); 151 | } 152 | 153 | public String getCLIENT_ID() { 154 | return getPropertyAsString(CLIENT_ID); 155 | } 156 | 157 | public void setCLIENT_ID(String cLIENT_ID) { 158 | setProperty(CLIENT_ID,cLIENT_ID); 159 | } 160 | 161 | public boolean isUSE_TIMESTAMP() { 162 | String isUseTimeStamp = getPropertyAsString(USE_TIMESTAMP); 163 | if("TRUE".equalsIgnoreCase(isUseTimeStamp)){ 164 | return true; 165 | } 166 | else { 167 | return false; 168 | } 169 | } 170 | 171 | public void setUSE_TIMESTAMP(boolean uSE_TIMESTAMP) { 172 | setProperty(USE_TIMESTAMP,uSE_TIMESTAMP); 173 | } 174 | 175 | public boolean isUSE_NUMBER_SEQUENCE() { 176 | String isUseNumberSequence = getPropertyAsString(USE_NUMBER_SEQUENCE); 177 | if("TRUE".equalsIgnoreCase(isUseNumberSequence)){ 178 | return true; 179 | } 180 | else { 181 | return false; 182 | } 183 | } 184 | 185 | public void setUSE_NUMBER_SEQUENCE(boolean uSE_NUMBER_SEQUENCE) { 186 | setProperty(USE_NUMBER_SEQUENCE,uSE_NUMBER_SEQUENCE); 187 | } 188 | public String getSEED() { 189 | return getPropertyAsString(SEED); 190 | } 191 | public void setSEED(String sEED) { 192 | setProperty(SEED,sEED); 193 | } 194 | public String getTYPE_GENERATED_VALUE() { 195 | return getPropertyAsString(TYPE_GENERATED_VALUE); 196 | } 197 | public void setTYPE_GENERATED_VALUE(String tYPE_GENERATED_VALUE) { 198 | setProperty(TYPE_GENERATED_VALUE,tYPE_GENERATED_VALUE); 199 | } 200 | 201 | public String getTYPE_RANDOM_VALUE() { 202 | return getPropertyAsString(TYPE_RANDOM_VALUE); 203 | } 204 | 205 | public void setTYPE_RANDOM_VALUE(String tYPE_RANDOM_VALUE) { 206 | setProperty(TYPE_RANDOM_VALUE,tYPE_RANDOM_VALUE); 207 | } 208 | 209 | public String getMIN_RANDOM_VALUE() { 210 | return getPropertyAsString(MIN_RANDOM_VALUE); 211 | 212 | } 213 | 214 | public void setMIN_RANDOM_VALUE(String mIN_RANDOM_VALUE) { 215 | setProperty(MIN_RANDOM_VALUE,mIN_RANDOM_VALUE); 216 | } 217 | 218 | public String getMAX_RANDOM_VALUE() { 219 | return getPropertyAsString(MAX_RANDOM_VALUE); 220 | } 221 | 222 | public void setMAX_RANDOM_VALUE(String mAX_RANDOM_VALUE) { 223 | setProperty(MAX_RANDOM_VALUE,mAX_RANDOM_VALUE); 224 | 225 | } 226 | 227 | public String getFIXED_VALUE() { 228 | return getPropertyAsString( FIXED_VALUE); 229 | 230 | } 231 | 232 | public void setFIXED_VALUE(String fIXED_VALUE) { 233 | setProperty(FIXED_VALUE,fIXED_VALUE); 234 | 235 | } 236 | 237 | public String getTYPE_FIXED_VALUE() { 238 | return getPropertyAsString(TYPE_FIXED_VALUE); 239 | 240 | } 241 | public void setTextMessage(String message) { 242 | setProperty(TEXT_MSG, message); 243 | } 244 | public void setConfigChoice(String choice) { 245 | setProperty(CONFIG_CHOICE, choice); 246 | 247 | } 248 | 249 | public void setMessageChoice(String choice) { 250 | setProperty(MESSAGE_CHOICE, choice); 251 | 252 | } 253 | 254 | public String getTextMessage() { 255 | return getPropertyAsString(TEXT_MSG); 256 | } 257 | 258 | /** 259 | * To get the message choice 260 | * 261 | * @return 262 | */ 263 | public String getMessageChoice() { 264 | return getPropertyAsString(MESSAGE_CHOICE); 265 | } 266 | 267 | public String getQuality() { 268 | return getPropertyAsString(QUALITY); 269 | } 270 | 271 | public void setQuality(String quality) { 272 | setProperty(QUALITY, quality); 273 | } 274 | 275 | public void setRetained(boolean isRetained) { 276 | 277 | setProperty(RETAIN,isRetained); 278 | } 279 | 280 | public boolean isRetained() { 281 | String isRetain = getPropertyAsString(RETAIN); 282 | if("TRUE".equalsIgnoreCase(isRetain)){ 283 | return true; 284 | } 285 | else { 286 | return false; 287 | } 288 | 289 | } 290 | 291 | 292 | private void logThreadStart() { 293 | if (log.isDebugEnabled()) { 294 | log.debug("Thread started " + new Date()); 295 | log.debug("MQTT PublishSampler: [" 296 | + Thread.currentThread().getName() + "], hashCode=[" 297 | + hashCode() + "]"); 298 | 299 | } 300 | 301 | } 302 | 303 | @Override 304 | public void threadStarted() { 305 | logThreadStart(); 306 | 307 | if (producer == null) { 308 | 309 | try { 310 | producer = new MqttPublisher(); 311 | } catch (Exception e) { 312 | log.warn(e.getLocalizedMessage(), e); 313 | } 314 | } 315 | 316 | String host = getProviderUrl(); 317 | String list_topic = getDestination(); 318 | String aggregate = "" + getIterationCount(); 319 | Arguments parameters = new Arguments(); 320 | parameters.addArgument("HOST", host); 321 | // ------------------------ClientId-----------------------------------// 322 | parameters.addArgument("CLIENT_ID", getCLIENT_ID()); 323 | parameters.addArgument("TOPIC", list_topic); 324 | 325 | // ------------------------Strategy-----------------------------------// 326 | if (MQTTPublisherGui.ROUND_ROBIN.equals(this.getSTRATEGY())) { 327 | parameters.addArgument("STRATEGY", "ROUND_ROBIN"); 328 | } else { 329 | parameters.addArgument("STRATEGY", "RANDOM"); 330 | } 331 | 332 | parameters.addArgument("AGGREGATE", aggregate); 333 | 334 | String quality = getQuality(); 335 | parameters.addArgument("QOS", quality); 336 | if (this.isRetained()) { 337 | parameters.addArgument("RETAINED", "TRUE"); 338 | } else { 339 | parameters.addArgument("RETAINED", "FALSE"); 340 | } 341 | // -------------------------TimeStamp-----------------------------// 342 | 343 | if (this.isUSE_TIMESTAMP()) { 344 | parameters.addArgument("TIME_STAMP", "TRUE"); 345 | } else 346 | parameters.addArgument("TIME_STAMP", "FALSE"); 347 | 348 | // -------------------------Number Sequence-----------------------// 349 | 350 | if (this.isUSE_NUMBER_SEQUENCE()) { 351 | parameters.addArgument("NUMBER_SEQUENCE", "TRUE"); 352 | } else 353 | parameters.addArgument("NUMBER_SEQUENCE", "FALSE"); 354 | 355 | // ---------------------Message Choice----------------------------// 356 | 357 | if (this.getMessageChoice().equals(MQTTPublisherGui.TEXT_MSG_RSC)) { 358 | 359 | parameters.addArgument("MESSAGE", getTextMessage()); 360 | parameters.addArgument("TYPE_MESSAGE", "TEXT"); 361 | parameters.addArgument("TYPE_VALUE", "TEXT"); 362 | } else if (this.getMessageChoice().equals(MQTTPublisherGui.FIXED_VALUE)) { 363 | 364 | parameters.addArgument("MESSAGE", getFIXED_VALUE()); 365 | parameters.addArgument("TYPE_MESSAGE", "FIXED"); 366 | parameters.addArgument("TYPE_VALUE", getTYPE_FIXED_VALUE()); 367 | } else if (this.getMessageChoice().equals( 368 | MQTTPublisherGui.GENERATED_VALUE)) { 369 | parameters.addArgument("TYPE_MESSAGE", "RANDOM"); 370 | parameters.addArgument("TYPE_VALUE", getTYPE_GENERATED_VALUE()); 371 | parameters.addArgument("SEED", getSEED()); 372 | parameters.addArgument("MIN_RANDOM_VALUE", getMIN_RANDOM_VALUE()); 373 | parameters.addArgument("MAX_RANDOM_VALUE", getMAX_RANDOM_VALUE()); 374 | parameters.addArgument("TYPE_RANDOM_VALUE", getTYPE_RANDOM_VALUE()); 375 | } else if (this.getMessageChoice().equals(MQTTPublisherGui.BIG_VOLUME)) { 376 | parameters.addArgument("TYPE_MESSAGE", "BYTE_ARRAY"); 377 | parameters.addArgument("SIZE_ARRAY", this.getSIZE_ARRAY()); 378 | } 379 | 380 | // -----------------------User/Password-------------------------------// 381 | 382 | if (this.isUseAuth()) { 383 | parameters.addArgument("AUTH", "TRUE"); 384 | parameters.addArgument("USER", getUsername()); 385 | parameters.addArgument("PASSWORD", getPassword()); 386 | } else 387 | parameters.addArgument("AUTH", "FALSE"); 388 | // -----------------------Format--------------------------------------// 389 | parameters.addArgument("FORMAT", getFORMAT()); 390 | if (this.getFORMAT().equals(MQTTPublisherGui.PLAIN_TEXT)) { 391 | parameters.addArgument("CHARSET", getCHARSET()); 392 | 393 | } else 394 | parameters.addArgument("CHARSET", "NULL"); 395 | 396 | // -------------------------List Topic Or Not-------------------------// 397 | 398 | String[] topics = list_topic.split("\\s*,\\s*"); 399 | if (topics.length <= 1) { 400 | parameters.addArgument("LIST_TOPIC", "FALSE"); 401 | } else { 402 | parameters.addArgument("LIST_TOPIC", "TRUE"); 403 | } 404 | // ------------------------Connection per topic--------------------// 405 | 406 | if (this.isOneConnectionPerTopic()) { 407 | parameters.addArgument("PER_TOPIC", "TRUE"); 408 | } else { 409 | parameters.addArgument("PER_TOPIC", "FALSE"); 410 | } 411 | if(this.useRandomSuffix()){ 412 | parameters.addArgument("RANDOM_SUFFIX","TRUE"); 413 | parameters.addArgument("SUFFIX_LENGTH",this.getLength()); 414 | }else { 415 | parameters.addArgument("RANDOM_SUFFIX","FALSE"); 416 | } 417 | 418 | parameters.addArgument("KEEP_ALIVE", String.valueOf(this.getKeepAlive())); 419 | 420 | parameters.addArgument("LABEL", this.getName()); 421 | 422 | this.context = new JavaSamplerContext(parameters); 423 | this.producer.setupTest(this.context); 424 | } 425 | 426 | 427 | @Override 428 | public void threadFinished() { 429 | log.debug("Thread ended " + new Date()); 430 | 431 | if (producer != null) { 432 | 433 | try { 434 | producer.close(); 435 | MqttPublisher.numSeq = 0; 436 | 437 | } catch (IOException e) { 438 | e.printStackTrace(); 439 | log.warn(e.getLocalizedMessage(), e); 440 | } 441 | 442 | } 443 | 444 | } 445 | 446 | // -------------------------Sample------------------------------------// 447 | 448 | @Override 449 | public SampleResult sample() { 450 | 451 | return this.producer.runTest(context); 452 | } 453 | 454 | @Override 455 | public void testEnded() { 456 | log.debug("Thread ended " + new Date()); 457 | 458 | 459 | if (producer != null) { 460 | 461 | try { 462 | producer.close(); 463 | System.out.println("close at:"+ new Date()); 464 | MqttPublisher.numSeq = 0; 465 | 466 | } catch (IOException e) { 467 | e.printStackTrace(); 468 | log.warn(e.getLocalizedMessage(), e); 469 | } 470 | 471 | } 472 | 473 | 474 | } 475 | 476 | @Override 477 | public void testEnded(String arg0) { 478 | testEnded(); 479 | 480 | } 481 | 482 | @Override 483 | public void testStarted() { 484 | 485 | } 486 | 487 | @Override 488 | public void testStarted(String arg0) { 489 | testStarted(); 490 | 491 | } 492 | 493 | } 494 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/control/gui/MQTTPublisherGui.java: -------------------------------------------------------------------------------- 1 | /** 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | Copyright 2014 University Joseph Fourier, LIG Laboratory, ERODS Team 20 | 21 | */ 22 | 23 | package org.apache.jmeter.protocol.mqtt.control.gui; 24 | import java.awt.BorderLayout; 25 | import java.awt.Color; 26 | import java.awt.Component; 27 | import java.awt.Dimension; 28 | import javax.swing.BorderFactory; 29 | import javax.swing.Box; 30 | import javax.swing.BoxLayout; 31 | import javax.swing.JCheckBox; 32 | import javax.swing.JComboBox; 33 | import javax.swing.JLabel; 34 | import javax.swing.JPanel; 35 | import javax.swing.event.ChangeEvent; 36 | import javax.swing.event.ChangeListener; 37 | import org.apache.jmeter.gui.util.JLabeledRadioI18N; 38 | import org.apache.jmeter.gui.util.JSyntaxTextArea; 39 | import org.apache.jmeter.gui.util.JTextScrollPane; 40 | import org.apache.jmeter.gui.util.VerticalPanel; 41 | import org.apache.jmeter.protocol.mqtt.sampler.PublisherSampler; 42 | import org.apache.jmeter.samplers.gui.AbstractSamplerGui; 43 | import org.apache.jmeter.testelement.TestElement; 44 | import org.apache.jmeter.util.JMeterUtils; 45 | import org.apache.jorphan.gui.JLabeledPasswordField; 46 | import org.apache.jorphan.gui.JLabeledTextField; 47 | 48 | /** 49 | * @author Tuan Hiep 50 | * 51 | */ 52 | public class MQTTPublisherGui extends AbstractSamplerGui implements 53 | ChangeListener { 54 | 55 | private static final long serialVersionUID = 240L; 56 | /** Take source from the named file */ 57 | public static final String USE_FILE_RSC = "mqtt_use_file"; //$NON-NLS-1$ 58 | /** Take source from a random file */ 59 | public static final String USE_RANDOM_RSC = "mqtt_use_random_file"; //$NON-NLS-1$ 60 | /** Take source from the text area */ 61 | private static final String USE_TEXT_RSC = "mqtt_use_text"; //$NON-NLS-1$ 62 | /** Create a TextMessage */ 63 | public static final String TEXT_MSG_RSC = "mqtt_text_message"; //$NON-NLS-1$ 64 | /** Create a big volume message */ 65 | public static final String BIG_VOLUME = "mqtt_big_volume"; //$NON-NLS-1$ 66 | /** Create a MapMessage */ 67 | public static final String MAP_MSG_RSC = "mqtt_map_message"; //$NON-NLS-1$ 68 | /** Create an ObjectMessage */ 69 | public static final String OBJECT_MSG_RSC = "mqtt_object_message"; //$NON-NLS-1$ 70 | /** Create a BytesMessage */ 71 | public static final String BYTES_MSG_RSC = "mqtt_bytes_message"; //$NON-NLS-1$ 72 | /** Create a message of type long number */ 73 | public static final String LONG = "mqtt_long_value"; //$NON-NLS-1$ 74 | /** Create a Message of type integer number */ 75 | public static final String INT = "mqtt_int_value"; //$NON-NLS-1$ 76 | /** Create a Message of type double number */ 77 | public static final String DOUBLE = "mqtt_double_value"; //$NON-NLS-1$ 78 | /** Create a Message of type double number */ 79 | public static final String FLOAT = "mqtt_float_value"; //$NON-NLS-1$ 80 | /** Create a Message of type String */ 81 | public static final String STRING = "mqtt_string_value"; //$NON-NLS-1$ 82 | 83 | // These are the names of properties used to define the labels 84 | private static final String DEST_SETUP_STATIC = "mqtt_dest_setup_static"; // $NON-NLS-1$ 85 | private static final String DEST_SETUP_DYNAMIC = "mqtt_dest_setup_dynamic"; // $NON-NLS-1$ 86 | public static final String GENERATED_VALUE = "mqtt_generated_value"; // $NON-NLS-1$ 87 | public static final String FIXED_VALUE = "mqtt_fixed_value";// $NON-NLS-1$ 88 | public static final String PSEUDO = "mqtt_pseudo_random";// $NON-NLS-1$ 89 | public static final String SECURE = "mqtt_secure_random";// $NON-NLS-1$ 90 | public static final String EXACTLY_ONCE = "mqtt_extactly_once";// $NON-NLS-1$ 91 | public static final String AT_LEAST_ONCE = "mqtt_at_least_once";// $NON-NLS-1$ 92 | public static final String AT_MOST_ONCE = "mqtt_at_most_once";// $NON-NLS-1$ 93 | public static final String PLAIN_TEXT = "mqtt_plain_text";// $NON-NLS-1$ 94 | public static final String BASE64 = "mqtt_base64";// $NON-NLS-1$ 95 | public static final String BINHEX = "mqtt_binhex";// $NON-NLS-1$ 96 | public static final String BINARY = "mqtt_binary";// $NON-NLS-1$ 97 | public static final String NO_ENCODING = "mqtt_no_encoding";// $NON-NLS-1$ 98 | public static final String ROUND_ROBIN = "mqtt_round_robin";// $NON-NLS-1$ 99 | public static final String RANDOM = "mqtt_random";// $NON-NLS-1$ 100 | // Button group resources 101 | private static final String[] DEST_SETUP_ITEMS = { DEST_SETUP_STATIC,DEST_SETUP_DYNAMIC }; 102 | private final JLabeledRadioI18N destSetup = new JLabeledRadioI18N("mqtt_dest_setup", DEST_SETUP_ITEMS, DEST_SETUP_STATIC); // $NON-NLS-1$ 103 | private static final String[] MSGTYPES_ITEMS = { TEXT_MSG_RSC,GENERATED_VALUE,FIXED_VALUE,BIG_VOLUME }; 104 | private static final String[] TOPIC_CHOICES={ROUND_ROBIN,RANDOM}; 105 | private static final String[] MSGFORMAT_ITEMS = {NO_ENCODING,BINARY,BASE64,BINHEX,PLAIN_TEXT}; 106 | private static final String[] VALTYPES_ITEMS = { INT,LONG,FLOAT,DOUBLE}; 107 | private static final String[] FVALTYPES_ITEMS = {INT,LONG,FLOAT,DOUBLE,STRING}; 108 | private static final String[] RANTYPES_ITEMS = {PSEUDO,SECURE}; 109 | private static final String[] QTYPES_ITEMS = {AT_MOST_ONCE,AT_LEAST_ONCE,EXACTLY_ONCE}; 110 | private final JLabeledTextField urlField = new JLabeledTextField(JMeterUtils.getResString("mqtt_provider_url")); //$NON-NLS-1$ 111 | private final JLabeledTextField mqttDestination = new JLabeledTextField(JMeterUtils.getResString("mqtt_topic")); //$NON-NLS-1$ 112 | private final JCheckBox useAuth = new JCheckBox(JMeterUtils.getResString("mqtt_use_auth"), false); //$NON-NLS-1$ 113 | private final JLabeledTextField mqttUser = new JLabeledTextField(JMeterUtils.getResString("mqtt_user")); //$NON-NLS-1$ 114 | private final JLabeledTextField mqttPwd = new JLabeledPasswordField(JMeterUtils.getResString("mqtt_pwd")); //$NON-NLS-1$ 115 | private final JLabeledTextField iterations = new JLabeledTextField( JMeterUtils.getResString("mqtt_itertions")); //$NON-NLS-1$ 116 | private final JLabeledTextField keepAlive = new JLabeledTextField( JMeterUtils.getResString("mqtt_keep_alive")); //$NON-NLS-1$ 117 | private final JSyntaxTextArea textMessage = new JSyntaxTextArea(10, 50); // $NON-NLS-1$ 118 | private final JLabeledRadioI18N msgChoice = new JLabeledRadioI18N("mqtt_message_type", MSGTYPES_ITEMS, TEXT_MSG_RSC); //$NON-NLS-1$ 119 | private final JLabeledRadioI18N msgFormat = new JLabeledRadioI18N("mqtt_message_format", MSGFORMAT_ITEMS,NO_ENCODING); //$NON-NLS-1$ 120 | private final JLabeledRadioI18N topicChoice = new JLabeledRadioI18N("mqtt_topic_choice", TOPIC_CHOICES,ROUND_ROBIN); //$NON-NLS-1$ 121 | private final JCheckBox connectionPerTopic = new JCheckBox(JMeterUtils.getResString("mqtt_connection_per_topic"), false); // $NON-NLS-1$ 122 | private final JCheckBox suffixClientId = new JCheckBox(JMeterUtils.getResString("mqtt_suffix_client_id"),true); // $NON-NLS-1$ 123 | private final JLabeledTextField suffixLength = new JLabeledTextField(JMeterUtils.getResString("mqtt_suffix_length")); //$NON-NLS-1$ 124 | // For messages content 125 | private final JCheckBox useTimeStamp = new JCheckBox(JMeterUtils.getResString("mqtt_use_time_stamp"), false); // $NON-NLS-1$ 126 | private final JCheckBox useNumberSeq = new JCheckBox(JMeterUtils.getResString("mqtt_use_number_seq"), false); // $NON-NLS-1$ 127 | private final JCheckBox isRetained = new JCheckBox(JMeterUtils.getResString("mqtt_send_as_retained_msg"), false); // $NON-NLS-1$ 128 | private final JLabeledRadioI18N typeQoSValue = new JLabeledRadioI18N("mqtt_qos", QTYPES_ITEMS,AT_MOST_ONCE); //$NON-NLS-1$ 129 | private final JLabeledRadioI18N typeGeneratedValue = new JLabeledRadioI18N("mqtt_type_of_generated_value", VALTYPES_ITEMS,INT); //$NON-NLS-1$ 130 | private final JLabeledRadioI18N typeFixedValue = new JLabeledRadioI18N("mqtt_type_of_fixed_value", FVALTYPES_ITEMS,INT); //$NON-NLS-1$ 131 | private final JLabeledTextField min = new JLabeledTextField(JMeterUtils.getResString("mqtt_min_value")); //$NON-NLS-1$ 132 | private final JLabeledTextField max = new JLabeledTextField(JMeterUtils.getResString("mqtt_max_value")); //$NON-NLS-1$ 133 | private final JLabeledTextField value = new JLabeledTextField(JMeterUtils.getResString("mqtt_value")); //$NON-NLS-1$ 134 | private final JLabeledRadioI18N typeRandom = new JLabeledRadioI18N("mqtt_type_random", RANTYPES_ITEMS,PSEUDO); //$NON-NLS-1$ 135 | private final JLabeledTextField seed = new JLabeledTextField(JMeterUtils.getResString("mqtt_seed_random")); //$NON-NLS-1$ 136 | private final JLabel textArea = new JLabel(JMeterUtils.getResString("mqtt_text_area")); 137 | private final JTextScrollPane textPanel = new JTextScrollPane(textMessage); 138 | private final JLabeledTextField clientId = new JLabeledTextField(JMeterUtils.getResString("mqtt_client_id")); //$NON-NLS-1$ 139 | private JComboBox CharsetChooser =new JComboBox(new String[] { "UTF-8", "UTF-16", "US-ASCII","UTF-16BE","UTF-16LE","ISO-8859-1"}); 140 | private final JLabeledTextField sizeArray = new JLabeledTextField(JMeterUtils.getResString("mqtt_size_array")); //$NON-NLS-1$ 141 | public MQTTPublisherGui() { 142 | init(); 143 | } 144 | 145 | private void init() { 146 | setLayout(new BorderLayout()); 147 | setBorder(makeBorder()); 148 | add(makeTitlePanel(), BorderLayout.NORTH); 149 | JPanel mainPanel = new VerticalPanel(); 150 | add(mainPanel, BorderLayout.CENTER); 151 | //-----------------------------------URL/CLIENT_ID---------------------------------------// 152 | JPanel DPanel = new JPanel(); 153 | DPanel.setLayout(new BoxLayout(DPanel, BoxLayout.X_AXIS)); 154 | DPanel.add(urlField); 155 | DPanel.add(clientId); 156 | DPanel.add(suffixClientId); 157 | DPanel.add(suffixLength); 158 | JPanel ControlPanel = new VerticalPanel(); 159 | ControlPanel.add(DPanel); 160 | ControlPanel.add(createDestinationPane()); 161 | ControlPanel.add(createAuthPane()); 162 | ControlPanel.add(createOtherPane()); 163 | ControlPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray),"Connection Info")); 164 | mainPanel.add(ControlPanel); 165 | //---------------------------------------Message Format----------------------------------// 166 | JPanel FormatPanel = new JPanel(); 167 | FormatPanel.setLayout(new BoxLayout(FormatPanel, BoxLayout.X_AXIS)); 168 | msgFormat.setLayout(new BoxLayout(msgFormat, BoxLayout.X_AXIS)); 169 | FormatPanel.add(msgFormat); 170 | Dimension minSize = new Dimension(10, 15); 171 | Dimension prefSize = new Dimension(10, 15); 172 | Dimension maxSize = new Dimension(Short.MAX_VALUE, 100); 173 | FormatPanel.add(new Box.Filler(minSize, prefSize, maxSize)); 174 | FormatPanel.add(CharsetChooser); 175 | JPanel EncodePanel = new VerticalPanel(); 176 | EncodePanel.add(FormatPanel); 177 | EncodePanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray),"Encoding")); 178 | mainPanel.add(EncodePanel); 179 | JPanel StampPanel = new VerticalPanel(); 180 | StampPanel.add(useTimeStamp); 181 | StampPanel.add(useNumberSeq); 182 | StampPanel.add(isRetained); 183 | typeQoSValue.setLayout(new BoxLayout(typeQoSValue, BoxLayout.X_AXIS)); 184 | StampPanel.add(this.typeQoSValue); 185 | StampPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray),"Option")); 186 | mainPanel.add(StampPanel); 187 | //--------------------------------------Message Type-------------------------------------// 188 | JPanel ContentPanel = new VerticalPanel(); 189 | msgChoice.setLayout(new BoxLayout(msgChoice, BoxLayout.X_AXIS)); 190 | ContentPanel.add(msgChoice); 191 | ContentPanel.add(sizeArray); 192 | 193 | //----------------------------------Fixed Value Panel------------------------------------// 194 | JPanel FPanel = new JPanel(); 195 | typeFixedValue.setLayout(new BoxLayout(typeFixedValue, BoxLayout.Y_AXIS)); 196 | FPanel.add(typeFixedValue); 197 | FPanel.add(value); 198 | ContentPanel.add(FPanel); 199 | //----------------------------------Generated Value Panel--------------------------------// 200 | JPanel GPanel = new JPanel(); 201 | typeGeneratedValue.setLayout(new BoxLayout(typeGeneratedValue, BoxLayout.Y_AXIS)); 202 | GPanel.add(typeGeneratedValue); 203 | GPanel.add(min); 204 | GPanel.add(max); 205 | this.typeRandom.setLayout(new BoxLayout(typeRandom, BoxLayout.Y_AXIS)); 206 | GPanel.add(typeRandom); 207 | GPanel.add(seed); 208 | ContentPanel.add(GPanel); 209 | //---------------------------------Big Volume ------------------------------------------// 210 | 211 | ContentPanel.add(sizeArray); 212 | //-------------------------------------Content Panel -----------------------------------// 213 | 214 | JPanel messageContentPanel = new JPanel(new BorderLayout()); 215 | messageContentPanel.add(this.textArea, BorderLayout.NORTH); 216 | messageContentPanel.add(this.textPanel,BorderLayout.CENTER); 217 | ContentPanel.add(messageContentPanel); 218 | ContentPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.gray),"Content")); 219 | mainPanel.add(ContentPanel); 220 | useAuth.addChangeListener(this); 221 | msgChoice.addChangeListener(this); 222 | typeFixedValue.addChangeListener(this); 223 | typeQoSValue.addChangeListener(this); 224 | typeRandom.addChangeListener(this); 225 | msgFormat.addChangeListener(this); 226 | suffixClientId.addChangeListener(this); 227 | 228 | } 229 | 230 | /** 231 | * 232 | * @return JPanel Panel with checkbox to choose user and password 233 | */ 234 | private Component createAuthPane() { 235 | JPanel panel = new JPanel(); 236 | panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); 237 | panel.add(useAuth); 238 | panel.add(Box.createHorizontalStrut(10)); 239 | panel.add(mqttUser); 240 | panel.add(Box.createHorizontalStrut(10)); 241 | panel.add(mqttPwd); 242 | return panel; 243 | } 244 | 245 | private Component createOtherPane() { 246 | JPanel panel = new JPanel(); 247 | panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); 248 | panel.add(iterations); 249 | panel.add(Box.createHorizontalStrut(10)); 250 | panel.add(keepAlive); 251 | return panel; 252 | } 253 | 254 | /** 255 | * 256 | * @return JPanel that contains destination infos 257 | */ 258 | private Component createDestinationPane() { 259 | JPanel panel = new VerticalPanel(); //new BorderLayout(3, 0) 260 | this.mqttDestination.setLayout((new BoxLayout(mqttDestination, BoxLayout.X_AXIS))); 261 | panel.add(mqttDestination); 262 | JPanel TPanel = new JPanel(); 263 | TPanel.setLayout(new BoxLayout(TPanel,BoxLayout.X_AXIS)); 264 | this.connectionPerTopic.setLayout(new BoxLayout(connectionPerTopic,BoxLayout.X_AXIS)); 265 | this.connectionPerTopic.setAlignmentX(CENTER_ALIGNMENT); 266 | TPanel.add(connectionPerTopic); 267 | TPanel.add(Box.createHorizontalStrut(100)); 268 | this.topicChoice.setLayout(new BoxLayout(topicChoice,BoxLayout.X_AXIS)); 269 | TPanel.add(topicChoice); 270 | panel.add(TPanel); 271 | return panel; 272 | } 273 | 274 | /** 275 | * To Clear the GUI 276 | */ 277 | @Override 278 | public void clearGui() { 279 | super.clearGui(); 280 | urlField.setText(""); // $NON-NLS-1$ 281 | mqttDestination.setText(""); // $NON-NLS-1$ 282 | mqttUser.setText(""); // $NON-NLS-1$ 283 | mqttPwd.setText(""); // $NON-NLS-1$ 284 | textMessage.setInitialText(""); // $NON-NLS-1$ 285 | msgChoice.setText(""); // $NON-NLS-1$ 286 | updateConfig(USE_TEXT_RSC); 287 | msgChoice.setText(TEXT_MSG_RSC); 288 | iterations.setText("1"); // $NON-NLS-1$ 289 | keepAlive.setText("60"); 290 | useAuth.setSelected(false); 291 | mqttUser.setEnabled(false); 292 | mqttPwd.setEnabled(false); 293 | destSetup.setText(DEST_SETUP_STATIC); 294 | textArea.setText(""); 295 | clientId.setText(""); 296 | connectionPerTopic.setSelected(false); 297 | 298 | 299 | } 300 | 301 | private void setupSamplerProperties(PublisherSampler sampler) { 302 | this.configureTestElement(sampler); 303 | sampler.setProviderUrl(urlField.getText()); 304 | sampler.setDestination(mqttDestination.getText()); 305 | sampler.setUsername(mqttUser.getText()); 306 | sampler.setPassword(mqttPwd.getText()); 307 | sampler.setTextMessage(textMessage.getText()); 308 | sampler.setMessageChoice(msgChoice.getText()); 309 | sampler.setIterations(iterations.getText()); 310 | sampler.setKeepAlive(keepAlive.getText()); 311 | sampler.setUseAuth(useAuth.isSelected()); 312 | sampler.setQuality(typeQoSValue.getText()); 313 | sampler.setRetained(isRetained.isSelected()); 314 | sampler.setTYPE_FIXED_VALUE(typeFixedValue.getText()); 315 | sampler.setFIXED_VALUE(this.value.getText()); 316 | sampler.setTYPE_RANDOM_VALUE(this.typeRandom.getText()); 317 | sampler.setMAX_RANDOM_VALUE(this.max.getText()); 318 | sampler.setMIN_RANDOM_VALUE(this.min.getText()); 319 | sampler.setTYPE_GENERATED_VALUE(this.typeGeneratedValue.getText()); 320 | sampler.setSEED(this.seed.getText()); 321 | sampler.setUSE_TIMESTAMP(useTimeStamp.isSelected()); 322 | sampler.setUSE_NUMBER_SEQUENCE(useNumberSeq.isSelected()); 323 | sampler.setCLIENT_ID(clientId.getText()); 324 | sampler.setFORMAT(msgFormat.getText()); 325 | sampler.setCHARSET((String) this.CharsetChooser.getSelectedItem()); 326 | sampler.setSIZE_ARRAY(this.sizeArray.getText()); 327 | sampler.setSTRATEGY(this.topicChoice.getText()); 328 | sampler.setOneConnectionPerTopic(this.connectionPerTopic.isSelected()); 329 | sampler.setRandomSuffix(this.suffixClientId.isSelected()); 330 | sampler.setLength(this.suffixLength.getText()); 331 | } 332 | 333 | /** 334 | * the implementation loads the URL and the soap action for the request. 335 | */ 336 | @Override 337 | public void configure(TestElement el) { 338 | super.configure(el); 339 | PublisherSampler sampler = (PublisherSampler) el; 340 | urlField.setText(sampler.getProviderUrl()); 341 | mqttDestination.setText(sampler.getDestination()); 342 | mqttUser.setText(sampler.getUsername()); 343 | mqttPwd.setText(sampler.getPassword()); 344 | textMessage.setInitialText(sampler.getTextMessage()); 345 | textMessage.setCaretPosition(0); 346 | msgChoice.setText(sampler.getMessageChoice()); 347 | iterations.setText(sampler.getIterations()); 348 | keepAlive.setText(String.valueOf(sampler.getKeepAlive())); 349 | clientId.setText(sampler.getCLIENT_ID()); 350 | suffixLength.setText(sampler.getLength()); 351 | useAuth.setSelected(sampler.isUseAuth()); 352 | mqttUser.setEnabled(useAuth.isSelected()); 353 | mqttPwd.setEnabled(useAuth.isSelected()); 354 | updateChoice(msgChoice.getText()); 355 | updateChoice(msgFormat.getText()); 356 | updateChoice("Suffix="+String.valueOf(this.suffixClientId.isSelected())); 357 | 358 | } 359 | 360 | @Override 361 | public TestElement createTestElement() { 362 | PublisherSampler sampler = new PublisherSampler(); 363 | setupSamplerProperties(sampler); 364 | return sampler; 365 | } 366 | /** 367 | * To Update the parameter of field in the GUI 368 | * 369 | * @param command 370 | */ 371 | 372 | private void updateConfig(String command) { 373 | 374 | if (command.equals(USE_TEXT_RSC)) { 375 | textMessage.setEnabled(true); 376 | 377 | } 378 | 379 | } 380 | 381 | @Override 382 | public String getLabelResource() { 383 | return "mqtt_publisher"; //$NON-NLS-1$ 384 | } 385 | 386 | @Override 387 | public void modifyTestElement(TestElement s) { 388 | PublisherSampler sampler = (PublisherSampler) s; 389 | setupSamplerProperties(sampler); 390 | } 391 | 392 | /** 393 | * When we change some parameter by clicking on the GUI 394 | */ 395 | @Override 396 | public void stateChanged(ChangeEvent event) { 397 | 398 | 399 | if (event.getSource() == msgChoice) { 400 | updateChoice(msgChoice.getText()); 401 | } else if (event.getSource() == useAuth) { 402 | mqttUser.setEnabled(useAuth.isSelected()); 403 | mqttPwd.setEnabled(useAuth.isSelected()); 404 | } 405 | else if (event.getSource()==msgFormat){ 406 | updateChoice(msgFormat.getText()); 407 | } 408 | else if(event.getSource()==suffixClientId){ 409 | updateChoice("Suffix="+String.valueOf(this.suffixClientId.isSelected())); 410 | } 411 | } 412 | 413 | /** 414 | * To Update the choice of message to send 415 | * 416 | * @param command 417 | */ 418 | private void updateChoice(String command) { 419 | 420 | if(TEXT_MSG_RSC.equals(command)){ 421 | this.typeGeneratedValue.setVisible(false); 422 | this.typeFixedValue.setVisible(false); 423 | this.max.setVisible(false); 424 | this.min.setVisible(false); 425 | this.value.setVisible(false); 426 | this.typeRandom.setVisible(false); 427 | this.seed.setVisible(false); 428 | this.textArea.setVisible(true); 429 | this.textPanel.setVisible(true); 430 | this.sizeArray.setVisible(false); } 431 | else if(GENERATED_VALUE.equals(command)) { 432 | this.typeFixedValue.setVisible(false); 433 | this.value.setVisible(false); 434 | this.textArea.setVisible(false); 435 | this.textPanel.setVisible(false); 436 | this.typeGeneratedValue.setVisible(true); 437 | this.max.setVisible(true); 438 | this.min.setVisible(true); 439 | this.typeRandom.setVisible(true); 440 | this.seed.setVisible(true); 441 | this.sizeArray.setVisible(false); 442 | } else if(FIXED_VALUE.equals(command)){ 443 | this.typeGeneratedValue.setVisible(false); 444 | this.typeFixedValue.setVisible(true); 445 | this.max.setVisible(false); 446 | this.min.setVisible(false); 447 | this.value.setVisible(true); 448 | this.typeRandom.setVisible(false); 449 | this.seed.setVisible(false); 450 | this.textArea.setVisible(false); 451 | this.textPanel.setVisible(false); 452 | this.sizeArray.setVisible(false); } 453 | else if(BIG_VOLUME.equals(command)){ 454 | this.typeGeneratedValue.setVisible(false); 455 | this.typeFixedValue.setVisible(false); 456 | this.max.setVisible(false); 457 | this.min.setVisible(false); 458 | this.value.setVisible(false); 459 | this.typeRandom.setVisible(false); 460 | this.seed.setVisible(false); 461 | this.textArea.setVisible(false); 462 | this.textPanel.setVisible(false); 463 | this.sizeArray.setVisible(true); 464 | } 465 | else if(BINARY.equals(command)){ 466 | this.CharsetChooser.setVisible(false); 467 | } 468 | else if(BINHEX.equals(command)){ 469 | this.CharsetChooser.setVisible(false); 470 | } 471 | else if(BASE64.equals(command)){ 472 | this.CharsetChooser.setVisible(false); 473 | } 474 | else if(PLAIN_TEXT.equals(command)){ 475 | this.CharsetChooser.setVisible(true); 476 | } 477 | else if(NO_ENCODING.equals(command)){ 478 | this.CharsetChooser.setVisible(false); 479 | } 480 | else if("suffix=true".equalsIgnoreCase(command)){ 481 | this.suffixLength.setVisible(true); 482 | 483 | } 484 | else if("suffix=false".equalsIgnoreCase(command)){ 485 | this.suffixLength.setVisible(false); 486 | } 487 | validate(); 488 | } 489 | 490 | } 491 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jmeter/protocol/mqtt/client/MqttPublisher.java: -------------------------------------------------------------------------------- 1 | /** 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | 19 | Copyright 2014 University Joseph Fourier, LIG Laboratory, ERODS Team 20 | 21 | */ 22 | package org.apache.jmeter.protocol.mqtt.client; 23 | import java.io.ByteArrayOutputStream; 24 | import java.io.Closeable; 25 | import java.io.DataOutputStream; 26 | import java.io.IOException; 27 | import java.io.Serializable; 28 | import java.net.URISyntaxException; 29 | import java.security.SecureRandom; 30 | import java.util.Date; 31 | import java.util.Random; 32 | import java.util.concurrent.TimeUnit; 33 | import java.util.concurrent.TimeoutException; 34 | import java.util.concurrent.atomic.AtomicInteger; 35 | 36 | import javax.net.ssl.SSLContext; 37 | 38 | import org.apache.commons.codec.binary.Base64; 39 | import org.apache.commons.codec.binary.BinaryCodec; 40 | import org.apache.commons.codec.binary.Hex; 41 | import org.apache.jmeter.config.Arguments; 42 | import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; 43 | import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; 44 | import org.apache.jmeter.protocol.mqtt.control.gui.MQTTPublisherGui; 45 | import org.apache.jmeter.protocol.mqtt.sampler.PublisherSampler; 46 | import org.apache.jmeter.samplers.SampleResult; 47 | import org.apache.jmeter.threads.JMeterContext; 48 | import org.apache.jmeter.threads.JMeterContextService; 49 | import org.fusesource.mqtt.client.FutureConnection; 50 | import org.fusesource.mqtt.client.MQTT; 51 | import org.fusesource.mqtt.client.QoS; 52 | 53 | 54 | public class MqttPublisher extends AbstractJavaSamplerClient implements 55 | Serializable, Closeable { 56 | private static final long serialVersionUID = 1L; 57 | private AtomicInteger total = new AtomicInteger(0); 58 | private FutureConnection[] connectionArray; 59 | public static int numSeq=0; 60 | private Random generator = new Random(); 61 | private SecureRandom secureGenerator= new SecureRandom(); 62 | private String label = ""; 63 | private static final int MQTT_CONNECTION_TIMEOUT = 10; 64 | 65 | @Override 66 | public Arguments getDefaultParameters() { 67 | Arguments defaultParameters = new Arguments(); 68 | defaultParameters.addArgument("HOST", "tcp://localhost:1883"); 69 | defaultParameters.addArgument("CLIENT_ID", "Hiep"); 70 | defaultParameters.addArgument("TOPIC", "TEST.MQTT"); 71 | defaultParameters.addArgument("AGGREGATE", "1"); 72 | defaultParameters.addArgument("MESSAGE", "This is my test message"); 73 | return defaultParameters; 74 | } 75 | 76 | public void setupTest(JavaSamplerContext context) { 77 | label = context.getParameter("LABEL"); 78 | 79 | String host = context.getParameter("HOST"); 80 | String clientId = context.getParameter("CLIENT_ID"); 81 | if ("TRUE".equalsIgnoreCase(context.getParameter("RANDOM_SUFFIX"))) { 82 | clientId = MqttPublisher.getClientId(clientId, Integer.parseInt(context.getParameter("SUFFIX_LENGTH"))); 83 | } 84 | if ("FALSE".equals(context.getParameter("PER_TOPIC"))) { 85 | if ("TRUE".equals(context.getParameter("AUTH"))) { 86 | setupTest(host, clientId, context.getParameter("USER"), context.getParameter("PASSWORD"), 1, context.getIntParameter("KEEP_ALIVE")); 87 | } else { 88 | setupTest(host, clientId, 1, context.getIntParameter("KEEP_ALIVE")); 89 | } 90 | 91 | } else if ("TRUE".equals(context.getParameter("PER_TOPIC"))) { 92 | String topics = context.getParameter("TOPIC"); 93 | String[] topicArray = topics.split("\\s*,\\s*"); 94 | int size = topicArray.length; 95 | 96 | if ("TRUE".equals(context.getParameter("AUTH"))) { 97 | setupTest(host, clientId, context.getParameter("USER"), context.getParameter("PASSWORD"), size, context.getIntParameter("KEEP_ALIVE")); 98 | } else { 99 | setupTest(host, clientId, size, context.getIntParameter("KEEP_ALIVE")); 100 | } 101 | } 102 | } 103 | 104 | public void setupTest(String host, String clientId, int size, int keepAlive) { 105 | try { 106 | JMeterContext jmcx = JMeterContextService.getContext(); 107 | this.connectionArray= new FutureConnection[size]; 108 | if(size==1){ 109 | this.connectionArray[0]= createConnection(host,clientId+jmcx.getThreadNum(), keepAlive); 110 | this.connectionArray[0].connect().await(MQTT_CONNECTION_TIMEOUT, TimeUnit.SECONDS); 111 | this.getLogger().info("NUMBER CONNECTION: "+PublisherSampler.numberOfConnection.getAndIncrement()); 112 | } 113 | else 114 | { 115 | for(int i = 0;i< size;i++){ 116 | this.connectionArray[i]= createConnection(host,clientId+jmcx.getThreadNum()+i, keepAlive); 117 | this.connectionArray[0].connect().await(MQTT_CONNECTION_TIMEOUT, TimeUnit.SECONDS); 118 | this.getLogger().info("NUMBER CONNECTION: "+PublisherSampler.numberOfConnection.getAndIncrement()); 119 | } 120 | } 121 | 122 | }catch(TimeoutException ex) { 123 | getLogger().error("The connection to MQTT server is timeout after " + MQTT_CONNECTION_TIMEOUT + " seconds."); 124 | }catch (Exception e) { 125 | getLogger().error(e.getMessage()); 126 | } 127 | 128 | } 129 | public void setupTest(String host, String clientId, String user, String password, int size, int keepAlive) { 130 | try { 131 | JMeterContext jmcx = JMeterContextService.getContext(); 132 | this.connectionArray= new FutureConnection[size]; 133 | 134 | if(size==1){ 135 | this.connectionArray[0]= createConnection(host,clientId+jmcx.getThreadNum(),user,password, keepAlive); 136 | this.connectionArray[0].connect().await(MQTT_CONNECTION_TIMEOUT, TimeUnit.SECONDS); 137 | this.getLogger().info("NUMBER CONNECTION: "+PublisherSampler.numberOfConnection.getAndIncrement()); 138 | 139 | } else { 140 | for(int i = 0;i< size;i++){ 141 | this.connectionArray[i]= createConnection(host,clientId+jmcx.getThreadNum()+i,user,password, keepAlive); 142 | this.connectionArray[0].connect().await(MQTT_CONNECTION_TIMEOUT, TimeUnit.SECONDS); 143 | this.getLogger().info("NUMBER CONNECTION: "+PublisherSampler.numberOfConnection.getAndIncrement()); 144 | } 145 | } 146 | getLogger().info("Connection successful.."); 147 | } catch(TimeoutException ex) { 148 | getLogger().error("The connection to MQTT server is timeout after " + MQTT_CONNECTION_TIMEOUT + " seconds."); 149 | } catch(Exception e) { 150 | getLogger().error(e.getMessage()); 151 | } 152 | } 153 | 154 | private FutureConnection createConnection(String host,String clientId, int keepAlive) { 155 | try { 156 | MQTT client = new MQTT(); 157 | client.setHost(host); 158 | client.setClientId(clientId); 159 | client.setKeepAlive((short) keepAlive); 160 | if(host != null && (host.trim().toLowerCase().startsWith("ssl://"))) { 161 | SSLContext context = SSLUtil.getContext(); 162 | if(context != null) { 163 | client.setSslContext(context); 164 | } 165 | } 166 | return client.futureConnection(); 167 | } catch (URISyntaxException e) { 168 | getLogger().error(e.getMessage()); 169 | return null; 170 | } 171 | 172 | } 173 | private FutureConnection createConnection(String host,String clientId,String user, String password, int keepAlive) { 174 | 175 | try { 176 | MQTT client = new MQTT(); 177 | client.setHost(host); 178 | client.setUserName(user); 179 | client.setPassword(password); 180 | client.setClientId(clientId); 181 | client.setKeepAlive((short) keepAlive); 182 | 183 | if(host != null && (host.trim().toLowerCase().startsWith("ssl://"))) { 184 | SSLContext context = SSLUtil.getContext(); 185 | if(context != null) { 186 | client.setSslContext(context); 187 | } 188 | } 189 | return client.futureConnection(); 190 | } catch (URISyntaxException e) { 191 | getLogger().error(e.getMessage()); 192 | return null; 193 | } 194 | 195 | } 196 | 197 | private void produce(JavaSamplerContext context) throws Exception { 198 | 199 | // ---------------------Type of message -------------------// 200 | 201 | if ("FIXED".equals(context.getParameter("TYPE_MESSAGE"))) { 202 | produce(context.getParameter("MESSAGE"), 203 | context.getParameter("TOPIC"), 204 | Integer.parseInt(context.getParameter("AGGREGATE")), 205 | context.getParameter("QOS"), 206 | context.getParameter("RETAINED"), 207 | context.getParameter("TIME_STAMP"), 208 | context.getParameter("NUMBER_SEQUENCE"), 209 | context.getParameter("TYPE_VALUE"), 210 | context.getParameter("FORMAT"), 211 | context.getParameter("CHARSET"), 212 | context.getParameter("LIST_TOPIC"), 213 | context.getParameter("STRATEGY"), 214 | context.getParameter("PER_TOPIC")); 215 | 216 | } else if ("RANDOM".equals(context.getParameter("TYPE_MESSAGE"))) { 217 | 218 | produceRandomly(context.getParameter("SEED"),context.getParameter("MIN_RANDOM_VALUE"), 219 | context.getParameter("MAX_RANDOM_VALUE"),context.getParameter("TYPE_RANDOM_VALUE"), 220 | context.getParameter("TOPIC"),Integer.parseInt(context.getParameter("AGGREGATE")), 221 | context.getParameter("QOS"),context.getParameter("RETAINED"), 222 | context.getParameter("TIME_STAMP"),context.getParameter("NUMBER_SEQUENCE"), 223 | context.getParameter("TYPE_VALUE"), 224 | context.getParameter("FORMAT"), 225 | context.getParameter("CHARSET"), 226 | context.getParameter("LIST_TOPIC"), 227 | context.getParameter("STRATEGY"), 228 | context.getParameter("PER_TOPIC")); 229 | 230 | } else if ("TEXT".equals(context.getParameter("TYPE_MESSAGE"))) { 231 | produce(context.getParameter("MESSAGE"), 232 | context.getParameter("TOPIC"), 233 | Integer.parseInt(context.getParameter("AGGREGATE")), 234 | context.getParameter("QOS"), 235 | context.getParameter("RETAINED"), 236 | context.getParameter("TIME_STAMP"), 237 | context.getParameter("NUMBER_SEQUENCE"), 238 | context.getParameter("TYPE_VALUE"), 239 | context.getParameter("FORMAT"), 240 | context.getParameter("CHARSET"), 241 | context.getParameter("LIST_TOPIC"), 242 | context.getParameter("STRATEGY"), 243 | context.getParameter("PER_TOPIC")); 244 | } else if("BYTE_ARRAY".equals(context.getParameter("TYPE_MESSAGE"))){ 245 | produceBigVolume( 246 | context.getParameter("TOPIC"), 247 | Integer.parseInt(context.getParameter("AGGREGATE")), 248 | context.getParameter("QOS"), 249 | context.getParameter("RETAINED"), 250 | context.getParameter("TIME_STAMP"), 251 | context.getParameter("NUMBER_SEQUENCE"), 252 | context.getParameter("FORMAT"), 253 | context.getParameter("CHARSET"), 254 | context.getParameter("SIZE_ARRAY"), 255 | context.getParameter("LIST_TOPIC"), 256 | context.getParameter("STRATEGY"), 257 | context.getParameter("PER_TOPIC")); 258 | } 259 | 260 | } 261 | 262 | 263 | 264 | private void produceBigVolume(String topic, int aggregate, 265 | String qos, String isRetained, String useTimeStamp, 266 | String useNumberSeq, String format, String charset,String sizeArray,String isListTopic,String strategy,String isPerTopic) throws Exception { 267 | // Quality 268 | QoS quality = null; 269 | if (MQTTPublisherGui.EXACTLY_ONCE.equals(qos)) { 270 | quality = QoS.EXACTLY_ONCE; 271 | } else if (MQTTPublisherGui.AT_LEAST_ONCE.equals(qos)) { 272 | quality = QoS.AT_LEAST_ONCE; 273 | } else if (MQTTPublisherGui.AT_MOST_ONCE.equals(qos)) { 274 | quality = QoS.AT_MOST_ONCE; 275 | 276 | } 277 | // Retained 278 | boolean retained = false; 279 | if ("TRUE".equals(isRetained)) 280 | retained = true; 281 | // List topic 282 | if("FALSE".equals(isListTopic)){ 283 | for (int i = 0; i < aggregate; ++i) { 284 | byte[] payload = createBigVolume( useTimeStamp, useNumberSeq,format, charset,sizeArray); 285 | checkConnActivity(this.connectionArray[0]); 286 | this.connectionArray[0].publish(topic,payload,quality,retained).await(); 287 | total.incrementAndGet(); 288 | } 289 | } else if("TRUE".equals(isListTopic)){ 290 | String[] topicArray= topic.split("\\s*,\\s*"); 291 | int length= topicArray.length; 292 | Random rand = new Random(); 293 | for (int i = 0; i < aggregate; ++i) { 294 | byte[] payload = createBigVolume( useTimeStamp, useNumberSeq,format, charset,sizeArray); 295 | //-----------------------------Publish after strategy---------------------// 296 | if("ROUND_ROBIN".equals(strategy)){ 297 | for(int j=0;j