diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
index 7ae4604c22..ef365ca776 100644
--- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
@@ -134,7 +134,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
@Override
protected void configure(HttpSecurity http) throws Exception {
- http.headers().frameOptions().disable()
+ http.headers().cacheControl().disable().frameOptions().disable()
.and()
.csrf().disable()
.exceptionHandling()
diff --git a/tools/pom.xml b/tools/pom.xml
index 552260be58..614ebd7d4d 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -17,89 +17,121 @@
-->
- 4.0.0
-
- org.thingsboard
- 1.0.1-SNAPSHOT
- thingsboard
-
+ 4.0.0
+
org.thingsboard
- tools
- jar
+ 1.0.1-SNAPSHOT
+ thingsboard
+
+ org.thingsboard
+ tools
+ jar
- Thingsboard Server Tools
- http://thingsboard.org
+ Thingsboard Server Tools
+ http://thingsboard.org
-
- UTF-8
- ${basedir}/..
-
+
+ UTF-8
+ ${basedir}/..
+
-
-
- org.thingsboard.common
- data
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.eclipse.paho
- org.eclipse.paho.client.mqttv3
-
-
- org.slf4j
- slf4j-api
-
-
- org.slf4j
- log4j-over-slf4j
-
-
- ch.qos.logback
- logback-core
-
-
- ch.qos.logback
- logback-classic
-
-
- org.springframework
- spring-test
-
-
- junit
- junit
- test
-
-
- org.mockito
- mockito-all
- test
-
-
- io.gatling.highcharts
- gatling-charts-highcharts
- test
-
-
- com.github.mnogu
- gatling-mqtt
- test
-
-
+
+
+ org.thingsboard.common
+ data
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.eclipse.paho
+ org.eclipse.paho.client.mqttv3
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ log4j-over-slf4j
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.springframework
+ spring-test
+
+
+ junit
+ junit
+ test
+
+
+ org.mockito
+ mockito-all
+ test
+
+
+ io.gatling.highcharts
+ gatling-charts-highcharts
+ test
+
+
+ com.github.mnogu
+ gatling-mqtt
+ test
+
+
-
-
-
- net.alchim31.maven
- scala-maven-plugin
-
-
- io.gatling
- gatling-maven-plugin
-
-
-
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
+
+ package
+
+ shade
+
+
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+
+ org.thingsboard.client.tools.MqttStressTestTool
+
+
+
+
+
+
+
+
+ net.alchim31.maven
+ scala-maven-plugin
+
+
+ io.gatling
+ gatling-maven-plugin
+
+
+
diff --git a/tools/src/main/java/org/thingsboard/client/tools/MqttStressTestClient.java b/tools/src/main/java/org/thingsboard/client/tools/MqttStressTestClient.java
index 66970a9150..5d1af34b4c 100644
--- a/tools/src/main/java/org/thingsboard/client/tools/MqttStressTestClient.java
+++ b/tools/src/main/java/org/thingsboard/client/tools/MqttStressTestClient.java
@@ -43,10 +43,10 @@ public class MqttStressTestClient {
this.client = new MqttAsyncClient(brokerUri, clientId, persistence);
}
- public void connect() throws MqttException {
+ public IMqttToken connect() throws MqttException {
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(deviceToken);
- client.connect(options, null, new IMqttActionListener() {
+ return client.connect(options, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken iMqttToken) {
log.info("OnSuccess");
@@ -63,6 +63,22 @@ public class MqttStressTestClient {
client.disconnect();
}
+
+
+ public void warmUp(byte[] data) throws MqttException {
+ MqttMessage msg = new MqttMessage(data);
+ client.publish("v1/devices/me/telemetry", msg, null, new IMqttActionListener() {
+ @Override
+ public void onSuccess(IMqttToken asyncActionToken) {
+ }
+
+ @Override
+ public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
+ }
+ }).waitForCompletion();
+ }
+
+
public void publishTelemetry(byte[] data) throws MqttException {
long sendTime = System.currentTimeMillis();
MqttMessage msg = new MqttMessage(data);
@@ -70,14 +86,12 @@ public class MqttStressTestClient {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
long ackTime = System.currentTimeMillis();
-// log.info("Delivery time: {}", ackTime - sendTime);
results.onResult(true, ackTime - sendTime);
}
@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
long failTime = System.currentTimeMillis();
-// log.info("Failure time: {}", failTime - sendTime);
results.onResult(false, failTime - sendTime);
}
});
diff --git a/tools/src/main/java/org/thingsboard/client/tools/MqttStressTestTool.java b/tools/src/main/java/org/thingsboard/client/tools/MqttStressTestTool.java
index 7852c09197..08e5cfe6d3 100644
--- a/tools/src/main/java/org/thingsboard/client/tools/MqttStressTestTool.java
+++ b/tools/src/main/java/org/thingsboard/client/tools/MqttStressTestTool.java
@@ -14,16 +14,19 @@
* limitations under the License.
*/
+
package org.thingsboard.client.tools;
import lombok.extern.slf4j.Slf4j;
+import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.TimeUnit;
+import java.util.UUID;
+import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -32,60 +35,83 @@ import java.util.concurrent.atomic.AtomicLong;
@Slf4j
public class MqttStressTestTool {
- private static final long TEST_DURATION = TimeUnit.MINUTES.toMillis(1);
- private static final long TEST_ITERATION = TimeUnit.MILLISECONDS.toMillis(100);
- private static final long TEST_SUB_ITERATION = TimeUnit.MILLISECONDS.toMillis(2);
- private static final int DEVICE_COUNT = 100;
- private static final String BASE_URL = "http://localhost:8080";
- private static final String[] MQTT_URLS = {"tcp://localhost:1883"};
-// private static final String[] MQTT_URLS = {"tcp://localhost:1883", "tcp://localhost:1884", "tcp://localhost:1885"};
- private static final String USERNAME = "tenant@thingsboard.org";
- private static final String PASSWORD = "tenant";
-
-
public static void main(String[] args) throws Exception {
+ TestParams params = new TestParams();
+ ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10);
+
+
+ if (params.getDuration() % params.getIterationInterval() != 0) {
+ throw new IllegalArgumentException("Test Duration % Iteration Interval != 0");
+ }
+
+ if ((params.getIterationInterval() * 1000) % params.getDeviceCount() != 0) {
+ throw new IllegalArgumentException("Iteration Interval % Device Count != 0");
+ }
+
ResultAccumulator results = new ResultAccumulator();
AtomicLong value = new AtomicLong(Long.MAX_VALUE);
log.info("value: {} ", value.incrementAndGet());
- RestClient restClient = new RestClient(BASE_URL);
- restClient.login(USERNAME, PASSWORD);
+ RestClient restClient = new RestClient(params.getRestApiUrl());
+ restClient.login(params.getUsername(), params.getPassword());
List clients = new ArrayList<>();
- for (int i = 0; i < DEVICE_COUNT; i++) {
- Device device = restClient.createDevice("Device " + i);
+ List connectTokens = new ArrayList<>();
+ for (int i = 0; i < params.getDeviceCount(); i++) {
+ Device device = restClient.createDevice("Device " + UUID.randomUUID());
DeviceCredentials credentials = restClient.getCredentials(device.getId());
- String mqttURL = MQTT_URLS[i % MQTT_URLS.length];
+ String[] mqttUrls = params.getMqttUrls();
+ String mqttURL = mqttUrls[i % mqttUrls.length];
MqttStressTestClient client = new MqttStressTestClient(results, mqttURL, credentials.getCredentialsId());
- client.connect();
+ connectTokens.add(client.connect());
clients.add(client);
}
- Thread.sleep(1000);
+ for (IMqttToken tokens : connectTokens) {
+ tokens.waitForCompletion();
+ }
byte[] data = "{\"longKey\":73}".getBytes(StandardCharsets.UTF_8);
- long startTime = System.currentTimeMillis();
- int iterationsCount = (int) (TEST_DURATION / TEST_ITERATION);
- int subIterationsCount = (int) (TEST_ITERATION / TEST_SUB_ITERATION);
- if (clients.size() % subIterationsCount != 0) {
- throw new IllegalArgumentException("Invalid parameter exception!");
- }
- for (int i = 0; i < iterationsCount; i++) {
- for (int j = 0; j < subIterationsCount; j++) {
- int packSize = clients.size() / subIterationsCount;
- for (int k = 0; k < packSize; k++) {
- int clientIndex = packSize * j + k;
- clients.get(clientIndex).publishTelemetry(data);
- }
- Thread.sleep(TEST_SUB_ITERATION);
- }
+
+ for (MqttStressTestClient client : clients) {
+ client.warmUp(data);
}
+
Thread.sleep(1000);
+
+ long startTime = System.currentTimeMillis();
+ int iterationsCount = (int) (params.getDuration() / params.getIterationInterval());
+ int subIterationMicroSeconds = (int) ((params.getIterationInterval() * 1000) / params.getDeviceCount());
+
+ List> iterationFutures = new ArrayList<>();
+ for (int i = 0; i < iterationsCount; i++) {
+ long delay = i * params.getIterationInterval();
+ iterationFutures.add(scheduler.schedule((Callable) () -> {
+ long sleepMicroSeconds = 0L;
+ for (MqttStressTestClient client : clients) {
+ client.publishTelemetry(data);
+ sleepMicroSeconds += subIterationMicroSeconds;
+ if (sleepMicroSeconds > 1000) {
+ Thread.sleep(sleepMicroSeconds / 1000);
+ sleepMicroSeconds = sleepMicroSeconds % 1000;
+ }
+ }
+ return null;
+ }, delay, TimeUnit.MILLISECONDS));
+ }
+
+ for (ScheduledFuture future : iterationFutures) {
+ future.get();
+ }
+
+ Thread.sleep(1000);
+
for (MqttStressTestClient client : clients) {
client.disconnect();
}
log.info("Results: {} took {}ms", results, System.currentTimeMillis() - startTime);
+ scheduler.shutdownNow();
}
}
diff --git a/tools/src/main/java/org/thingsboard/client/tools/ResultAccumulator.java b/tools/src/main/java/org/thingsboard/client/tools/ResultAccumulator.java
index 197a45b7de..ac1fa198d9 100644
--- a/tools/src/main/java/org/thingsboard/client/tools/ResultAccumulator.java
+++ b/tools/src/main/java/org/thingsboard/client/tools/ResultAccumulator.java
@@ -76,7 +76,7 @@ public class ResultAccumulator {
@Override
public String toString() {
- return "org.thingsboard.client.tools.ResultAccumulator{" +
+ return "Result {" +
"successCount=" + getSuccessCount() +
", errorCount=" + getErrorCount() +
", totalTime=" + getTimeSpent() +
diff --git a/tools/src/main/java/org/thingsboard/client/tools/TestParams.java b/tools/src/main/java/org/thingsboard/client/tools/TestParams.java
new file mode 100644
index 0000000000..eb1328b61e
--- /dev/null
+++ b/tools/src/main/java/org/thingsboard/client/tools/TestParams.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright © 2016 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.client.tools;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+public class TestParams {
+ static final String TEST_PROPERTIES = "test.properties";
+ static final long DEFAULT_TEST_DURATION = TimeUnit.MINUTES.toMillis(1);
+ static final long DEFAULT_TEST_INTERVAL = TimeUnit.MILLISECONDS.toMillis(100);
+ static final int DEFAULT_DEVICE_COUNT = 100;
+ static final String DEFAULT_REST_URL = "http://localhost:8080";
+ static final String DEFAULT_MQTT_URLS = "tcp://localhost:1883";
+ static final String DEFAULT_USERNAME = "tenant@thingsboard.org";
+ static final String DEFAULT_PASSWORD = "tenant";
+
+ private Properties params = new Properties();
+
+ public TestParams() throws IOException {
+ try {
+ params.load(new FileInputStream(TEST_PROPERTIES));
+ } catch (Exception e) {
+ log.warn("Failed to read " + TEST_PROPERTIES);
+ }
+ }
+
+ public long getDuration() {
+ return Long.valueOf(params.getProperty("durationMs", Long.toString(DEFAULT_TEST_DURATION)));
+ }
+
+ public long getIterationInterval() {
+ return Long.valueOf(params.getProperty("iterationIntervalMs", Long.toString(DEFAULT_TEST_INTERVAL)));
+ }
+
+ public int getDeviceCount() {
+ return Integer.valueOf(params.getProperty("deviceCount", Integer.toString(DEFAULT_DEVICE_COUNT)));
+ }
+
+ public String getRestApiUrl() {
+ return params.getProperty("restUrl", DEFAULT_REST_URL);
+ }
+
+ public String[] getMqttUrls() {
+ return params.getProperty("mqttUrls", DEFAULT_MQTT_URLS).split(",");
+ }
+
+ public String getUsername() {
+ return params.getProperty("username", DEFAULT_USERNAME);
+ }
+
+ public String getPassword() {
+ return params.getProperty("password", DEFAULT_PASSWORD);
+ }
+}
diff --git a/tools/src/main/resources/logback.xml b/tools/src/main/resources/logback.xml
new file mode 100644
index 0000000000..11973fa644
--- /dev/null
+++ b/tools/src/main/resources/logback.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+ %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/test.properties b/tools/test.properties
new file mode 100644
index 0000000000..93efc60698
--- /dev/null
+++ b/tools/test.properties
@@ -0,0 +1,3 @@
+deviceCount=1000
+durationMs=5000
+iterationIntervalMs=250
diff --git a/ui/package.json b/ui/package.json
index 0354e09ad6..070c07fa7f 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -15,12 +15,12 @@
},
"dependencies": {
"ace-builds": "^1.2.5",
- "angular": "^1.5.8",
- "angular-animate": "^1.5.8",
- "angular-aria": "^1.5.8",
+ "angular": "1.5.8",
+ "angular-animate": "1.5.8",
+ "angular-aria": "1.5.8",
"angular-breadcrumb": "^0.4.1",
"angular-carousel": "^1.0.1",
- "angular-cookies": "^1.5.8",
+ "angular-cookies": "1.5.8",
"angular-drag-and-drop-lists": "^1.4.0",
"angular-fullscreen": "git://github.com/fabiobiondi/angular-fullscreen.git#master",
"angular-gridster": "^0.13.14",
@@ -29,11 +29,11 @@
"angular-material": "^1.1.1",
"angular-material-data-table": "^0.10.9",
"angular-material-icons": "^0.7.1",
- "angular-messages": "^1.5.8",
- "angular-route": "^1.5.8",
- "angular-sanitize": "^1.5.8",
+ "angular-messages": "1.5.8",
+ "angular-route": "1.5.8",
+ "angular-sanitize": "1.5.8",
"angular-storage": "0.0.15",
- "angular-touch": "^1.5.8",
+ "angular-touch": "1.5.8",
"angular-translate": "^2.12.1",
"angular-translate-handler-log": "^2.12.1",
"angular-translate-interpolation-messageformat": "^2.12.1",