diff --git a/application/pom.xml b/application/pom.xml
index 1b89752925..98a65bc5bb 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -249,6 +249,10 @@
io.grpc
grpc-stub
+
+ org.opensmpp
+ opensmpp-core
+
org.thingsboard
springfox-boot-starter
diff --git a/application/src/main/data/json/tenant/edge_management/rule_chains/edge_root_rule_chain.json b/application/src/main/data/json/tenant/edge_management/rule_chains/edge_root_rule_chain.json
index a065279cce..88f8232328 100644
--- a/application/src/main/data/json/tenant/edge_management/rule_chains/edge_root_rule_chain.json
+++ b/application/src/main/data/json/tenant/edge_management/rule_chains/edge_root_rule_chain.json
@@ -156,6 +156,21 @@
"toIndex": 7,
"type": "Attributes Updated"
},
+ {
+ "fromIndex": 3,
+ "toIndex": 7,
+ "type": "Attributes Deleted"
+ },
+ {
+ "fromIndex": 3,
+ "toIndex": 7,
+ "type": "Timeseries Deleted"
+ },
+ {
+ "fromIndex": 3,
+ "toIndex": 7,
+ "type": "Timeseries Updated"
+ },
{
"fromIndex": 4,
"toIndex": 7,
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index 95125b331a..0b28aca7b0 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -35,6 +35,7 @@ import org.thingsboard.server.actors.TbActorCtx;
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@@ -97,7 +98,6 @@ import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -107,6 +107,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -200,8 +201,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
boolean sent = false;
if (systemContext.isEdgesEnabled() && edgeId != null) {
log.debug("[{}][{}] device is related to edge [{}]. Saving RPC request to edge queue", tenantId, deviceId, edgeId.getId());
- saveRpcRequestToEdgeQueue(request, rpcRequest.getRequestId());
- sent = true;
+ try {
+ saveRpcRequestToEdgeQueue(request, rpcRequest.getRequestId()).get();
+ sent = true;
+ } catch (InterruptedException | ExecutionException e) {
+ String errMsg = String.format("[%s][%s][%s] Failed to save rpc request to edge queue %s", tenantId, deviceId, edgeId.getId(), request);
+ log.error(errMsg, e);
+ }
} else if (isSendNewRpcAvailable()) {
sent = rpcSubscriptions.size() > 0;
Set syncSessionSet = new HashSet<>();
@@ -810,13 +816,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
systemContext.getTbCoreToTransportService().process(nodeId, msg);
}
- private void saveRpcRequestToEdgeQueue(ToDeviceRpcRequest msg, Integer requestId) {
- EdgeEvent edgeEvent = new EdgeEvent();
- edgeEvent.setTenantId(tenantId);
- edgeEvent.setAction(EdgeEventActionType.RPC_CALL);
- edgeEvent.setEntityId(deviceId.getId());
- edgeEvent.setType(EdgeEventType.DEVICE);
-
+ private ListenableFuture saveRpcRequestToEdgeQueue(ToDeviceRpcRequest msg, Integer requestId) {
ObjectNode body = mapper.createObjectNode();
body.put("requestId", requestId);
body.put("requestUUID", msg.getId().toString());
@@ -824,11 +824,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
body.put("expirationTime", msg.getExpirationTime());
body.put("method", msg.getBody().getMethod());
body.put("params", msg.getBody().getParams());
- edgeEvent.setBody(body);
- edgeEvent.setEdgeId(edgeId);
- systemContext.getEdgeEventService().save(edgeEvent);
- systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
+ EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.DEVICE, EdgeEventActionType.RPC_CALL, deviceId, body);
+
+ return Futures.transform(systemContext.getEdgeEventService().saveAsync(edgeEvent), unused -> {
+ systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
+ return null;
+ }, systemContext.getDbCallbackExecutor());
}
private List toTsKvProtos(@Nullable List result) {
diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
index e71b881477..b6ba0088bb 100644
--- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
@@ -22,6 +22,7 @@ import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
@@ -84,6 +85,7 @@ import static springfox.documentation.builders.PathSelectors.regex;
@Slf4j
@Configuration
@TbCoreComponent
+@Profile("!test")
public class SwaggerConfiguration {
@Value("${swagger.api_path_regex}")
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 90703481cd..3e9597a72d 100644
--- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
@@ -176,16 +176,16 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
return new BCryptPasswordEncoder();
}
- @Override
- public void configure(WebSecurity web) throws Exception {
- web.ignoring().antMatchers("/*.js","/*.css","/*.ico","/assets/**","/static/**");
- }
-
@Autowired
private OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver;
@Override
protected void configure(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests((authorizeHttpRequests) ->
+ authorizeHttpRequests
+ .antMatchers("/*.js","/*.css","/*.ico","/assets/**","/static/**")
+ .permitAll()
+ );
http.headers().cacheControl().and().frameOptions().disable()
.and()
.cors()
diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
index 664e6c3181..13b13123e6 100644
--- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
@@ -55,7 +55,7 @@ public class WebSocketConfiguration implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
- registry.addHandler(wsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*")
+ registry.addHandler(wsHandler(), WS_PLUGIN_MAPPING).setAllowedOriginPatterns("*")
.addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() {
@Override
diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
index f3d73f13e5..a516a1e4a3 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
@@ -180,7 +180,7 @@ public class EdgeController extends BaseController {
}
}
- private void onEdgeCreatedOrUpdated(TenantId tenantId, Edge edge, RuleChain edgeTemplateRootRuleChain, boolean updated, SecurityUser user) throws IOException, ThingsboardException {
+ private void onEdgeCreatedOrUpdated(TenantId tenantId, Edge edge, RuleChain edgeTemplateRootRuleChain, boolean updated, SecurityUser user) throws Exception {
if (!updated) {
ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), edge.getId());
edgeNotificationService.setEdgeRootRuleChain(tenantId, edge, edgeTemplateRootRuleChain.getId());
diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
index 002956924d..281a13a6a0 100644
--- a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
@@ -28,6 +28,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@@ -49,6 +50,8 @@ import org.thingsboard.server.service.security.permission.Resource;
import java.nio.ByteBuffer;
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.OTA_PACKAGE_CHECKSUM_ALGORITHM_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.OTA_PACKAGE_DESCRIPTION;
@@ -105,7 +108,7 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Get OTA Package Info (getOtaPackageInfoById)",
notes = "Fetch the OTA Package Info object based on the provided OTA Package Id. " +
OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ produces = APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/otaPackage/info/{otaPackageId}", method = RequestMethod.GET)
@ResponseBody
@@ -123,7 +126,7 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Get OTA Package (getOtaPackageById)",
notes = "Fetch the OTA Package object based on the provided OTA Package Id. " +
"The server checks that the OTA Package is owned by the same tenant. " + OTA_PACKAGE_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ produces = APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.GET)
@ResponseBody
@@ -144,8 +147,8 @@ public class OtaPackageController extends BaseController {
"Specify existing OTA Package id to update the OTA Package Info. " +
"Referencing non-existing OTA Package Id will cause 'Not Found' error. " +
"\n\nOTA Package combination of the title with the version is unique in the scope of tenant. " + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json",
- consumes = "application/json")
+ produces = APPLICATION_JSON_VALUE,
+ consumes = APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage", method = RequestMethod.POST)
@ResponseBody
@@ -168,9 +171,10 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Save OTA Package data (saveOtaPackageData)",
notes = "Update the OTA Package. Adds the date to the existing OTA Package Info" + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ produces = APPLICATION_JSON_VALUE,
+ consumes = MULTIPART_FORM_DATA_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
- @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.POST)
+ @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.POST, consumes = MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public OtaPackageInfo saveOtaPackageData(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId,
@@ -179,7 +183,7 @@ public class OtaPackageController extends BaseController {
@ApiParam(value = "OTA Package checksum algorithm.", allowableValues = OTA_PACKAGE_CHECKSUM_ALGORITHM_ALLOWABLE_VALUES)
@RequestParam(CHECKSUM_ALGORITHM) String checksumAlgorithmStr,
@ApiParam(value = "OTA Package data.")
- @RequestBody MultipartFile file) throws ThingsboardException {
+ @RequestPart MultipartFile file) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
checkParameter(CHECKSUM_ALGORITHM, checksumAlgorithmStr);
try {
@@ -221,7 +225,7 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Get OTA Package Infos (getOtaPackages)",
notes = "Returns a page of OTA Package Info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ produces = APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/otaPackages", method = RequestMethod.GET)
@ResponseBody
@@ -246,7 +250,7 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Get OTA Package Infos (getOtaPackages)",
notes = "Returns a page of OTA Package Info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ produces = APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}", method = RequestMethod.GET)
@ResponseBody
@@ -278,7 +282,7 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Delete OTA Package (deleteOtaPackage)",
notes = "Deletes the OTA Package. Referencing non-existing OTA Package Id will cause an error. " +
"Can't delete the OTA Package if it is referenced by existing devices or device profile." + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ produces = APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.DELETE)
@ResponseBody
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
index 55779f19ef..6b5e519f80 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
@@ -16,11 +16,16 @@
package org.thingsboard.server.service.edge;
import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.cluster.TbClusterService;
+import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@@ -42,7 +47,6 @@ import org.thingsboard.server.service.edge.rpc.processor.RelationEdgeProcessor;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
-import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -76,29 +80,29 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
@Autowired
private CustomerEdgeProcessor customerProcessor;
- private ExecutorService tsCallBackExecutor;
+ private ExecutorService dbCallBackExecutor;
@PostConstruct
public void initExecutor() {
- tsCallBackExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("edge-notifications"));
+ dbCallBackExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("edge-notifications"));
}
@PreDestroy
public void shutdownExecutor() {
- if (tsCallBackExecutor != null) {
- tsCallBackExecutor.shutdownNow();
+ if (dbCallBackExecutor != null) {
+ dbCallBackExecutor.shutdownNow();
}
}
@Override
- public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException {
+ public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws Exception {
edge.setRootRuleChainId(ruleChainId);
Edge savedEdge = edgeService.saveEdge(edge);
- saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, null);
+ saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, null).get();
return savedEdge;
}
- private void saveEdgeEvent(TenantId tenantId,
+ private ListenableFuture saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType type,
EdgeEventActionType action,
@@ -107,17 +111,12 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body);
- EdgeEvent edgeEvent = new EdgeEvent();
- edgeEvent.setEdgeId(edgeId);
- edgeEvent.setTenantId(tenantId);
- edgeEvent.setType(type);
- edgeEvent.setAction(action);
- if (entityId != null) {
- edgeEvent.setEntityId(entityId.getId());
- }
- edgeEvent.setBody(body);
- edgeEventService.save(edgeEvent);
- clusterService.onEdgeEventUpdate(tenantId, edgeId);
+ EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
+
+ return Futures.transform(edgeEventService.saveAsync(edgeEvent), unused -> {
+ clusterService.onEdgeEventUpdate(tenantId, edgeId);
+ return null;
+ }, dbCallBackExecutor);
}
@Override
@@ -126,9 +125,10 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
try {
TenantId tenantId = TenantId.fromUUID(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB()));
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
+ ListenableFuture future;
switch (type) {
case EDGE:
- edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg);
+ future = edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg);
break;
case USER:
case ASSET:
@@ -137,33 +137,47 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
case ENTITY_VIEW:
case DASHBOARD:
case RULE_CHAIN:
- entityProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
+ future = entityProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break;
case CUSTOMER:
- customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg);
+ future = customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg);
break;
case WIDGETS_BUNDLE:
case WIDGET_TYPE:
- entityProcessor.processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
+ future = entityProcessor.processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
break;
case ALARM:
- alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg);
+ future = alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg);
break;
case RELATION:
- relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg);
+ future = relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg);
break;
default:
log.warn("Edge event type [{}] is not designed to be pushed to edge", type);
+ future = Futures.immediateFuture(null);
}
+ Futures.addCallback(future, new FutureCallback<>() {
+ @Override
+ public void onSuccess(@Nullable Void unused) {
+ callback.onSuccess();
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ callBackFailure(edgeNotificationMsg, callback, throwable);
+ }
+ }, dbCallBackExecutor);
} catch (Exception e) {
- callback.onFailure(e);
- String errMsg = String.format("Can't push to edge updates, edgeNotificationMsg [%s]", edgeNotificationMsg);
- log.error(errMsg, e);
- } finally {
- callback.onSuccess();
+ callBackFailure(edgeNotificationMsg, callback, e);
}
}
+ private void callBackFailure(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback, Throwable throwable) {
+ String errMsg = String.format("Can't push to edge updates, edgeNotificationMsg [%s]", edgeNotificationMsg);
+ log.error(errMsg, throwable);
+ callback.onFailure(throwable);
+ }
+
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java
index be6741cef6..ee36c80454 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java
@@ -21,11 +21,9 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos;
-import java.io.IOException;
-
public interface EdgeNotificationService {
- Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException;
+ Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws Exception;
void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeEventUtils.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeEventUtils.java
deleted file mode 100644
index 0aeac4c16a..0000000000
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeEventUtils.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Copyright © 2016-2022 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.server.service.edge.rpc;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import org.thingsboard.server.common.data.edge.EdgeEvent;
-import org.thingsboard.server.common.data.edge.EdgeEventActionType;
-import org.thingsboard.server.common.data.edge.EdgeEventType;
-import org.thingsboard.server.common.data.id.EdgeId;
-import org.thingsboard.server.common.data.id.EntityId;
-import org.thingsboard.server.common.data.id.TenantId;
-
-public final class EdgeEventUtils {
-
- private EdgeEventUtils() {
- }
-
- public static EdgeEvent constructEdgeEvent(TenantId tenantId,
- EdgeId edgeId,
- EdgeEventType type,
- EdgeEventActionType action,
- EntityId entityId,
- JsonNode body) {
- EdgeEvent edgeEvent = new EdgeEvent();
- edgeEvent.setTenantId(tenantId);
- edgeEvent.setEdgeId(edgeId);
- edgeEvent.setType(type);
- edgeEvent.setAction(action);
- if (entityId != null) {
- edgeEvent.setEntityId(entityId.getId());
- }
- edgeEvent.setBody(body);
- return edgeEvent;
- }
-}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
index 65ca6b4ea6..c56fcd4ed9 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
@@ -15,7 +15,6 @@
*/
package org.thingsboard.server.service.edge.rpc;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import io.grpc.Server;
@@ -71,7 +70,6 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
private final ConcurrentMap sessionNewEventsLocks = new ConcurrentHashMap<>();
private final Map sessionNewEvents = new HashMap<>();
private final ConcurrentMap> sessionEdgeEventChecks = new ConcurrentHashMap<>();
- private static final ObjectMapper mapper = new ObjectMapper();
@Value("${edges.rpc.port}")
private int rpcPort;
@@ -159,7 +157,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
@Override
public StreamObserver handleMsgs(StreamObserver outputStream) {
- return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, mapper, sendDownlinkExecutorService).getInputStream();
+ return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, sendDownlinkExecutorService).getInputStream();
}
@Override
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
index e4f32f6a89..de0cea14d6 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
@@ -16,7 +16,6 @@
package org.thingsboard.server.service.edge.rpc;
import com.datastax.oss.driver.api.core.uuid.Uuids;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -96,7 +95,6 @@ public final class EdgeGrpcSession implements Closeable {
private final UUID sessionId;
private final BiConsumer sessionOpenListener;
private final Consumer sessionCloseListener;
- private final ObjectMapper mapper;
private final EdgeSessionState sessionState = new EdgeSessionState();
@@ -112,13 +110,12 @@ public final class EdgeGrpcSession implements Closeable {
private ScheduledExecutorService sendDownlinkExecutorService;
EdgeGrpcSession(EdgeContextComponent ctx, StreamObserver outputStream, BiConsumer sessionOpenListener,
- Consumer sessionCloseListener, ObjectMapper mapper, ScheduledExecutorService sendDownlinkExecutorService) {
+ Consumer sessionCloseListener, ScheduledExecutorService sendDownlinkExecutorService) {
this.sessionId = UUID.randomUUID();
this.ctx = ctx;
this.outputStream = outputStream;
this.sessionOpenListener = sessionOpenListener;
this.sessionCloseListener = sessionCloseListener;
- this.mapper = mapper;
this.sendDownlinkExecutorService = sendDownlinkExecutorService;
initInputStream();
}
@@ -402,11 +399,11 @@ public final class EdgeGrpcSession implements Closeable {
Runnable sendDownlinkMsgsTask = () -> {
try {
if (isConnected() && sessionState.getPendingMsgsMap().values().size() > 0) {
+ List copy = new ArrayList<>(sessionState.getPendingMsgsMap().values());
if (!firstRun) {
- log.warn("[{}] Failed to deliver the batch: {}", this.sessionId, sessionState.getPendingMsgsMap().values());
+ log.warn("[{}] Failed to deliver the batch: {}", this.sessionId, copy);
}
- log.trace("[{}] [{}] downlink msg(s) are going to be send.", this.sessionId, sessionState.getPendingMsgsMap().values().size());
- List copy = new ArrayList<>(sessionState.getPendingMsgsMap().values());
+ log.trace("[{}] [{}] downlink msg(s) are going to be send.", this.sessionId, copy.size());
for (DownlinkMsg downlinkMsg : copy) {
sendDownlinkMsg(ResponseMsg.newBuilder()
.setDownlinkMsg(downlinkMsg)
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java
index 51b84d6a94..0f4ecf7e30 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java
@@ -26,6 +26,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.thingsboard.server.common.data.AdminSettings;
+import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@@ -35,7 +36,6 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.settings.AdminSettingsService;
-import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -80,19 +80,19 @@ public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher {
List result = new ArrayList<>();
AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail");
- result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
+ result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailSettings)));
AdminSettings tenantMailSettings = convertToTenantAdminSettings(systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue());
- result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
+ result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailSettings)));
AdminSettings systemMailTemplates = loadMailTemplates();
- result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
+ result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailTemplates)));
AdminSettings tenantMailTemplates = convertToTenantAdminSettings(systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue());
- result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
+ result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailTemplates)));
// @voba - returns PageData object to be in sync with other fetchers
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AssetsEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AssetsEdgeEventFetcher.java
index e66cafda56..ead87638fb 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AssetsEdgeEventFetcher.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AssetsEdgeEventFetcher.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
@@ -26,7 +27,6 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.asset.AssetService;
-import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
@AllArgsConstructor
@Slf4j
@@ -41,7 +41,7 @@ public class AssetsEdgeEventFetcher extends BasePageableEdgeEventFetcher
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, Asset asset) {
- return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET,
+ return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET,
EdgeEventActionType.ADDED, asset.getId(), null);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseUsersEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseUsersEdgeEventFetcher.java
index 606cd6eaca..6791ba69a6 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseUsersEdgeEventFetcher.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseUsersEdgeEventFetcher.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
@@ -26,7 +27,6 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.user.UserService;
-import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
@Slf4j
@AllArgsConstructor
@@ -41,7 +41,7 @@ public abstract class BaseUsersEdgeEventFetcher extends BasePageableEdgeEventFet
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, User user) {
- return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
+ return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
EdgeEventActionType.ADDED, user.getId(), null);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetsBundlesEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetsBundlesEdgeEventFetcher.java
index 3f8beb861d..709c438739 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetsBundlesEdgeEventFetcher.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetsBundlesEdgeEventFetcher.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@@ -26,7 +27,6 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
-import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
@Slf4j
@AllArgsConstructor
@@ -41,7 +41,7 @@ public abstract class BaseWidgetsBundlesEdgeEventFetcher extends BasePageableEdg
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, WidgetsBundle widgetsBundle) {
- return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGETS_BUNDLE,
+ return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGETS_BUNDLE,
EdgeEventActionType.ADDED, widgetsBundle.getId(), null);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java
index adaac874a0..1d5c618a0a 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/CustomerEdgeEventFetcher.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@@ -24,7 +25,6 @@ import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
-import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
import java.util.ArrayList;
import java.util.List;
@@ -41,7 +41,7 @@ public class CustomerEdgeEventFetcher implements EdgeEventFetcher {
@Override
public PageData fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
List result = new ArrayList<>();
- result.add(EdgeEventUtils.constructEdgeEvent(edge.getTenantId(), edge.getId(),
+ result.add(EdgeUtils.constructEdgeEvent(edge.getTenantId(), edge.getId(),
EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, edge.getCustomerId(), null));
// @voba - returns PageData object to be in sync with other fetchers
return new PageData<>(result, 1, result.size(), false);
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DashboardsEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DashboardsEdgeEventFetcher.java
index f46ab01ee5..a6e7b3307c 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DashboardsEdgeEventFetcher.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DashboardsEdgeEventFetcher.java
@@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.DashboardInfo;
+import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@@ -26,7 +27,6 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.dashboard.DashboardService;
-import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
@AllArgsConstructor
@Slf4j
@@ -41,7 +41,7 @@ public class DashboardsEdgeEventFetcher extends BasePageableEdgeEventFetcher processAlarmNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
switch (actionType) {
case DELETED:
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
- Alarm alarm = mapper.readValue(edgeNotificationMsg.getBody(), Alarm.class);
- saveEdgeEvent(tenantId, edgeId, EdgeEventType.ALARM, actionType, alarmId, mapper.valueToTree(alarm));
- break;
+ Alarm deletedAlarm = mapper.readValue(edgeNotificationMsg.getBody(), Alarm.class);
+ return saveEdgeEvent(tenantId, edgeId, EdgeEventType.ALARM, actionType, alarmId, mapper.valueToTree(deletedAlarm));
default:
ListenableFuture alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
- Futures.addCallback(alarmFuture, new FutureCallback() {
- @Override
- public void onSuccess(@Nullable Alarm alarm) {
- if (alarm != null) {
- EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());
- if (type != null) {
- PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
- PageData pageData;
- do {
- pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator(), pageLink);
- if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
- for (EdgeId edgeId : pageData.getData()) {
- saveEdgeEvent(tenantId,
- edgeId,
- EdgeEventType.ALARM,
- EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
- alarmId,
- null);
- }
- if (pageData.hasNext()) {
- pageLink = pageLink.nextPageLink();
- }
- }
- } while (pageData != null && pageData.hasNext());
- }
- }
+ return Futures.transformAsync(alarmFuture, alarm -> {
+ if (alarm == null) {
+ return Futures.immediateFuture(null);
}
-
- @Override
- public void onFailure(Throwable t) {
- log.warn("[{}] can't find alarm by id [{}] {}", tenantId.getId(), alarmId.getId(), t);
+ EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());
+ if (type == null) {
+ return Futures.immediateFuture(null);
}
+ PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
+ PageData pageData;
+ List> futures = new ArrayList<>();
+ do {
+ pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator(), pageLink);
+ if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
+ for (EdgeId relatedEdgeId : pageData.getData()) {
+ futures.add(saveEdgeEvent(tenantId,
+ relatedEdgeId,
+ EdgeEventType.ALARM,
+ EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
+ alarmId,
+ null));
+ }
+ if (pageData.hasNext()) {
+ pageLink = pageLink.nextPageLink();
+ }
+ }
+ } while (pageData != null && pageData.hasNext());
+ return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
}, dbCallbackExecutorService);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
index 10d1ae88c5..09bef32af9 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
@@ -17,10 +17,13 @@ package org.thingsboard.server.service.edge.rpc.processor;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
@@ -67,6 +70,9 @@ import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.state.DeviceStateService;
+import java.util.ArrayList;
+import java.util.List;
+
@Slf4j
public abstract class BaseEdgeProcessor {
@@ -179,27 +185,22 @@ public abstract class BaseEdgeProcessor {
@Autowired
protected DbCallbackExecutorService dbCallbackExecutorService;
- protected void saveEdgeEvent(TenantId tenantId,
- EdgeId edgeId,
- EdgeEventType type,
- EdgeEventActionType action,
- EntityId entityId,
- JsonNode body) {
+ protected ListenableFuture saveEdgeEvent(TenantId tenantId,
+ EdgeId edgeId,
+ EdgeEventType type,
+ EdgeEventActionType action,
+ EntityId entityId,
+ JsonNode body) {
log.debug("Pushing event to edge queue. tenantId [{}], edgeId [{}], type[{}], " +
"action [{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body);
- EdgeEvent edgeEvent = new EdgeEvent();
- edgeEvent.setTenantId(tenantId);
- edgeEvent.setEdgeId(edgeId);
- edgeEvent.setType(type);
- edgeEvent.setAction(action);
- if (entityId != null) {
- edgeEvent.setEntityId(entityId.getId());
- }
- edgeEvent.setBody(body);
- edgeEventService.save(edgeEvent);
- tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
+ EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
+
+ return Futures.transform(edgeEventService.saveAsync(edgeEvent), unused -> {
+ tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
+ return null;
+ }, dbCallbackExecutorService);
}
protected CustomerId getCustomerIdIfEdgeAssignedToCustomer(HasCustomerId hasCustomerIdEntity, Edge edge) {
@@ -210,19 +211,21 @@ public abstract class BaseEdgeProcessor {
}
}
- protected void processActionForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId) {
+ protected ListenableFuture processActionForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData pageData;
+ List> futures = new ArrayList<>();
do {
pageData = edgeService.findEdgesByTenantId(tenantId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (Edge edge : pageData.getData()) {
- saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null);
+ futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null));
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
+ return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/CustomerEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/CustomerEdgeProcessor.java
index 6c670e47ce..3554057482 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/CustomerEdgeProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/CustomerEdgeProcessor.java
@@ -15,6 +15,8 @@
*/
package org.thingsboard.server.service.edge.rpc.processor;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Customer;
@@ -35,6 +37,8 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import java.util.ArrayList;
+import java.util.List;
import java.util.UUID;
@Component
@@ -70,7 +74,7 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor {
return downlinkMsg;
}
- public void processCustomerNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
+ public ListenableFuture processCustomerNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
UUID uuid = new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB());
@@ -79,22 +83,24 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor {
case UPDATED:
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData pageData;
+ List> futures = new ArrayList<>();
do {
pageData = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (Edge edge : pageData.getData()) {
- saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null);
+ futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null));
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
- break;
+ return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
case DELETED:
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
- saveEdgeEvent(tenantId, edgeId, type, actionType, customerId, null);
- break;
+ return saveEdgeEvent(tenantId, edgeId, type, actionType, customerId, null);
+ default:
+ return Futures.immediateFuture(null);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java
index f19ec25b27..0e1dee7132 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java
@@ -84,7 +84,7 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
if (deviceAlreadyExistsForThisEdge) {
log.info("[{}] Device with name '{}' already exists on the cloud, and related to this edge [{}]. " +
"deviceUpdateMsg [{}], Updating device", tenantId, deviceName, edge.getId(), deviceUpdateMsg);
- updateDevice(tenantId, edge, deviceUpdateMsg);
+ return updateDevice(tenantId, edge, deviceUpdateMsg);
} else {
log.info("[{}] Device with name '{}' already exists on the cloud, but not related to this edge [{}]. deviceUpdateMsg [{}]." +
"Creating a new device with random prefix and relate to this edge", tenantId, deviceName, edge.getId(), deviceUpdateMsg);
@@ -99,8 +99,10 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
}
ObjectNode body = mapper.createObjectNode();
body.put("conflictName", deviceName);
- saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, newDevice.getId(), body);
- saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, newDevice.getId(), null);
+ ListenableFuture input = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, newDevice.getId(), body);
+ return Futures.transformAsync(input, unused ->
+ saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, newDevice.getId(), null),
+ dbCallbackExecutorService);
}
} else {
log.info("[{}] Creating new device and replacing device entity on the edge [{}]", tenantId, deviceUpdateMsg);
@@ -111,24 +113,22 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
log.error(errMsg, e);
return Futures.immediateFuture(null);
}
- saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, device.getId(), null);
+ return saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, device.getId(), null);
}
- break;
case ENTITY_UPDATED_RPC_MESSAGE:
- updateDevice(tenantId, edge, deviceUpdateMsg);
- break;
+ return updateDevice(tenantId, edge, deviceUpdateMsg);
case ENTITY_DELETED_RPC_MESSAGE:
DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
Device deviceToDelete = deviceService.findDeviceById(tenantId, deviceId);
if (deviceToDelete != null) {
deviceService.unassignDeviceFromEdge(tenantId, deviceId, edge.getId());
}
- break;
+ return Futures.immediateFuture(null);
case UNRECOGNIZED:
+ default:
log.error("Unsupported msg type {}", deviceUpdateMsg.getMsgType());
return Futures.immediateFailedFuture(new RuntimeException("Unsupported msg type " + deviceUpdateMsg.getMsgType()));
}
- return Futures.immediateFuture(null);
}
private boolean isDeviceAlreadyExistsOnCloudForThisEdge(TenantId tenantId, Edge edge, Device device) {
@@ -174,7 +174,7 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
}
- private void updateDevice(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) {
+ private ListenableFuture updateDevice(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) {
DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
Device device = deviceService.findDeviceById(tenantId, deviceId);
if (device != null) {
@@ -194,9 +194,11 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
}
Device savedDevice = deviceService.saveDevice(device);
tbClusterService.onDeviceUpdated(savedDevice, device);
- saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, deviceId, null);
+ return saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, deviceId, null);
} else {
- log.warn("[{}] can't find device [{}], edge [{}]", tenantId, deviceUpdateMsg, edge.getId());
+ String errMsg = String.format("[%s] can't find device [%s], edge [%s]", tenantId, deviceUpdateMsg, edge.getId());
+ log.warn(errMsg);
+ return Futures.immediateFailedFuture(new RuntimeException(errMsg));
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EdgeProcessor.java
index 3d662da232..fdbebafc98 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EdgeProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EdgeProcessor.java
@@ -15,11 +15,9 @@
*/
package org.thingsboard.server.service.edge.rpc.processor;
-import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
-import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.edge.Edge;
@@ -33,6 +31,8 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import java.util.ArrayList;
+import java.util.List;
import java.util.UUID;
@Component
@@ -40,7 +40,7 @@ import java.util.UUID;
@TbCoreComponent
public class EdgeProcessor extends BaseEdgeProcessor {
- public void processEdgeNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
+ public ListenableFuture processEdgeNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
try {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
@@ -49,54 +49,43 @@ public class EdgeProcessor extends BaseEdgeProcessor {
case ASSIGNED_TO_CUSTOMER:
CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
- Futures.addCallback(edgeFuture, new FutureCallback() {
- @Override
- public void onSuccess(@Nullable Edge edge) {
- if (edge != null && !customerId.isNullUid()) {
- saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, customerId, null);
- PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
- PageData pageData;
- do {
- pageData = userService.findCustomerUsers(tenantId, customerId, pageLink);
- if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
- log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size());
- for (User user : pageData.getData()) {
- saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null);
- }
- if (pageData.hasNext()) {
- pageLink = pageLink.nextPageLink();
- }
- }
- } while (pageData != null && pageData.hasNext());
- }
- }
-
- @Override
- public void onFailure(Throwable t) {
- log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
+ return Futures.transformAsync(edgeFuture, edge -> {
+ if (edge == null || customerId.isNullUid()) {
+ return Futures.immediateFuture(null);
}
+ List> futures = new ArrayList<>();
+ futures.add(saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, customerId, null));
+ PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
+ PageData pageData;
+ do {
+ pageData = userService.findCustomerUsers(tenantId, customerId, pageLink);
+ if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
+ log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size());
+ for (User user : pageData.getData()) {
+ futures.add(saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null));
+ }
+ if (pageData.hasNext()) {
+ pageLink = pageLink.nextPageLink();
+ }
+ }
+ } while (pageData != null && pageData.hasNext());
+ return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
}, dbCallbackExecutorService);
- break;
case UNASSIGNED_FROM_CUSTOMER:
CustomerId customerIdToDelete = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
- Futures.addCallback(edgeFuture, new FutureCallback() {
- @Override
- public void onSuccess(@Nullable Edge edge) {
- if (edge != null && !customerIdToDelete.isNullUid()) {
- saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.DELETED, customerIdToDelete, null);
- }
- }
-
- @Override
- public void onFailure(Throwable t) {
- log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
+ return Futures.transformAsync(edgeFuture, edge -> {
+ if (edge == null || customerIdToDelete.isNullUid()) {
+ return Futures.immediateFuture(null);
}
+ return saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.DELETED, customerIdToDelete, null);
}, dbCallbackExecutorService);
- break;
+ default:
+ return Futures.immediateFuture(null);
}
} catch (Exception e) {
log.error("Exception during processing edge event", e);
+ return Futures.immediateFailedFuture(e);
}
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EntityEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EntityEdgeProcessor.java
index 89ce662467..195f932b93 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EntityEdgeProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EntityEdgeProcessor.java
@@ -15,11 +15,9 @@
*/
package org.thingsboard.server.service.edge.rpc.processor;
-import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
-import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EdgeUtils;
@@ -45,6 +43,7 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
+import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -89,92 +88,107 @@ public class EntityEdgeProcessor extends BaseEdgeProcessor {
return downlinkMsg;
}
- public void processEntityNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
+ public ListenableFuture processEntityNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type,
new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
- EdgeId edgeId = null;
- if (edgeNotificationMsg.getEdgeIdMSB() != 0 && edgeNotificationMsg.getEdgeIdLSB() != 0) {
- edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
- }
+ EdgeId edgeId = safeGetEdgeId(edgeNotificationMsg);
switch (actionType) {
case ADDED: // used only for USER entity
case UPDATED:
case CREDENTIALS_UPDATED:
- pushNotificationToAllRelatedEdges(tenantId, entityId, type, actionType);
- break;
+ return pushNotificationToAllRelatedEdges(tenantId, entityId, type, actionType);
case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER:
- PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
- PageData pageData;
- do {
- pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId, pageLink);
- if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
- for (EdgeId relatedEdgeId : pageData.getData()) {
- try {
- CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
- ListenableFuture future = edgeService.findEdgeByIdAsync(tenantId, relatedEdgeId);
- Futures.addCallback(future, new FutureCallback<>() {
- @Override
- public void onSuccess(@Nullable Edge edge) {
- if (edge != null && edge.getCustomerId() != null &&
- !edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) {
- saveEdgeEvent(tenantId, relatedEdgeId, type, actionType, entityId, null);
- }
- }
-
- @Override
- public void onFailure(Throwable t) {
- log.error("Failed to find edge by id [{}] {}", edgeNotificationMsg, t);
- }
- }, dbCallbackExecutorService);
- } catch (Exception e) {
- log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e);
- }
- }
- if (pageData.hasNext()) {
- pageLink = pageLink.nextPageLink();
- }
- }
- } while (pageData != null && pageData.hasNext());
- break;
+ return pushNotificationToAllRelatedCustomerEdges(tenantId, edgeNotificationMsg, entityId, actionType, type);
case DELETED:
if (edgeId != null) {
- saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
+ return saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
} else {
- pushNotificationToAllRelatedEdges(tenantId, entityId, type, actionType);
+ return pushNotificationToAllRelatedEdges(tenantId, entityId, type, actionType);
}
- break;
case ASSIGNED_TO_EDGE:
case UNASSIGNED_FROM_EDGE:
- saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
- if (type.equals(EdgeEventType.RULE_CHAIN)) {
- updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId);
+ ListenableFuture future = saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
+ return Futures.transformAsync(future, unused -> {
+ if (type.equals(EdgeEventType.RULE_CHAIN)) {
+ return updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId);
+ } else {
+ return Futures.immediateFuture(null);
+ }
+ }, dbCallbackExecutorService);
+ default:
+ return Futures.immediateFuture(null);
+ }
+ }
+
+ private ListenableFuture pushNotificationToAllRelatedCustomerEdges(TenantId tenantId,
+ TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg,
+ EntityId entityId,
+ EdgeEventActionType actionType,
+ EdgeEventType type) {
+ PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
+ PageData pageData;
+ List> futures = new ArrayList<>();
+ do {
+ pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId, pageLink);
+ if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
+ for (EdgeId relatedEdgeId : pageData.getData()) {
+ try {
+ CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
+ ListenableFuture future = edgeService.findEdgeByIdAsync(tenantId, relatedEdgeId);
+ futures.add(Futures.transformAsync(future, edge -> {
+ if (edge != null && edge.getCustomerId() != null &&
+ !edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) {
+ return saveEdgeEvent(tenantId, relatedEdgeId, type, actionType, entityId, null);
+ } else {
+ return Futures.immediateFuture(null);
+ }
+ }, dbCallbackExecutorService));
+ } catch (Exception e) {
+ log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e);
+ return Futures.immediateFailedFuture(e);
+ }
}
- break;
+ if (pageData.hasNext()) {
+ pageLink = pageLink.nextPageLink();
+ }
+ }
+ } while (pageData != null && pageData.hasNext());
+ return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
+ }
+
+ private EdgeId safeGetEdgeId(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
+ if (edgeNotificationMsg.getEdgeIdMSB() != 0 && edgeNotificationMsg.getEdgeIdLSB() != 0) {
+ return new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
+ } else {
+ return null;
}
}
- private void pushNotificationToAllRelatedEdges(TenantId tenantId, EntityId entityId, EdgeEventType type, EdgeEventActionType actionType) {
+ private ListenableFuture pushNotificationToAllRelatedEdges(TenantId tenantId, EntityId entityId, EdgeEventType type, EdgeEventActionType actionType) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData pageData;
+ List> futures = new ArrayList<>();
do {
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (EdgeId relatedEdgeId : pageData.getData()) {
- saveEdgeEvent(tenantId, relatedEdgeId, type, actionType, entityId, null);
+ futures.add(saveEdgeEvent(tenantId, relatedEdgeId, type, actionType, entityId, null));
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
+ return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
}
- private void updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) {
+ private ListenableFuture updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData pageData;
+ List> futures = new ArrayList<>();
do {
pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
@@ -185,12 +199,12 @@ public class EntityEdgeProcessor extends BaseEdgeProcessor {
if (connectionInfos != null && !connectionInfos.isEmpty()) {
for (RuleChainConnectionInfo connectionInfo : connectionInfos) {
if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) {
- saveEdgeEvent(tenantId,
+ futures.add(saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.RULE_CHAIN_METADATA,
EdgeEventActionType.UPDATED,
ruleChain.getId(),
- null);
+ null));
}
}
}
@@ -201,9 +215,10 @@ public class EntityEdgeProcessor extends BaseEdgeProcessor {
}
}
} while (pageData != null && pageData.hasNext());
+ return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
}
- public void processEntityNotificationForAllEdges(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
+ public ListenableFuture processEntityNotificationForAllEdges(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
@@ -211,8 +226,9 @@ public class EntityEdgeProcessor extends BaseEdgeProcessor {
case ADDED:
case UPDATED:
case DELETED:
- processActionForAllEdges(tenantId, type, actionType, entityId);
- break;
+ return processActionForAllEdges(tenantId, type, actionType, entityId);
+ default:
+ return Futures.immediateFuture(null);
}
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationEdgeProcessor.java
index d49ac7094c..f86ea8aad2 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationEdgeProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationEdgeProcessor.java
@@ -126,24 +126,29 @@ public class RelationEdgeProcessor extends BaseEdgeProcessor {
.build();
}
- public void processRelationNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {
+ public ListenableFuture processRelationNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {
EntityRelation relation = mapper.readValue(edgeNotificationMsg.getBody(), EntityRelation.class);
- if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
- !relation.getTo().getEntityType().equals(EntityType.EDGE)) {
- Set uniqueEdgeIds = new HashSet<>();
- uniqueEdgeIds.addAll(findRelatedEdgeIds(tenantId, relation.getTo()));
- uniqueEdgeIds.addAll(findRelatedEdgeIds(tenantId, relation.getFrom()));
- if (!uniqueEdgeIds.isEmpty()) {
- for (EdgeId edgeId : uniqueEdgeIds) {
- saveEdgeEvent(tenantId,
- edgeId,
- EdgeEventType.RELATION,
- EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
- null,
- mapper.valueToTree(relation));
- }
- }
+ if (relation.getFrom().getEntityType().equals(EntityType.EDGE) ||
+ relation.getTo().getEntityType().equals(EntityType.EDGE)) {
+ return Futures.immediateFuture(null);
+ }
+
+ Set uniqueEdgeIds = new HashSet<>();
+ uniqueEdgeIds.addAll(findRelatedEdgeIds(tenantId, relation.getTo()));
+ uniqueEdgeIds.addAll(findRelatedEdgeIds(tenantId, relation.getFrom()));
+ if (uniqueEdgeIds.isEmpty()) {
+ return Futures.immediateFuture(null);
+ }
+ List> futures = new ArrayList<>();
+ for (EdgeId edgeId : uniqueEdgeIds) {
+ futures.add(saveEdgeEvent(tenantId,
+ edgeId,
+ EdgeEventType.RELATION,
+ EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
+ null,
+ mapper.valueToTree(relation)));
}
+ return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
}
private List findRelatedEdgeIds(TenantId tenantId, EntityId entityId) {
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
index d69133434a..9d87cabb2c 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
@@ -26,6 +26,7 @@ import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EdgeUtils;
@@ -71,9 +72,7 @@ import org.thingsboard.server.gen.edge.v1.RelationRequestMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg;
-import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
-import org.thingsboard.server.cluster.TbClusterService;
import java.util.ArrayList;
import java.util.HashMap;
@@ -122,13 +121,13 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
@Override
public ListenableFuture processRuleChainMetadataRequestMsg(TenantId tenantId, Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg) {
log.trace("[{}] processRuleChainMetadataRequestMsg [{}][{}]", tenantId, edge.getName(), ruleChainMetadataRequestMsg);
- if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) {
- RuleChainId ruleChainId =
- new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB()));
- saveEdgeEvent(tenantId, edge.getId(),
- EdgeEventType.RULE_CHAIN_METADATA, EdgeEventActionType.ADDED, ruleChainId, null);
+ if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() == 0 || ruleChainMetadataRequestMsg.getRuleChainIdLSB() == 0) {
+ return Futures.immediateFuture(null);
}
- return Futures.immediateFuture(null);
+ RuleChainId ruleChainId =
+ new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB()));
+ return saveEdgeEvent(tenantId, edge.getId(),
+ EdgeEventType.RULE_CHAIN_METADATA, EdgeEventActionType.ADDED, ruleChainId, null);
}
@Override
@@ -138,63 +137,72 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
EntityType.valueOf(attributesRequestMsg.getEntityType()),
new UUID(attributesRequestMsg.getEntityIdMSB(), attributesRequestMsg.getEntityIdLSB()));
final EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType());
- if (type != null) {
- SettableFuture futureToSet = SettableFuture.create();
- String scope = attributesRequestMsg.getScope();
- ListenableFuture> findAttrFuture = attributesService.findAll(tenantId, entityId, scope);
- Futures.addCallback(findAttrFuture, new FutureCallback>() {
- @Override
- public void onSuccess(@Nullable List ssAttributes) {
- if (ssAttributes != null && !ssAttributes.isEmpty()) {
- try {
- Map entityData = new HashMap<>();
- ObjectNode attributes = mapper.createObjectNode();
- for (AttributeKvEntry attr : ssAttributes) {
- if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) {
- attributes.put(attr.getKey(), attr.getBooleanValue().get());
- } else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) {
- attributes.put(attr.getKey(), attr.getDoubleValue().get());
- } else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) {
- attributes.put(attr.getKey(), attr.getLongValue().get());
- } else {
- attributes.put(attr.getKey(), attr.getValueAsString());
- }
- }
- entityData.put("kv", attributes);
- entityData.put("scope", scope);
- JsonNode body = mapper.valueToTree(entityData);
- log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", entityId, body);
- saveEdgeEvent(tenantId,
- edge.getId(),
- type,
- EdgeEventActionType.ATTRIBUTES_UPDATED,
- entityId,
- body);
- } catch (Exception e) {
- log.error("[{}] Failed to save attribute updates to the edge", edge.getName(), e);
- futureToSet.setException(new RuntimeException("[" + edge.getName() + "] Failed to send attribute updates to the edge", e));
- return;
- }
- } else {
- log.trace("[{}][{}] No attributes found for entity {} [{}]", tenantId,
- edge.getName(),
- entityId.getEntityType(),
- entityId.getId());
- }
+ if (type == null) {
+ log.warn("[{}] Type doesn't supported {}", tenantId, entityId.getEntityType());
+ return Futures.immediateFuture(null);
+ }
+ SettableFuture futureToSet = SettableFuture.create();
+ String scope = attributesRequestMsg.getScope();
+ ListenableFuture> findAttrFuture = attributesService.findAll(tenantId, entityId, scope);
+ Futures.addCallback(findAttrFuture, new FutureCallback<>() {
+ @Override
+ public void onSuccess(@Nullable List ssAttributes) {
+ if (ssAttributes == null || ssAttributes.isEmpty()) {
+ log.trace("[{}][{}] No attributes found for entity {} [{}]", tenantId,
+ edge.getName(),
+ entityId.getEntityType(),
+ entityId.getId());
futureToSet.set(null);
+ return;
}
- @Override
- public void onFailure(Throwable t) {
- log.error("Can't find attributes [{}]", attributesRequestMsg, t);
- futureToSet.setException(t);
+ try {
+ Map entityData = new HashMap<>();
+ ObjectNode attributes = mapper.createObjectNode();
+ for (AttributeKvEntry attr : ssAttributes) {
+ if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) {
+ attributes.put(attr.getKey(), attr.getBooleanValue().get());
+ } else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) {
+ attributes.put(attr.getKey(), attr.getDoubleValue().get());
+ } else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) {
+ attributes.put(attr.getKey(), attr.getLongValue().get());
+ } else {
+ attributes.put(attr.getKey(), attr.getValueAsString());
+ }
+ }
+ entityData.put("kv", attributes);
+ entityData.put("scope", scope);
+ JsonNode body = mapper.valueToTree(entityData);
+ log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", entityId, body);
+ ListenableFuture future = saveEdgeEvent(tenantId, edge.getId(), type, EdgeEventActionType.ATTRIBUTES_UPDATED, entityId, body);
+ Futures.addCallback(future, new FutureCallback<>() {
+ @Override
+ public void onSuccess(@Nullable Void unused) {
+ futureToSet.set(null);
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ String errMsg = String.format("[%s] Failed to save edge event [%s]", edge.getId(), attributesRequestMsg);
+ log.error(errMsg, throwable);
+ futureToSet.setException(new RuntimeException(errMsg, throwable));
+ }
+ }, dbCallbackExecutorService);
+ } catch (Exception e) {
+ String errMsg = String.format("[%s] Failed to save attribute updates to the edge [%s]", edge.getId(), attributesRequestMsg);
+ log.error(errMsg, e);
+ futureToSet.setException(new RuntimeException(errMsg, e));
}
- }, dbCallbackExecutorService);
- return futureToSet;
- } else {
- log.warn("[{}] Type doesn't supported {}", tenantId, entityId.getEntityType());
- return Futures.immediateFuture(null);
- }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ String errMsg = String.format("[%s] Can't find attributes [%s]", edge.getId(), attributesRequestMsg);
+ log.error(errMsg, t);
+ futureToSet.setException(new RuntimeException(errMsg, t));
+ }
+ }, dbCallbackExecutorService);
+ return futureToSet;
}
@Override
@@ -209,33 +217,49 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
futures.add(findRelationByQuery(tenantId, edge, entityId, EntitySearchDirection.TO));
ListenableFuture>> relationsListFuture = Futures.allAsList(futures);
SettableFuture futureToSet = SettableFuture.create();
- Futures.addCallback(relationsListFuture, new FutureCallback>>() {
+ Futures.addCallback(relationsListFuture, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List> relationsList) {
try {
if (relationsList != null && !relationsList.isEmpty()) {
+ List> futures = new ArrayList<>();
for (List entityRelations : relationsList) {
log.trace("[{}] [{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), entityId, entityRelations.size());
for (EntityRelation relation : entityRelations) {
try {
if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
!relation.getTo().getEntityType().equals(EntityType.EDGE)) {
- saveEdgeEvent(tenantId,
+ futures.add(saveEdgeEvent(tenantId,
edge.getId(),
EdgeEventType.RELATION,
EdgeEventActionType.ADDED,
null,
- mapper.valueToTree(relation));
+ mapper.valueToTree(relation)));
}
} catch (Exception e) {
- log.error("Exception during loading relation [{}] to edge on sync!", relation, e);
- futureToSet.setException(e);
+ String errMsg = String.format("[%s] Exception during loading relation [%s] to edge on sync!", edge.getId(), relation);
+ log.error(errMsg, e);
+ futureToSet.setException(new RuntimeException(errMsg, e));
return;
}
}
}
+ Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
+ @Override
+ public void onSuccess(@Nullable List voids) {
+ futureToSet.set(null);
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ String errMsg = String.format("[%s] Exception during saving edge events [%s]!", edge.getId(), relationRequestMsg);
+ log.error(errMsg, throwable);
+ futureToSet.setException(new RuntimeException(errMsg, throwable));
+ }
+ }, dbCallbackExecutorService);
+ } else {
+ futureToSet.set(null);
}
- futureToSet.set(null);
} catch (Exception e) {
log.error("Exception during loading relation(s) to edge on sync!", e);
futureToSet.setException(e);
@@ -244,8 +268,9 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
@Override
public void onFailure(Throwable t) {
- log.error("[{}] Can't find relation by query. Entity id [{}]", tenantId, entityId, t);
- futureToSet.setException(t);
+ String errMsg = String.format("[%s] Can't find relation by query. Entity id [%s]!", tenantId, entityId);
+ log.error(errMsg, t);
+ futureToSet.setException(new RuntimeException(errMsg, t));
}
}, dbCallbackExecutorService);
return futureToSet;
@@ -261,40 +286,42 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
@Override
public ListenableFuture processDeviceCredentialsRequestMsg(TenantId tenantId, Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg) {
log.trace("[{}] processDeviceCredentialsRequestMsg [{}][{}]", tenantId, edge.getName(), deviceCredentialsRequestMsg);
- if (deviceCredentialsRequestMsg.getDeviceIdMSB() != 0 && deviceCredentialsRequestMsg.getDeviceIdLSB() != 0) {
- DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsRequestMsg.getDeviceIdMSB(), deviceCredentialsRequestMsg.getDeviceIdLSB()));
- saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE,
- EdgeEventActionType.CREDENTIALS_UPDATED, deviceId, null);
+ if (deviceCredentialsRequestMsg.getDeviceIdMSB() == 0 || deviceCredentialsRequestMsg.getDeviceIdLSB() == 0) {
+ return Futures.immediateFuture(null);
}
- return Futures.immediateFuture(null);
+ DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsRequestMsg.getDeviceIdMSB(), deviceCredentialsRequestMsg.getDeviceIdLSB()));
+ return saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE,
+ EdgeEventActionType.CREDENTIALS_UPDATED, deviceId, null);
}
@Override
public ListenableFuture processUserCredentialsRequestMsg(TenantId tenantId, Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg) {
log.trace("[{}] processUserCredentialsRequestMsg [{}][{}]", tenantId, edge.getName(), userCredentialsRequestMsg);
- if (userCredentialsRequestMsg.getUserIdMSB() != 0 && userCredentialsRequestMsg.getUserIdLSB() != 0) {
- UserId userId = new UserId(new UUID(userCredentialsRequestMsg.getUserIdMSB(), userCredentialsRequestMsg.getUserIdLSB()));
- saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
- EdgeEventActionType.CREDENTIALS_UPDATED, userId, null);
+ if (userCredentialsRequestMsg.getUserIdMSB() == 0 || userCredentialsRequestMsg.getUserIdLSB() == 0) {
+ return Futures.immediateFuture(null);
}
- return Futures.immediateFuture(null);
+ UserId userId = new UserId(new UUID(userCredentialsRequestMsg.getUserIdMSB(), userCredentialsRequestMsg.getUserIdLSB()));
+ return saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
+ EdgeEventActionType.CREDENTIALS_UPDATED, userId, null);
}
@Override
public ListenableFuture processDeviceProfileDevicesRequestMsg(TenantId tenantId, Edge edge, DeviceProfileDevicesRequestMsg deviceProfileDevicesRequestMsg) {
log.trace("[{}] processDeviceProfileDevicesRequestMsg [{}][{}]", tenantId, edge.getName(), deviceProfileDevicesRequestMsg);
- if (deviceProfileDevicesRequestMsg.getDeviceProfileIdMSB() != 0 && deviceProfileDevicesRequestMsg.getDeviceProfileIdLSB() != 0) {
- DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(deviceProfileDevicesRequestMsg.getDeviceProfileIdMSB(), deviceProfileDevicesRequestMsg.getDeviceProfileIdLSB()));
- DeviceProfile deviceProfileById = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
- if (deviceProfileById != null) {
- syncDevices(tenantId, edge, deviceProfileById.getName());
- }
+ if (deviceProfileDevicesRequestMsg.getDeviceProfileIdMSB() == 0 || deviceProfileDevicesRequestMsg.getDeviceProfileIdLSB() == 0) {
+ return Futures.immediateFuture(null);
+ }
+ DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(deviceProfileDevicesRequestMsg.getDeviceProfileIdMSB(), deviceProfileDevicesRequestMsg.getDeviceProfileIdLSB()));
+ DeviceProfile deviceProfileById = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
+ if (deviceProfileById == null) {
+ return Futures.immediateFuture(null);
}
- return Futures.immediateFuture(null);
+ return syncDevices(tenantId, edge, deviceProfileById.getName());
}
- private void syncDevices(TenantId tenantId, Edge edge, String deviceType) {
+ private ListenableFuture syncDevices(TenantId tenantId, Edge edge, String deviceType) {
log.trace("[{}] syncDevices [{}][{}]", tenantId, edge.getName(), deviceType);
+ List> futures = new ArrayList<>();
try {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData pageData;
@@ -303,7 +330,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (Device device : pageData.getData()) {
- saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ADDED, device.getId(), null);
+ futures.add(saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ADDED, device.getId(), null));
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@@ -313,25 +340,26 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
} catch (Exception e) {
log.error("Exception during loading edge device(s) on sync!", e);
}
+ return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
}
@Override
public ListenableFuture processWidgetBundleTypesRequestMsg(TenantId tenantId, Edge edge,
WidgetBundleTypesRequestMsg widgetBundleTypesRequestMsg) {
log.trace("[{}] processWidgetBundleTypesRequestMsg [{}][{}]", tenantId, edge.getName(), widgetBundleTypesRequestMsg);
+ List> futures = new ArrayList<>();
if (widgetBundleTypesRequestMsg.getWidgetBundleIdMSB() != 0 && widgetBundleTypesRequestMsg.getWidgetBundleIdLSB() != 0) {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(new UUID(widgetBundleTypesRequestMsg.getWidgetBundleIdMSB(), widgetBundleTypesRequestMsg.getWidgetBundleIdLSB()));
WidgetsBundle widgetsBundleById = widgetsBundleService.findWidgetsBundleById(tenantId, widgetsBundleId);
if (widgetsBundleById != null) {
List widgetTypesToPush =
widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(widgetsBundleById.getTenantId(), widgetsBundleById.getAlias());
-
for (WidgetType widgetType : widgetTypesToPush) {
- saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, EdgeEventActionType.ADDED, widgetType.getId(), null);
+ futures.add(saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, EdgeEventActionType.ADDED, widgetType.getId(), null));
}
}
}
- return Futures.immediateFuture(null);
+ return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService);
}
@Override
@@ -344,46 +372,35 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
Futures.addCallback(entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List entityViews) {
- try {
- if (entityViews != null && !entityViews.isEmpty()) {
- List> futures = new ArrayList<>();
- for (EntityView entityView : entityViews) {
- ListenableFuture future = relationService.checkRelation(tenantId, edge.getId(), entityView.getId(),
- EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
- futures.add(future);
- Futures.addCallback(future, new FutureCallback<>() {
- @Override
- public void onSuccess(@Nullable Boolean result) {
- if (Boolean.TRUE.equals(result)) {
- saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ENTITY_VIEW,
- EdgeEventActionType.ADDED, entityView.getId(), null);
- }
- }
- @Override
- public void onFailure(Throwable t) {
- // Do nothing - error handles in allAsList
- }
- }, dbCallbackExecutorService);
+ if (entityViews == null || entityViews.isEmpty()) {
+ futureToSet.set(null);
+ return;
+ }
+ List> futures = new ArrayList<>();
+ for (EntityView entityView : entityViews) {
+ ListenableFuture future = relationService.checkRelation(tenantId, edge.getId(), entityView.getId(),
+ EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
+ futures.add(Futures.transformAsync(future, result -> {
+ if (Boolean.TRUE.equals(result)) {
+ return saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ENTITY_VIEW,
+ EdgeEventActionType.ADDED, entityView.getId(), null);
+ } else {
+ return Futures.immediateFuture(null);
}
- Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
- @Override
- public void onSuccess(@Nullable List result) {
- futureToSet.set(null);
- }
-
- @Override
- public void onFailure(Throwable t) {
- log.error("Exception during loading relation [{}] to edge on sync!", t, t);
- futureToSet.setException(t);
- }
- }, dbCallbackExecutorService);
- } else {
+ }, dbCallbackExecutorService));
+ }
+ Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
+ @Override
+ public void onSuccess(@Nullable List result) {
futureToSet.set(null);
}
- } catch (Exception e) {
- log.error("Exception during loading relation(s) to edge on sync!", e);
- futureToSet.setException(e);
- }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.error("Exception during loading relation to edge on sync!", t);
+ futureToSet.setException(t);
+ }
+ }, dbCallbackExecutorService);
}
@Override
@@ -395,7 +412,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
return futureToSet;
}
- private void saveEdgeEvent(TenantId tenantId,
+ private ListenableFuture saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType type,
EdgeEventActionType action,
@@ -404,10 +421,12 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
log.trace("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body);
- EdgeEvent edgeEvent = EdgeEventUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
+ EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
- edgeEventService.save(edgeEvent);
- tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
+ return Futures.transform(edgeEventService.saveAsync(edgeEvent), unused -> {
+ tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
+ return null;
+ }, dbCallbackExecutorService);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java
index 2591790137..b42dcb6b83 100644
--- a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java
+++ b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java
@@ -19,9 +19,11 @@ import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.sms.SmsSender;
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
import org.thingsboard.server.common.data.sms.config.AwsSnsSmsProviderConfiguration;
+import org.thingsboard.server.common.data.sms.config.SmppSmsProviderConfiguration;
import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration;
import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration;
import org.thingsboard.server.service.sms.aws.AwsSmsSender;
+import org.thingsboard.server.service.sms.smpp.SmppSmsSender;
import org.thingsboard.server.service.sms.twilio.TwilioSmsSender;
@Component
@@ -34,6 +36,8 @@ public class DefaultSmsSenderFactory implements SmsSenderFactory {
return new AwsSmsSender((AwsSnsSmsProviderConfiguration)config);
case TWILIO:
return new TwilioSmsSender((TwilioSmsProviderConfiguration)config);
+ case SMPP:
+ return new SmppSmsSender((SmppSmsProviderConfiguration) config);
default:
throw new RuntimeException("Unknown SMS provider type " + config.getType());
}
diff --git a/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java
new file mode 100644
index 0000000000..57c5c5d25c
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java
@@ -0,0 +1,186 @@
+/**
+ * Copyright © 2016-2022 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.server.service.sms.smpp;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.smpp.Connection;
+import org.smpp.Data;
+import org.smpp.Session;
+import org.smpp.TCPIPConnection;
+import org.smpp.TimeoutException;
+import org.smpp.WrongSessionStateException;
+import org.smpp.pdu.Address;
+import org.smpp.pdu.BindReceiver;
+import org.smpp.pdu.BindRequest;
+import org.smpp.pdu.BindResponse;
+import org.smpp.pdu.BindTransciever;
+import org.smpp.pdu.BindTransmitter;
+import org.smpp.pdu.PDUException;
+import org.smpp.pdu.SubmitSM;
+import org.smpp.pdu.SubmitSMResp;
+import org.thingsboard.rule.engine.api.sms.exception.SmsException;
+import org.thingsboard.server.common.data.sms.config.SmppSmsProviderConfiguration;
+import org.thingsboard.server.service.sms.AbstractSmsSender;
+
+import java.io.IOException;
+import java.util.Optional;
+
+@Slf4j
+public class SmppSmsSender extends AbstractSmsSender {
+ protected SmppSmsProviderConfiguration config;
+
+ protected Session smppSession;
+
+ public SmppSmsSender(SmppSmsProviderConfiguration config) {
+ if (config.getBindType() == null) {
+ config.setBindType(SmppSmsProviderConfiguration.SmppBindType.TX);
+ }
+ if (StringUtils.isNotEmpty(config.getSourceAddress())) {
+ if (config.getSourceTon() == null) {
+ config.setSourceTon((byte) 5);
+ }
+ if (config.getSourceNpi() == null) {
+ config.setSourceNpi((byte) 0);
+ }
+ }
+ if (config.getDestinationTon() == null) {
+ config.setDestinationTon((byte) 5);
+ }
+ if (config.getDestinationNpi() == null) {
+ config.setDestinationNpi((byte) 0);
+ }
+
+ this.config = config;
+ this.smppSession = initSmppSession();
+ }
+
+ private SmppSmsSender() {} // for testing purposes
+
+
+ @Override
+ public int sendSms(String numberTo, String message) throws SmsException {
+ try {
+ checkSmppSession();
+
+ SubmitSM request = new SubmitSM();
+ if (StringUtils.isNotEmpty(config.getServiceType())) {
+ request.setServiceType(config.getServiceType());
+ }
+ if (StringUtils.isNotEmpty(config.getSourceAddress())) {
+ request.setSourceAddr(new Address(config.getSourceTon(), config.getSourceNpi(), config.getSourceAddress()));
+ }
+ request.setDestAddr(new Address(config.getDestinationTon(), config.getDestinationNpi(), prepareNumber(numberTo)));
+ request.setShortMessage(message);
+ request.setDataCoding(Optional.ofNullable(config.getCodingScheme()).orElse((byte) 0));
+ request.setReplaceIfPresentFlag((byte) 0);
+ request.setEsmClass((byte) 0);
+ request.setProtocolId((byte) 0);
+ request.setPriorityFlag((byte) 0);
+ request.setRegisteredDelivery((byte) 0);
+ request.setSmDefaultMsgId((byte) 0);
+
+ SubmitSMResp response = smppSession.submit(request);
+
+ log.debug("SMPP submit command status: {}", response.getCommandStatus());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ return countMessageSegments(message);
+ }
+
+ private synchronized void checkSmppSession() {
+ if (smppSession == null || !smppSession.isOpened()) {
+ smppSession = initSmppSession();
+ }
+ }
+
+ protected Session initSmppSession() {
+ try {
+ Connection connection = new TCPIPConnection(config.getHost(), config.getPort());
+ Session session = new Session(connection);
+
+ BindRequest bindRequest;
+ switch (config.getBindType()) {
+ case TX:
+ bindRequest = new BindTransmitter();
+ break;
+ case RX:
+ bindRequest = new BindReceiver();
+ break;
+ case TRX:
+ bindRequest = new BindTransciever();
+ break;
+ default:
+ throw new UnsupportedOperationException("Unsupported bind type " + config.getBindType());
+ }
+
+ bindRequest.setSystemId(config.getSystemId());
+ bindRequest.setPassword(config.getPassword());
+
+ byte interfaceVersion;
+ switch (config.getProtocolVersion()) {
+ case "3.3":
+ interfaceVersion = Data.SMPP_V33;
+ break;
+ case "3.4":
+ interfaceVersion = Data.SMPP_V34;
+ break;
+ default:
+ throw new UnsupportedOperationException("Unsupported SMPP version: " + config.getProtocolVersion());
+ }
+ bindRequest.setInterfaceVersion(interfaceVersion);
+
+ if (StringUtils.isNotEmpty(config.getSystemType())) {
+ bindRequest.setSystemType(config.getSystemType());
+ }
+ if (StringUtils.isNotEmpty(config.getAddressRange())) {
+ bindRequest.setAddressRange(config.getDestinationTon(), config.getDestinationNpi(), config.getAddressRange());
+ }
+
+ BindResponse bindResponse = session.bind(bindRequest);
+ log.debug("SMPP bind response: {}", bindResponse.debugString());
+
+ if (bindResponse.getCommandStatus() != 0) {
+ throw new IllegalStateException("Error status when binding: " + bindResponse.getCommandStatus());
+ }
+
+ return session;
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Failed to establish SMPP session: " + ExceptionUtils.getRootCauseMessage(e), e);
+ }
+ }
+
+ private String prepareNumber(String number) {
+ if (config.getDestinationTon() == Data.GSM_TON_INTERNATIONAL) {
+ return StringUtils.removeStart(number, "+");
+ }
+ return number;
+ }
+
+ @Override
+ public void destroy() {
+ try {
+ smppSession.unbind();
+ smppSession.close();
+ } catch (TimeoutException | PDUException | IOException | WrongSessionStateException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+}
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 4eb82e93dc..0aa7b3bb66 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -283,6 +283,11 @@ sql:
batch_max_delay: "${SQL_EVENTS_BATCH_MAX_DELAY_MS:100}"
stats_print_interval_ms: "${SQL_EVENTS_BATCH_STATS_PRINT_MS:10000}"
batch_threads: "${SQL_EVENTS_BATCH_THREADS:3}" # batch thread count have to be a prime number like 3 or 5 to gain perfect hash distribution
+ edge_events:
+ batch_size: "${SQL_EDGE_EVENTS_BATCH_SIZE:1000}"
+ batch_max_delay: "${SQL_EDGE_EVENTS_BATCH_MAX_DELAY_MS:100}"
+ stats_print_interval_ms: "${SQL_EDGE_EVENTS_BATCH_STATS_PRINT_MS:10000}"
+ batch_threads: "${SQL_EDGE_EVENTS_BATCH_THREADS:3}" # batch thread count have to be a prime number like 3 or 5 to gain perfect hash distribution
# Specify whether to sort entities before batch update. Should be enabled for cluster mode to avoid deadlocks
batch_sort: "${SQL_BATCH_SORT:false}"
# Specify whether to remove null characters from strValue of attributes and timeseries before insert
@@ -487,7 +492,7 @@ spring.mvc.cors:
# Intercept path
"[/api/**]":
#Comma-separated list of origins to allow. '*' allows all origins. When not set,CORS support is disabled.
- allowed-origins: "*"
+ allowed-origin-patterns: "*"
#Comma-separated list of methods to allow. '*' allows all methods.
allowed-methods: "*"
#Comma-separated list of headers to allow in a request. '*' allows all headers.
diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
index eae6f59198..98e4c12759 100644
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
@@ -16,16 +16,25 @@
package org.thingsboard.server.controller;
import lombok.extern.slf4j.Slf4j;
+import org.junit.After;
+import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootContextLoader;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
-import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@@ -33,9 +42,49 @@ import org.springframework.test.context.web.WebAppConfiguration;
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
@Configuration
@ComponentScan({"org.thingsboard.server"})
-@WebAppConfiguration
-@SpringBootTest()
+@EnableWebSocket
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
-public abstract class AbstractControllerTest extends AbstractWebTest {
+public abstract class AbstractControllerTest extends AbstractWebTest {
+
+ public static final String WS_URL = "ws://localhost:";
+
+ @LocalServerPort
+ protected int wsPort;
+
+ private TbTestWebSocketClient wsClient; // lazy
+
+ public TbTestWebSocketClient getWsClient() {
+ if (wsClient == null) {
+ synchronized (this) {
+ try {
+ if (wsClient == null) {
+ wsClient = buildAndConnectWebSocketClient();
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return wsClient;
+ }
+
+ @Before
+ public void beforeWsTest() throws Exception {
+ // placeholder
+ }
+
+ @After
+ public void afterWsTest() throws Exception {
+ if (wsClient != null) {
+ wsClient.close();
+ }
+ }
+
+ private TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException {
+ TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws/plugins/telemetry?token=" + token));
+ assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue();
+ return wsClient;
+ }
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractInMemoryStorageTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractInMemoryStorageTest.java
index 59f7714233..efa9d9f08a 100644
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractInMemoryStorageTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/AbstractInMemoryStorageTest.java
@@ -23,20 +23,4 @@ import org.thingsboard.server.queue.memory.InMemoryStorage;
@Slf4j
public abstract class AbstractInMemoryStorageTest {
- @Before
- public void setUpInMemoryStorage() {
- log.info("set up InMemoryStorage");
- cleanupInMemStorage();
- }
-
- @After
- public void tearDownInMemoryStorage() {
- log.info("tear down InMemoryStorage");
- cleanupInMemStorage();
- }
-
- public static void cleanupInMemStorage() {
- InMemoryStorage.getInstance().cleanup();
- }
-
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
index cc561e31a8..b4d0fe87e7 100644
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
@@ -18,6 +18,9 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwt;
@@ -67,6 +70,7 @@ import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
@@ -97,6 +101,7 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppC
@Slf4j
public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
+ public static final int TIMEOUT = 30;
protected ObjectMapper mapper = new ObjectMapper();
@@ -106,7 +111,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
private static final String SYS_ADMIN_PASSWORD = "sysadmin";
protected static final String TENANT_ADMIN_EMAIL = "testtenant@thingsboard.org";
- private static final String TENANT_ADMIN_PASSWORD = "tenant";
+ protected static final String TENANT_ADMIN_PASSWORD = "tenant";
protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
private static final String CUSTOMER_USER_PASSWORD = "customer";
@@ -377,18 +382,23 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
}
}
+ protected DeviceProfile createDeviceProfile(String name) {
+ return createDeviceProfile(name, null);
+ }
+
protected DeviceProfile createDeviceProfile(String name, DeviceProfileTransportConfiguration deviceProfileTransportConfiguration) {
DeviceProfile deviceProfile = new DeviceProfile();
deviceProfile.setName(name);
deviceProfile.setType(DeviceProfileType.DEFAULT);
- deviceProfile.setTransportType(DeviceTransportType.DEFAULT);
deviceProfile.setDescription(name + " Test");
DeviceProfileData deviceProfileData = new DeviceProfileData();
DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
deviceProfileData.setConfiguration(configuration);
if (deviceProfileTransportConfiguration != null) {
+ deviceProfile.setTransportType(deviceProfileTransportConfiguration.getType());
deviceProfileData.setTransportConfiguration(deviceProfileTransportConfiguration);
} else {
+ deviceProfile.setTransportType(DeviceTransportType.DEFAULT);
deviceProfileData.setTransportConfiguration(new DefaultDeviceProfileTransportConfiguration());
}
deviceProfile.setProfileData(deviceProfileData);
@@ -397,10 +407,11 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
return deviceProfile;
}
- protected MqttDeviceProfileTransportConfiguration createMqttDeviceProfileTransportConfiguration(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration) {
+ protected MqttDeviceProfileTransportConfiguration createMqttDeviceProfileTransportConfiguration(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration, boolean sendAckOnValidationException) {
MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration();
mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_TELEMETRY_TOPIC);
mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC);
+ mqttDeviceProfileTransportConfiguration.setSendAckOnValidationException(sendAckOnValidationException);
mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration);
return mqttDeviceProfileTransportConfiguration;
}
@@ -503,16 +514,24 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType);
}
- protected T doPost(String urlTemplate, Class responseClass, String... params) throws Exception {
- return readResponse(doPost(urlTemplate, params).andExpect(status().isOk()), responseClass);
+ protected T doPost(String urlTemplate, Class responseClass, String... params) {
+ try {
+ return readResponse(doPost(urlTemplate, params).andExpect(status().isOk()), responseClass);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
protected T doPost(String urlTemplate, T content, Class responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
return readResponse(doPost(urlTemplate, content, params).andExpect(resultMatcher), responseClass);
}
- protected T doPost(String urlTemplate, T content, Class responseClass, String... params) throws Exception {
- return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
+ protected T doPost(String urlTemplate, T content, Class responseClass, String... params) {
+ try {
+ return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
protected R doPostWithResponse(String urlTemplate, T content, Class responseClass, String... params) throws Exception {
@@ -633,4 +652,15 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20));
return edge;
}
+
+ protected > ListenableFuture> deleteEntitiesAsync(String urlTemplate, List entities, ListeningExecutorService executor) {
+ List> futures = new ArrayList<>(entities.size());
+ for (T entity : entities) {
+ futures.add(executor.submit(() ->
+ doDelete(urlTemplate + entity.getId().getId())
+ .andExpect(status().isOk())));
+ }
+ return Futures.allAsList(futures);
+ }
+
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java
deleted file mode 100644
index 15884f38a0..0000000000
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * Copyright © 2016-2022 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.server.controller;
-
-import lombok.extern.slf4j.Slf4j;
-import org.junit.Assert;
-import org.junit.runner.RunWith;
-import org.springframework.boot.test.context.SpringBootContextLoader;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.web.server.LocalServerPort;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.test.annotation.DirtiesContext;
-import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringRunner;
-import java.net.URI;
-import java.net.URISyntaxException;
-
-@ActiveProfiles("test")
-@RunWith(SpringRunner.class)
-@ContextConfiguration(classes = AbstractControllerTest.class, loader = SpringBootContextLoader.class)
-@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
-@Configuration
-@ComponentScan({"org.thingsboard.server"})
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
-@Slf4j
-public abstract class AbstractWebsocketTest extends AbstractWebTest {
-
- protected static final String WS_URL = "ws://localhost:";
-
- @LocalServerPort
- protected int wsPort;
-
- protected TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException {
- TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws/plugins/telemetry?token=" + token));
- Assert.assertTrue(wsClient.connectBlocking());
- return wsClient;
- }
-
-}
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
index c44c005efb..0861ab34c8 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
@@ -24,6 +24,7 @@ import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntitySubtype;
+import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
@@ -182,6 +183,39 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
.andExpect(status().isNotFound());
}
+ @Test
+ public void testDeleteAssetAssignedToEntityView() throws Exception {
+ Asset asset1 = new Asset();
+ asset1.setName("My asset 1");
+ asset1.setType("default");
+ Asset savedAsset1 = doPost("/api/asset", asset1, Asset.class);
+
+ Asset asset2 = new Asset();
+ asset2.setName("My asset 2");
+ asset2.setType("default");
+ Asset savedAsset2 = doPost("/api/asset", asset2, Asset.class);
+
+ EntityView view = new EntityView();
+ view.setEntityId(savedAsset1.getId());
+ view.setTenantId(savedTenant.getId());
+ view.setName("My entity view");
+ view.setType("default");
+ EntityView savedView = doPost("/api/entityView", view, EntityView.class);
+
+ doDelete("/api/asset/" + savedAsset1.getId().getId().toString())
+ .andExpect(status().isBadRequest());
+
+ savedView.setEntityId(savedAsset2.getId());
+
+ doPost("/api/entityView", savedView, EntityView.class);
+
+ doDelete("/api/asset/" + savedAsset1.getId().getId().toString())
+ .andExpect(status().isOk());
+
+ doGet("/api/asset/" + savedAsset1.getId().getId().toString())
+ .andExpect(status().isNotFound());
+ }
+
@Test
public void testSaveAssetWithEmptyType() throws Exception {
Asset asset = new Asset();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
index dcf3f85a71..a7f623d2e4 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
@@ -15,16 +15,17 @@
*/
package org.thingsboard.server.controller;
-import static org.hamcrest.Matchers.containsString;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
+import org.junit.Test;
+import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
@@ -32,20 +33,28 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
-import org.junit.Assert;
-import org.junit.Test;
-import com.fasterxml.jackson.core.type.TypeReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public abstract class BaseCustomerControllerTest extends AbstractControllerTest {
+ static final TypeReference> PAGE_DATA_CUSTOMER_TYPE_REFERENCE = new TypeReference<>() {
+ };
- private IdComparator idComparator = new IdComparator<>();
+ ListeningExecutorService executor;
private Tenant savedTenant;
private User tenantAdmin;
@Before
public void beforeTest() throws Exception {
+ executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
+
loginSysAdmin();
Tenant tenant = new Tenant();
@@ -65,6 +74,8 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
@After
public void afterTest() throws Exception {
+ executor.shutdownNow();
+
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
@@ -83,11 +94,11 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
savedCustomer.setTitle("My new customer");
doPost("/api/customer", savedCustomer, Customer.class);
- Customer foundCustomer = doGet("/api/customer/"+savedCustomer.getId().getId().toString(), Customer.class);
+ Customer foundCustomer = doGet("/api/customer/" + savedCustomer.getId().getId().toString(), Customer.class);
Assert.assertEquals(foundCustomer.getTitle(), savedCustomer.getTitle());
- doDelete("/api/customer/"+savedCustomer.getId().getId().toString())
- .andExpect(status().isOk());
+ doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
+ .andExpect(status().isOk());
}
@Test
@@ -182,29 +193,30 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
public void testFindCustomers() throws Exception {
TenantId tenantId = savedTenant.getId();
- List customers = new ArrayList<>();
+ List> futures = new ArrayList<>(135);
for (int i = 0; i < 135; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
customer.setTitle("Customer" + i);
- customers.add(doPost("/api/customer", customer, Customer.class));
+ futures.add(executor.submit(() ->
+ doPost("/api/customer", customer, Customer.class)));
}
+ List customers = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
- List loadedCustomers = new ArrayList<>();
+ List loadedCustomers = new ArrayList<>(135);
PageLink pageLink = new PageLink(23);
PageData pageData = null;
do {
- pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink);
loadedCustomers.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(customers, idComparator);
- Collections.sort(loadedCustomers, idComparator);
+ assertThat(customers).containsExactlyInAnyOrderElementsOf(loadedCustomers);
- Assert.assertEquals(customers, loadedCustomers);
+ deleteEntitiesAsync("/api/customer/", loadedCustomers, executor).get(TIMEOUT, TimeUnit.SECONDS);
}
@Test
@@ -212,7 +224,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
TenantId tenantId = savedTenant.getId();
String title1 = "Customer title 1";
- List customersTitle1 = new ArrayList<>();
+ List> futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
@@ -220,10 +232,13 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
String title = title1 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
customer.setTitle(title);
- customersTitle1.add(doPost("/api/customer", customer, Customer.class));
+ futures.add(executor.submit(() ->
+ doPost("/api/customer", customer, Customer.class)));
}
+ List customersTitle1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
+
String title2 = "Customer title 2";
- List customersTitle2 = new ArrayList<>();
+ futures = new ArrayList<>(175);
for (int i = 0; i < 175; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
@@ -231,57 +246,48 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
String title = title2 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
customer.setTitle(title);
- customersTitle2.add(doPost("/api/customer", customer, Customer.class));
+ futures.add(executor.submit(() ->
+ doPost("/api/customer", customer, Customer.class)));
}
+ List customersTitle2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
+
List loadedCustomersTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData pageData = null;
do {
- pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink);
loadedCustomersTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(customersTitle1, idComparator);
- Collections.sort(loadedCustomersTitle1, idComparator);
-
- Assert.assertEquals(customersTitle1, loadedCustomersTitle1);
+ assertThat(customersTitle1).as(title1).containsExactlyInAnyOrderElementsOf(loadedCustomersTitle1);
List loadedCustomersTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
do {
- pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink);
loadedCustomersTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(customersTitle2, idComparator);
- Collections.sort(loadedCustomersTitle2, idComparator);
+ assertThat(customersTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedCustomersTitle2);
- Assert.assertEquals(customersTitle2, loadedCustomersTitle2);
-
- for (Customer customer : loadedCustomersTitle1) {
- doDelete("/api/customer/" + customer.getId().getId().toString())
- .andExpect(status().isOk());
- }
+ deleteEntitiesAsync("/api/customer/", loadedCustomersTitle1, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4, 0, title1);
- pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
- for (Customer customer : loadedCustomersTitle2) {
- doDelete("/api/customer/" + customer.getId().getId().toString())
- .andExpect(status().isOk());
- }
+ deleteEntitiesAsync("/api/customer/", loadedCustomersTitle2, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4, 0, title2);
- pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
index 27173b3e38..d6e30c644c 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
@@ -17,11 +17,17 @@ package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
@@ -41,22 +47,32 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
+import java.util.concurrent.TimeUnit;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
+@Slf4j
public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
+ static final TypeReference> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() {
+ };
- private IdComparator idComparator = new IdComparator<>();
+ ListeningExecutorService executor;
+
+ List> futures;
+ PageData pageData;
private Tenant savedTenant;
private User tenantAdmin;
@Before
public void beforeTest() throws Exception {
+ log.debug("beforeTest");
+ executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
+
loginSysAdmin();
Tenant tenant = new Tenant();
@@ -76,10 +92,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
@After
public void afterTest() throws Exception {
+ log.debug("afterTest...");
+ executor.shutdownNow();
+
loginSysAdmin();
- doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
+ doDelete("/api/tenant/" + savedTenant.getId().getId())
.andExpect(status().isOk());
+ log.debug("afterTest done");
}
@Test
@@ -98,7 +118,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(device.getName(), savedDevice.getName());
DeviceCredentials deviceCredentials =
- doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
+ doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
Assert.assertNotNull(deviceCredentials);
Assert.assertNotNull(deviceCredentials.getId());
@@ -110,7 +130,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
savedDevice.setName("My new device");
doPost("/api/device", savedDevice, Device.class);
- Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
+ Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
}
@@ -145,7 +165,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
- Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
+ Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertNotNull(foundDevice);
Assert.assertEquals(savedDevice, foundDevice);
}
@@ -180,6 +200,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals("typeA", deviceTypes.get(0).getType());
Assert.assertEquals("typeB", deviceTypes.get(1).getType());
Assert.assertEquals("typeC", deviceTypes.get(2).getType());
+
+ deleteEntitiesAsync("/api/device/", devices, executor).get(TIMEOUT, TimeUnit.SECONDS);
}
@Test
@@ -189,10 +211,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
- doDelete("/api/device/" + savedDevice.getId().getId().toString())
+ doDelete("/api/device/" + savedDevice.getId().getId())
.andExpect(status().isOk());
- doGet("/api/device/" + savedDevice.getId().getId().toString())
+ doGet("/api/device/" + savedDevice.getId().getId())
.andExpect(status().isNotFound());
}
@@ -224,18 +246,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
customer.setTitle("My customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
- Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
- + "/device/" + savedDevice.getId().getId().toString(), Device.class);
+ Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId()
+ + "/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertEquals(savedCustomer.getId(), assignedDevice.getCustomerId());
- Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
+ Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertEquals(savedCustomer.getId(), foundDevice.getCustomerId());
Device unassignedDevice =
- doDelete("/api/customer/device/" + savedDevice.getId().getId().toString(), Device.class);
+ doDelete("/api/customer/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId());
- foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
+ foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertEquals(ModelConstants.NULL_UUID, foundDevice.getCustomerId().getId());
}
@@ -246,7 +268,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/customer/" + Uuids.timeBased().toString()
- + "/device/" + savedDevice.getId().getId().toString())
+ + "/device/" + savedDevice.getId().getId())
.andExpect(status().isNotFound());
}
@@ -279,13 +301,13 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
- doPost("/api/customer/" + savedCustomer.getId().getId().toString()
- + "/device/" + savedDevice.getId().getId().toString())
+ doPost("/api/customer/" + savedCustomer.getId().getId()
+ + "/device/" + savedDevice.getId().getId())
.andExpect(status().isForbidden());
loginSysAdmin();
- doDelete("/api/tenant/" + savedTenant2.getId().getId().toString())
+ doDelete("/api/tenant/" + savedTenant2.getId().getId())
.andExpect(status().isOk());
}
@@ -296,7 +318,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
- doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
+ doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
}
@@ -307,7 +329,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
- doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
+ doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId("access_token");
@@ -315,7 +337,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
DeviceCredentials foundDeviceCredentials =
- doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
+ doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
Assert.assertEquals(deviceCredentials, foundDeviceCredentials);
}
@@ -334,7 +356,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
- doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
+ doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
deviceCredentials.setCredentialsType(null);
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isBadRequest())
@@ -348,7 +370,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
- doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
+ doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
deviceCredentials.setCredentialsId(null);
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isBadRequest())
@@ -362,7 +384,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
- doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
+ doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
DeviceCredentials newDeviceCredentials = new DeviceCredentials(new DeviceCredentialsId(Uuids.timeBased()));
newDeviceCredentials.setCreatedTime(deviceCredentials.getCreatedTime());
newDeviceCredentials.setDeviceId(deviceCredentials.getDeviceId());
@@ -380,7 +402,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
- doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
+ doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
deviceCredentials.setDeviceId(new DeviceId(Uuids.timeBased()));
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isNotFound());
@@ -388,19 +410,24 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
@Test
public void testFindTenantDevices() throws Exception {
- List devices = new ArrayList<>();
+ log.debug("testFindTenantDevices");
+ futures = new ArrayList<>(178);
for (int i = 0; i < 178; i++) {
Device device = new Device();
device.setName("Device" + i);
device.setType("default");
- devices.add(doPost("/api/device", device, Device.class));
+ futures.add(executor.submit(() ->
+ doPost("/api/device", device, Device.class)));
}
- List loadedDevices = new ArrayList<>();
+ log.debug("await create devices");
+ List devices = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
+
+ log.debug("start reading");
+ List loadedDevices = new ArrayList<>(178);
PageLink pageLink = new PageLink(23);
- PageData pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
- new TypeReference>(){}, pageLink);
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevices.addAll(pageData.getData());
if (pageData.hasNext()) {
@@ -408,16 +435,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
}
} while (pageData.hasNext());
- Collections.sort(devices, idComparator);
- Collections.sort(loadedDevices, idComparator);
-
- Assert.assertEquals(devices, loadedDevices);
+ log.debug("asserting");
+ assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices);
+ log.debug("delete devices async");
+ deleteEntitiesAsync("/api/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS);
+ log.debug("done");
}
@Test
public void testFindTenantDevicesByName() throws Exception {
String title1 = "Device title 1";
- List devicesTitle1 = new ArrayList<>();
+
+ futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@@ -425,10 +454,13 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
- devicesTitle1.add(doPost("/api/device", device, Device.class));
+ futures.add(executor.submit(() ->
+ doPost("/api/device", device, Device.class)));
}
+ List devicesTitle1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
+
String title2 = "Device title 2";
- List devicesTitle2 = new ArrayList<>();
+ futures = new ArrayList<>(75);
for (int i = 0; i < 75; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@@ -436,59 +468,50 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
- devicesTitle2.add(doPost("/api/device", device, Device.class));
+ futures.add(executor.submit(() ->
+ doPost("/api/device", device, Device.class)));
}
+ List devicesTitle2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
- List loadedDevicesTitle1 = new ArrayList<>();
+ List loadedDevicesTitle1 = new ArrayList<>(143);
PageLink pageLink = new PageLink(15, 0, title1);
- PageData pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
- new TypeReference>(){}, pageLink);
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevicesTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(devicesTitle1, idComparator);
- Collections.sort(loadedDevicesTitle1, idComparator);
-
- Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
+ assertThat(devicesTitle1).as(title1).containsExactlyInAnyOrderElementsOf(loadedDevicesTitle1);
- List loadedDevicesTitle2 = new ArrayList<>();
+ List loadedDevicesTitle2 = new ArrayList<>(75);
pageLink = new PageLink(4, 0, title2);
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
- new TypeReference>(){}, pageLink);
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevicesTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(devicesTitle2, idComparator);
- Collections.sort(loadedDevicesTitle2, idComparator);
+ assertThat(devicesTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesTitle2);
- Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
+ deleteEntitiesAsync("/api/device/", loadedDevicesTitle1, executor).get(TIMEOUT, TimeUnit.SECONDS);
- for (Device device : loadedDevicesTitle1) {
- doDelete("/api/device/" + device.getId().getId().toString())
- .andExpect(status().isOk());
- }
pageLink = new PageLink(4, 0, title1);
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
- new TypeReference>(){}, pageLink);
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
- for (Device device : loadedDevicesTitle2) {
- doDelete("/api/device/" + device.getId().getId().toString())
- .andExpect(status().isOk());
- }
+ deleteEntitiesAsync("/api/device/", loadedDevicesTitle2, executor).get(TIMEOUT, TimeUnit.SECONDS);
+
pageLink = new PageLink(4, 0, title2);
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
- new TypeReference>(){}, pageLink);
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@@ -497,7 +520,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
public void testFindTenantDevicesByType() throws Exception {
String title1 = "Device title 1";
String type1 = "typeA";
- List devicesType1 = new ArrayList<>();
+ futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@@ -505,11 +528,17 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type1);
- devicesType1.add(doPost("/api/device", device, Device.class));
+ futures.add(executor.submit(() ->
+ doPost("/api/device", device, Device.class)));
+ if (i == 0) {
+ futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time
+ }
}
+ List devicesType1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
+
String title2 = "Device title 2";
String type2 = "typeB";
- List devicesType2 = new ArrayList<>();
+ futures = new ArrayList<>(75);
for (int i = 0; i < 75; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@@ -517,61 +546,54 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type2);
- devicesType2.add(doPost("/api/device", device, Device.class));
+ futures.add(executor.submit(() ->
+ doPost("/api/device", device, Device.class)));
+ if (i == 0) {
+ futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time
+ }
}
- List loadedDevicesType1 = new ArrayList<>();
+ List devicesType2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
+
+ List loadedDevicesType1 = new ArrayList<>(143);
PageLink pageLink = new PageLink(15);
- PageData pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
- new TypeReference>(){}, pageLink, type1);
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink, type1);
loadedDevicesType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(devicesType1, idComparator);
- Collections.sort(loadedDevicesType1, idComparator);
+ assertThat(devicesType1).as(title1).containsExactlyInAnyOrderElementsOf(loadedDevicesType1);
- Assert.assertEquals(devicesType1, loadedDevicesType1);
-
- List loadedDevicesType2 = new ArrayList<>();
+ List loadedDevicesType2 = new ArrayList<>(75);
pageLink = new PageLink(4);
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
- new TypeReference>(){}, pageLink, type2);
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink, type2);
loadedDevicesType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(devicesType2, idComparator);
- Collections.sort(loadedDevicesType2, idComparator);
-
- Assert.assertEquals(devicesType2, loadedDevicesType2);
+ assertThat(devicesType2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesType2);
- for (Device device : loadedDevicesType1) {
- doDelete("/api/device/" + device.getId().getId().toString())
- .andExpect(status().isOk());
- }
+ deleteEntitiesAsync("/api/device/", loadedDevicesType1, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
- new TypeReference>(){}, pageLink, type1);
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
- for (Device device : loadedDevicesType2) {
- doDelete("/api/device/" + device.getId().getId().toString())
- .andExpect(status().isOk());
- }
+ deleteEntitiesAsync("/api/device/", loadedDevicesType2, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
- new TypeReference>(){}, pageLink, type2);
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@@ -583,32 +605,35 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
customer = doPost("/api/customer", customer, Customer.class);
CustomerId customerId = customer.getId();
- List devices = new ArrayList<>();
+ futures = new ArrayList<>(128);
for (int i = 0; i < 128; i++) {
Device device = new Device();
device.setName("Device" + i);
device.setType("default");
- device = doPost("/api/device", device, Device.class);
- devices.add(doPost("/api/customer/" + customerId.getId().toString()
- + "/device/" + device.getId().getId().toString(), Device.class));
+ ListenableFuture future = executor.submit(() -> doPost("/api/device", device, Device.class));
+ futures.add(Futures.transform(future, (dev) ->
+ doPost("/api/customer/" + customerId.getId()
+ + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor()));
}
- List loadedDevices = new ArrayList<>();
+ List devices = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
+
+ List loadedDevices = new ArrayList<>(128);
PageLink pageLink = new PageLink(23);
- PageData pageData = null;
do {
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?",
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevices.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(devices, idComparator);
- Collections.sort(loadedDevices, idComparator);
+ assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices);
- Assert.assertEquals(devices, loadedDevices);
+ log.debug("delete devices async");
+ deleteEntitiesAsync("/api/customer/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS);
+ log.debug("done");
}
@Test
@@ -619,7 +644,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
CustomerId customerId = customer.getId();
String title1 = "Device title 1";
- List devicesTitle1 = new ArrayList<>();
+ futures = new ArrayList<>(125);
for (int i = 0; i < 125; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@@ -627,12 +652,15 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
- device = doPost("/api/device", device, Device.class);
- devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
- + "/device/" + device.getId().getId().toString(), Device.class));
+ ListenableFuture future = executor.submit(() -> doPost("/api/device", device, Device.class));
+ futures.add(Futures.transform(future, (dev) ->
+ doPost("/api/customer/" + customerId.getId()
+ + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor()));
}
+ List devicesTitle1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
+
String title2 = "Device title 2";
- List devicesTitle2 = new ArrayList<>();
+ futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@@ -640,61 +668,52 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
- device = doPost("/api/device", device, Device.class);
- devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
- + "/device/" + device.getId().getId().toString(), Device.class));
+ ListenableFuture future = executor.submit(() -> doPost("/api/device", device, Device.class));
+ futures.add(Futures.transform(future, (dev) ->
+ doPost("/api/customer/" + customerId.getId()
+ + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor()));
}
+ List devicesTitle2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
- List loadedDevicesTitle1 = new ArrayList<>();
+ List loadedDevicesTitle1 = new ArrayList<>(125);
PageLink pageLink = new PageLink(15, 0, title1);
- PageData pageData = null;
do {
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?",
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevicesTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(devicesTitle1, idComparator);
- Collections.sort(loadedDevicesTitle1, idComparator);
+ assertThat(devicesTitle1).as(title1).containsExactlyInAnyOrderElementsOf(loadedDevicesTitle1);
- Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
-
- List loadedDevicesTitle2 = new ArrayList<>();
+ List loadedDevicesTitle2 = new ArrayList<>(143);
pageLink = new PageLink(4, 0, title2);
do {
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?",
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevicesTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(devicesTitle2, idComparator);
- Collections.sort(loadedDevicesTitle2, idComparator);
+ assertThat(devicesTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesTitle2);
- Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
+ deleteEntitiesAsync("/api/customer/device/", loadedDevicesTitle1, executor).get(TIMEOUT, TimeUnit.SECONDS);
- for (Device device : loadedDevicesTitle1) {
- doDelete("/api/customer/device/" + device.getId().getId().toString())
- .andExpect(status().isOk());
- }
pageLink = new PageLink(4, 0, title1);
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?",
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
- for (Device device : loadedDevicesTitle2) {
- doDelete("/api/customer/device/" + device.getId().getId().toString())
- .andExpect(status().isOk());
- }
+ deleteEntitiesAsync("/api/customer/device/", loadedDevicesTitle2, executor).get(TIMEOUT, TimeUnit.SECONDS);
+
pageLink = new PageLink(4, 0, title2);
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
- new TypeReference>(){}, pageLink);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?",
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@@ -708,7 +727,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
String title1 = "Device title 1";
String type1 = "typeC";
- List devicesType1 = new ArrayList<>();
+ futures = new ArrayList<>(125);
for (int i = 0; i < 125; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@@ -716,13 +735,19 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type1);
- device = doPost("/api/device", device, Device.class);
- devicesType1.add(doPost("/api/customer/" + customerId.getId().toString()
- + "/device/" + device.getId().getId().toString(), Device.class));
+ ListenableFuture future = executor.submit(() -> doPost("/api/device", device, Device.class));
+ futures.add(Futures.transform(future, (dev) ->
+ doPost("/api/customer/" + customerId.getId()
+ + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor()));
+ if (i == 0) {
+ futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time
+ }
}
+ List devicesType1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
+
String title2 = "Device title 2";
String type2 = "typeD";
- List devicesType2 = new ArrayList<>();
+ futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@@ -730,63 +755,55 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type2);
- device = doPost("/api/device", device, Device.class);
- devicesType2.add(doPost("/api/customer/" + customerId.getId().toString()
- + "/device/" + device.getId().getId().toString(), Device.class));
+ ListenableFuture future = executor.submit(() -> doPost("/api/device", device, Device.class));
+ futures.add(Futures.transform(future, (dev) ->
+ doPost("/api/customer/" + customerId.getId()
+ + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor()));
+ if (i == 0) {
+ futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time
+ }
}
+ List devicesType2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
- List loadedDevicesType1 = new ArrayList<>();
+ List loadedDevicesType1 = new ArrayList<>(125);
PageLink pageLink = new PageLink(15);
- PageData pageData = null;
do {
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
- new TypeReference>(){}, pageLink, type1);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?type={type}&",
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink, type1);
loadedDevicesType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(devicesType1, idComparator);
- Collections.sort(loadedDevicesType1, idComparator);
+ assertThat(devicesType1).as(title1).containsExactlyInAnyOrderElementsOf(loadedDevicesType1);
- Assert.assertEquals(devicesType1, loadedDevicesType1);
-
- List loadedDevicesType2 = new ArrayList<>();
+ List loadedDevicesType2 = new ArrayList<>(143);
pageLink = new PageLink(4);
do {
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
- new TypeReference>(){}, pageLink, type2);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?type={type}&",
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink, type2);
loadedDevicesType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
- Collections.sort(devicesType2, idComparator);
- Collections.sort(loadedDevicesType2, idComparator);
-
- Assert.assertEquals(devicesType2, loadedDevicesType2);
+ assertThat(devicesType2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesType2);
- for (Device device : loadedDevicesType1) {
- doDelete("/api/customer/device/" + device.getId().getId().toString())
- .andExpect(status().isOk());
- }
+ deleteEntitiesAsync("/api/customer/device/", loadedDevicesType1, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4);
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
- new TypeReference>(){}, pageLink, type1);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?type={type}&",
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
- for (Device device : loadedDevicesType2) {
- doDelete("/api/customer/device/" + device.getId().getId().toString())
- .andExpect(status().isOk());
- }
+ deleteEntitiesAsync("/api/customer/device/", loadedDevicesType2, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4);
- pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
- new TypeReference>(){}, pageLink, type2);
+ pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?type={type}&",
+ PAGE_DATA_DEVICE_TYPE_REF, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@@ -828,17 +845,17 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
login("tenant2@thingsboard.org", "testPassword1");
Device assignedDevice = doPost("/api/tenant/" + savedDifferentTenant.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class);
- doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class, status().isNotFound());
+ doGet("/api/device/" + assignedDevice.getId().getId(), Device.class, status().isNotFound());
login("tenant9@thingsboard.org", "testPassword1");
- Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class);
+ Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId(), Device.class);
Assert.assertNotNull(foundDevice1);
doGet("/api/relation?fromId=" + savedDevice.getId().getId() + "&fromType=DEVICE&relationType=Contains&toId=" + savedAnotherDevice.getId().getId() + "&toType=DEVICE", EntityRelation.class, status().isNotFound());
loginSysAdmin();
- doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString())
+ doDelete("/api/tenant/" + savedDifferentTenant.getId().getId())
.andExpect(status().isOk());
}
@@ -852,19 +869,19 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
- doPost("/api/edge/" + savedEdge.getId().getId().toString()
- + "/device/" + savedDevice.getId().getId().toString(), Device.class);
+ doPost("/api/edge/" + savedEdge.getId().getId()
+ + "/device/" + savedDevice.getId().getId(), Device.class);
- PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/devices?",
- new TypeReference>() {}, new PageLink(100));
+ pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?",
+ PAGE_DATA_DEVICE_TYPE_REF, new PageLink(100));
Assert.assertEquals(1, pageData.getData().size());
- doDelete("/api/edge/" + savedEdge.getId().getId().toString()
- + "/device/" + savedDevice.getId().getId().toString(), Device.class);
+ doDelete("/api/edge/" + savedEdge.getId().getId()
+ + "/device/" + savedDevice.getId().getId(), Device.class);
- pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/devices?",
- new TypeReference>() {}, new PageLink(100));
+ pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?",
+ PAGE_DATA_DEVICE_TYPE_REF, new PageLink(100));
Assert.assertEquals(0, pageData.getData().size());
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
index b77a7d9f41..143fdce0f3 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
@@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
+import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
@@ -93,7 +94,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
@Test
public void testSaveDeviceProfile() throws Exception {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
Assert.assertNotNull(savedDeviceProfile);
Assert.assertNotNull(savedDeviceProfile.getId());
@@ -112,13 +113,13 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
@Test
public void saveDeviceProfileWithViolationOfValidation() throws Exception {
- doPost("/api/deviceProfile", this.createDeviceProfile(RandomStringUtils.randomAlphabetic(300), null))
+ doPost("/api/deviceProfile", this.createDeviceProfile(RandomStringUtils.randomAlphabetic(300)))
.andExpect(statusReason(containsString("length of name must be equal or less than 255")));
}
@Test
public void testFindDeviceProfileById() throws Exception {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString(), DeviceProfile.class);
Assert.assertNotNull(foundDeviceProfile);
@@ -127,7 +128,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
@Test
public void testFindDeviceProfileInfoById() throws Exception {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
DeviceProfileInfo foundDeviceProfileInfo = doGet("/api/deviceProfileInfo/"+savedDeviceProfile.getId().getId().toString(), DeviceProfileInfo.class);
Assert.assertNotNull(foundDeviceProfileInfo);
@@ -149,7 +150,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
@Test
public void testSetDefaultDeviceProfile() throws Exception {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile 1", null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile 1");
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
DeviceProfile defaultDeviceProfile = doPost("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString()+"/default", DeviceProfile.class);
Assert.assertNotNull(defaultDeviceProfile);
@@ -169,19 +170,19 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
@Test
public void testSaveDeviceProfileWithSameName() throws Exception {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk());
- DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile", null);
+ DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile");
doPost("/api/deviceProfile", deviceProfile2).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device profile with such name already exists")));
}
@Test
public void testSaveDeviceProfileWithSameProvisionDeviceKey() throws Exception {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
deviceProfile.setProvisionDeviceKey("testProvisionDeviceKey");
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk());
- DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2", null);
+ DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2");
deviceProfile2.setProvisionDeviceKey("testProvisionDeviceKey");
doPost("/api/deviceProfile", deviceProfile2).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device profile with such provision device key already exists")));
@@ -190,7 +191,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
@Ignore
@Test
public void testChangeDeviceProfileTypeWithExistingDevices() throws Exception {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
Device device = new Device();
device.setName("Test device");
@@ -205,7 +206,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
@Test
public void testChangeDeviceProfileTransportTypeWithExistingDevices() throws Exception {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
Device device = new Device();
device.setName("Test device");
@@ -219,7 +220,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
@Test
public void testDeleteDeviceProfileWithExistingDevice() throws Exception {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
Device device = new Device();
@@ -236,7 +237,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
@Test
public void testDeleteDeviceProfile() throws Exception {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
doDelete("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString())
@@ -257,7 +258,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
deviceProfiles.addAll(pageData.getData());
for (int i=0;i<28;i++) {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i, null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i);
deviceProfiles.add(doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class));
}
@@ -302,7 +303,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
deviceProfiles.addAll(deviceProfilePageData.getData());
for (int i=0;i<28;i++) {
- DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i, null);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i);
deviceProfiles.add(doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class));
}
@@ -835,19 +836,35 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
"}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: params!");
}
+ @Test
+ public void testSaveDeviceProfileWithSendAckOnValidationException() throws Exception {
+ JsonTransportPayloadConfiguration jsonTransportPayloadConfiguration = new JsonTransportPayloadConfiguration();
+ MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(jsonTransportPayloadConfiguration, true);
+ DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", mqttDeviceProfileTransportConfiguration);
+ DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
+ Assert.assertNotNull(savedDeviceProfile);
+ Assert.assertEquals(savedDeviceProfile.getTransportType(), DeviceTransportType.MQTT);
+ Assert.assertTrue(savedDeviceProfile.getProfileData().getTransportConfiguration() instanceof MqttDeviceProfileTransportConfiguration);
+ MqttDeviceProfileTransportConfiguration transportConfiguration = (MqttDeviceProfileTransportConfiguration) savedDeviceProfile.getProfileData().getTransportConfiguration();
+ Assert.assertTrue(transportConfiguration.isSendAckOnValidationException());
+ DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+ savedDeviceProfile.getId().getId().toString(), DeviceProfile.class);
+ Assert.assertEquals(savedDeviceProfile, foundDeviceProfile);
+ }
+
private DeviceProfile testSaveDeviceProfileWithProtoPayloadType(String schema) throws Exception {
ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema, null, null);
- MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration);
+ MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration, false);
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", mqttDeviceProfileTransportConfiguration);
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
- DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString(), DeviceProfile.class);
- Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfile.getName());
+ Assert.assertNotNull(savedDeviceProfile);
+ DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+ savedDeviceProfile.getId().getId().toString(), DeviceProfile.class);
+ Assert.assertEquals(savedDeviceProfile, foundDeviceProfile);
return savedDeviceProfile;
}
private void testSaveDeviceProfileWithInvalidProtoSchema(String schema, String errorMsg) throws Exception {
ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema, null, null);
- MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration);
+ MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration, false);
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", mqttDeviceProfileTransportConfiguration);
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(errorMsg)));
@@ -855,7 +872,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
private void testSaveDeviceProfileWithInvalidRpcRequestProtoSchema(String schema, String errorMsg) throws Exception {
ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema, schema, null);
- MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration);
+ MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration, false);
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", mqttDeviceProfileTransportConfiguration);
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(errorMsg)));
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java
index 6bf1a6f1d2..24b2053c67 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java
@@ -23,6 +23,7 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.test.context.TestPropertySource;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
@@ -54,6 +55,9 @@ import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
+@TestPropertySource(properties = {
+ "edges.enabled=true",
+})
public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
public static final String EDGE_HOST = "localhost";
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeEventControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeEventControllerTest.java
index 0f8b046fb9..ef66270130 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeEventControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeEventControllerTest.java
@@ -22,6 +22,7 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.test.context.TestPropertySource;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
@@ -40,6 +41,9 @@ import java.util.concurrent.TimeUnit;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+@TestPropertySource(properties = {
+ "edges.enabled=true",
+})
@Slf4j
public abstract class BaseEdgeEventControllerTest extends AbstractControllerTest {
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
index 07955c0f93..155c768cb0 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
@@ -17,8 +17,13 @@ package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
+import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttMessage;
@@ -27,6 +32,9 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.web.servlet.ResultActions;
+import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityView;
@@ -39,70 +47,71 @@ import org.thingsboard.server.common.data.objects.AttributesEntityView;
import org.thingsboard.server.common.data.objects.TelemetryEntityView;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.common.data.query.DeviceTypeFilter;
+import org.thingsboard.server.common.data.query.EntityKey;
+import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
+@TestPropertySource(properties = {
+ "transport.mqtt.enabled=true",
+ "js.evaluator=mock",
+})
@Slf4j
public abstract class BaseEntityViewControllerTest extends AbstractControllerTest {
+ static final TypeReference> PAGE_DATA_ENTITY_VIEW_TYPE_REF = new TypeReference<>() {
+ };
+ static final TypeReference> PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF = new TypeReference<>() {
+ };
- private IdComparator idComparator;
- private Tenant savedTenant;
- private User tenantAdmin;
private Device testDevice;
private TelemetryEntityView telemetry;
+ List> deleteFutures = new ArrayList<>();
+ ListeningExecutorService executor;
+
@Before
public void beforeTest() throws Exception {
- loginSysAdmin();
- idComparator = new IdComparator<>();
+ log.debug("beforeTest");
+ executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
- savedTenant = doPost("/api/tenant", getNewTenant("My tenant"), Tenant.class);
- Assert.assertNotNull(savedTenant);
-
- tenantAdmin = new User();
- tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
- tenantAdmin.setTenantId(savedTenant.getId());
- tenantAdmin.setEmail("tenant2@thingsboard.org");
- tenantAdmin.setFirstName("Joe");
- tenantAdmin.setLastName("Downs");
- tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
+ loginTenantAdmin();
Device device = new Device();
- device.setName("Test device");
+ device.setName("Test device 4view");
device.setType("default");
testDevice = doPost("/api/device", device, Device.class);
telemetry = new TelemetryEntityView(
- Arrays.asList("tsKey1", "tsKey2", "tsKey3"),
+ List.of("tsKey1", "tsKey2", "tsKey3"),
new AttributesEntityView(
- Arrays.asList("caKey1", "caKey2", "caKey3", "caKey4"),
- Arrays.asList("saKey1", "saKey2", "saKey3", "saKey4"),
- Arrays.asList("shKey1", "shKey2", "shKey3", "shKey4")));
+ List.of("caKey1", "caKey2", "caKey3", "caKey4"),
+ List.of("saKey1", "saKey2", "saKey3", "saKey4"),
+ List.of("shKey1", "shKey2", "shKey3", "shKey4")));
}
@After
public void afterTest() throws Exception {
- loginSysAdmin();
-
- doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
- .andExpect(status().isOk());
+ executor.shutdownNow();
}
@Test
@@ -115,22 +124,29 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testSaveEntityView() throws Exception {
- EntityView savedView = getNewSavedEntityView("Test entity view");
+ String name = "Test entity view";
+ EntityView savedView = getNewSavedEntityView(name);
Assert.assertNotNull(savedView);
Assert.assertNotNull(savedView.getId());
Assert.assertTrue(savedView.getCreatedTime() > 0);
- assertEquals(savedTenant.getId(), savedView.getTenantId());
+ assertEquals(tenantId, savedView.getTenantId());
Assert.assertNotNull(savedView.getCustomerId());
assertEquals(NULL_UUID, savedView.getCustomerId().getId());
- assertEquals(savedView.getName(), savedView.getName());
+ assertEquals(name, savedView.getName());
+
+ EntityView foundEntityView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class);
+
+ assertEquals(savedView, foundEntityView);
savedView.setName("New test entity view");
+
doPost("/api/entityView", savedView, EntityView.class);
- EntityView foundEntityView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class);
+ foundEntityView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class);
+
+ assertEquals(savedView, foundEntityView);
- assertEquals(foundEntityView.getName(), savedView.getName());
- assertEquals(foundEntityView.getKeys(), telemetry);
+ doGet("/api/tenant/entityViews?entityViewName=" + name, EntityView.class, status().isNotFound());
}
@Test
@@ -221,7 +237,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
Customer customer = getNewCustomer("Different customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
- login(tenantAdmin.getEmail(), "testPassword1");
+ login(TENANT_ADMIN_EMAIL, TENANT_ADMIN_PASSWORD);
EntityView savedView = getNewSavedEntityView("Test entity view");
@@ -240,21 +256,18 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
CustomerId customerId = customer.getId();
String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViewInfos?";
- List views = new ArrayList<>();
+ List> viewFutures = new ArrayList<>(128);
for (int i = 0; i < 128; i++) {
- views.add(
+ String entityName = "Test entity view " + i;
+ viewFutures.add(executor.submit(() ->
new EntityViewInfo(doPost("/api/customer/" + customerId.getId().toString() + "/entityView/"
- + getNewSavedEntityView("Test entity view " + i).getId().getId().toString(), EntityView.class),
- customer.getTitle(), customer.isPublic())
- );
+ + getNewSavedEntityView(entityName).getId().getId().toString(), EntityView.class),
+ customer.getTitle(), customer.isPublic())));
}
-
+ List entityViewInfos = Futures.allAsList(viewFutures).get(TIMEOUT, SECONDS);
List loadedViews = loadListOfInfo(new PageLink(23), urlTemplate);
- Collections.sort(views, idComparator);
- Collections.sort(loadedViews, idComparator);
-
- assertEquals(views, loadedViews);
+ assertThat(entityViewInfos).containsExactlyInAnyOrderElementsOf(loadedViews);
}
@Test
@@ -263,35 +276,37 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViews?";
String name1 = "Entity view name1";
- List namesOfView1 = fillListOf(125, name1, "/api/customer/" + customerId.getId().toString()
- + "/entityView/");
+ List namesOfView1 = Futures.allAsList(fillListByTemplate(125, name1, "/api/customer/" + customerId.getId().toString()
+ + "/entityView/")).get(TIMEOUT, SECONDS);
List loadedNamesOfView1 = loadListOf(new PageLink(15, 0, name1), urlTemplate);
- Collections.sort(namesOfView1, idComparator);
- Collections.sort(loadedNamesOfView1, idComparator);
- assertEquals(namesOfView1, loadedNamesOfView1);
+ assertThat(namesOfView1).as(name1).containsExactlyInAnyOrderElementsOf(loadedNamesOfView1);
String name2 = "Entity view name2";
- List NamesOfView2 = fillListOf(143, name2, "/api/customer/" + customerId.getId().toString()
- + "/entityView/");
+ List namesOfView2 = Futures.allAsList(fillListByTemplate(143, name2, "/api/customer/" + customerId.getId().toString()
+ + "/entityView/")).get(TIMEOUT, SECONDS);
List loadedNamesOfView2 = loadListOf(new PageLink(4, 0, name2), urlTemplate);
- Collections.sort(NamesOfView2, idComparator);
- Collections.sort(loadedNamesOfView2, idComparator);
- assertEquals(NamesOfView2, loadedNamesOfView2);
+ assertThat(namesOfView2).as(name2).containsExactlyInAnyOrderElementsOf(loadedNamesOfView2);
+ deleteFutures.clear();
for (EntityView view : loadedNamesOfView1) {
- doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
+ deleteFutures.add(executor.submit(() ->
+ doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk())));
}
- PageData pageData = doGetTypedWithPageLink(urlTemplate,
- new TypeReference>() {
- }, new PageLink(4, 0, name1));
+ Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS);
+
+ PageData pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF,
+ new PageLink(4, 0, name1));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
+ deleteFutures.clear();
for (EntityView view : loadedNamesOfView2) {
- doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
+ deleteFutures.add(executor.submit(() ->
+ doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk())));
}
- pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference>() {
- },
+ Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS);
+
+ pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF,
new PageLink(4, 0, name2));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
@@ -299,49 +314,51 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testGetTenantEntityViews() throws Exception {
-
- List views = new ArrayList<>();
+ List> entityViewInfoFutures = new ArrayList<>(178);
for (int i = 0; i < 178; i++) {
- views.add(new EntityViewInfo(getNewSavedEntityView("Test entity view" + i), null, false));
+ ListenableFuture entityViewFuture = getNewSavedEntityViewAsync("Test entity view" + i);
+ entityViewInfoFutures.add(Futures.transform(entityViewFuture,
+ view -> new EntityViewInfo(view, null, false),
+ MoreExecutors.directExecutor()));
}
+ List entityViewInfos = Futures.allAsList(entityViewInfoFutures).get(TIMEOUT, SECONDS);
List loadedViews = loadListOfInfo(new PageLink(23), "/api/tenant/entityViewInfos?");
-
- Collections.sort(views, idComparator);
- Collections.sort(loadedViews, idComparator);
-
- assertEquals(views, loadedViews);
+ assertThat(entityViewInfos).containsExactlyInAnyOrderElementsOf(loadedViews);
}
@Test
public void testGetTenantEntityViewsByName() throws Exception {
String name1 = "Entity view name1";
- List namesOfView1 = fillListOf(143, name1);
- List loadedNamesOfView1 = loadListOf(new PageLink(15, 0, name1), "/api/tenant/entityViews?");
- Collections.sort(namesOfView1, idComparator);
- Collections.sort(loadedNamesOfView1, idComparator);
- assertEquals(namesOfView1, loadedNamesOfView1);
+ List namesOfView1 = Futures.allAsList(fillListOf(17, name1)).get(TIMEOUT, SECONDS);
+ List loadedNamesOfView1 = loadListOf(new PageLink(5, 0, name1), "/api/tenant/entityViews?");
+ assertThat(namesOfView1).as(name1).containsExactlyInAnyOrderElementsOf(loadedNamesOfView1);
String name2 = "Entity view name2";
- List NamesOfView2 = fillListOf(75, name2);
+ List namesOfView2 = Futures.allAsList(fillListOf(15, name2)).get(TIMEOUT, SECONDS);
+ ;
List loadedNamesOfView2 = loadListOf(new PageLink(4, 0, name2), "/api/tenant/entityViews?");
- Collections.sort(NamesOfView2, idComparator);
- Collections.sort(loadedNamesOfView2, idComparator);
- assertEquals(NamesOfView2, loadedNamesOfView2);
+ assertThat(namesOfView2).as(name2).containsExactlyInAnyOrderElementsOf(loadedNamesOfView2);
+ deleteFutures.clear();
for (EntityView view : loadedNamesOfView1) {
- doDelete("/api/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
+ deleteFutures.add(executor.submit(() ->
+ doDelete("/api/entityView/" + view.getId().getId().toString()).andExpect(status().isOk())));
}
- PageData pageData = doGetTypedWithPageLink("/api/tenant/entityViews?",
- new TypeReference>() {
- }, new PageLink(4, 0, name1));
+ Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS);
+
+ PageData pageData = doGetTypedWithPageLink("/api/tenant/entityViews?", PAGE_DATA_ENTITY_VIEW_TYPE_REF,
+ new PageLink(4, 0, name1));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
+ deleteFutures.clear();
for (EntityView view : loadedNamesOfView2) {
- doDelete("/api/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
+ deleteFutures.add(executor.submit(() ->
+ doDelete("/api/entityView/" + view.getId().getId().toString()).andExpect(status().isOk())));
}
- pageData = doGetTypedWithPageLink("/api/tenant/entityViews?", new TypeReference>() {
- },
+ Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS);
+
+ pageData = doGetTypedWithPageLink("/api/tenant/entityViews?", PAGE_DATA_ENTITY_VIEW_TYPE_REF,
new PageLink(4, 0, name2));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
@@ -349,20 +366,21 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testTheCopyOfAttrsIntoTSForTheView() throws Exception {
+ Set expectedActualAttributesSet = Set.of("caKey1", "caKey2", "caKey3", "caKey4");
Set actualAttributesSet =
- getAttributesByKeys("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}");
-
- Set expectedActualAttributesSet =
- new HashSet<>(Arrays.asList("caKey1", "caKey2", "caKey3", "caKey4"));
- assertTrue(actualAttributesSet.containsAll(expectedActualAttributesSet));
-
+ getAttributesByKeys("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}", expectedActualAttributesSet);
+ log.debug("got correct actualAttributesSet, saving new entity view...");
EntityView savedView = getNewSavedEntityView("Test entity view");
- Thread.sleep(1000);
-
- List