Commit 00bd26f1f5a7bb450aba354822d712a5aff935cd

Authored by Andrii Shvaika
2 parents 37891ec6 069a51c0

Merge with master

Showing 96 changed files with 2840 additions and 346 deletions

Too many changes to show.

To preserve performance only 96 of 221 files are displayed.

@@ -90,6 +90,10 @@ @@ -90,6 +90,10 @@
90 <artifactId>lwm2m</artifactId> 90 <artifactId>lwm2m</artifactId>
91 </dependency> 91 </dependency>
92 <dependency> 92 <dependency>
  93 + <groupId>org.thingsboard.common.transport</groupId>
  94 + <artifactId>snmp</artifactId>
  95 + </dependency>
  96 + <dependency>
93 <groupId>org.thingsboard</groupId> 97 <groupId>org.thingsboard</groupId>
94 <artifactId>dao</artifactId> 98 <artifactId>dao</artifactId>
95 </dependency> 99 </dependency>
@@ -22,10 +22,10 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -22,10 +22,10 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
22 import org.springframework.context.event.EventListener; 22 import org.springframework.context.event.EventListener;
23 import org.springframework.core.annotation.Order; 23 import org.springframework.core.annotation.Order;
24 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
  25 +import org.thingsboard.common.util.ThingsBoardExecutors;
25 import org.thingsboard.common.util.ThingsBoardThreadFactory; 26 import org.thingsboard.common.util.ThingsBoardThreadFactory;
26 import org.thingsboard.server.actors.ActorSystemContext; 27 import org.thingsboard.server.actors.ActorSystemContext;
27 import org.thingsboard.server.actors.DefaultTbActorSystem; 28 import org.thingsboard.server.actors.DefaultTbActorSystem;
28 -import org.thingsboard.server.actors.TbActorId;  
29 import org.thingsboard.server.actors.TbActorRef; 29 import org.thingsboard.server.actors.TbActorRef;
30 import org.thingsboard.server.actors.TbActorSystem; 30 import org.thingsboard.server.actors.TbActorSystem;
31 import org.thingsboard.server.actors.TbActorSystemSettings; 31 import org.thingsboard.server.actors.TbActorSystemSettings;
@@ -33,14 +33,13 @@ import org.thingsboard.server.actors.app.AppActor; @@ -33,14 +33,13 @@ import org.thingsboard.server.actors.app.AppActor;
33 import org.thingsboard.server.actors.app.AppInitMsg; 33 import org.thingsboard.server.actors.app.AppInitMsg;
34 import org.thingsboard.server.actors.stats.StatsActor; 34 import org.thingsboard.server.actors.stats.StatsActor;
35 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 35 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
36 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;  
37 import org.thingsboard.server.queue.discovery.TbApplicationEventListener; 36 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
  37 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
38 38
39 import javax.annotation.PostConstruct; 39 import javax.annotation.PostConstruct;
40 import javax.annotation.PreDestroy; 40 import javax.annotation.PreDestroy;
41 import java.util.concurrent.ExecutorService; 41 import java.util.concurrent.ExecutorService;
42 import java.util.concurrent.Executors; 42 import java.util.concurrent.Executors;
43 -import java.util.concurrent.ScheduledExecutorService;  
44 43
45 @Service 44 @Service
46 @Slf4j 45 @Slf4j
@@ -110,7 +109,7 @@ public class DefaultActorService extends TbApplicationEventListener<PartitionCha @@ -110,7 +109,7 @@ public class DefaultActorService extends TbApplicationEventListener<PartitionCha
110 if (poolSize == 1) { 109 if (poolSize == 1) {
111 return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(dispatcherName)); 110 return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(dispatcherName));
112 } else { 111 } else {
113 - return Executors.newWorkStealingPool(poolSize); 112 + return ThingsBoardExecutors.newWorkStealingPool(poolSize, dispatcherName);
114 } 113 }
115 } 114 }
116 115
@@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.RequestParam;
32 import org.springframework.web.bind.annotation.ResponseBody; 32 import org.springframework.web.bind.annotation.ResponseBody;
33 import org.springframework.web.bind.annotation.ResponseStatus; 33 import org.springframework.web.bind.annotation.ResponseStatus;
34 import org.springframework.web.bind.annotation.RestController; 34 import org.springframework.web.bind.annotation.RestController;
  35 +import org.thingsboard.common.util.JacksonUtil;
35 import org.thingsboard.rule.engine.api.MailService; 36 import org.thingsboard.rule.engine.api.MailService;
36 import org.thingsboard.server.common.data.EntityType; 37 import org.thingsboard.server.common.data.EntityType;
37 import org.thingsboard.server.common.data.User; 38 import org.thingsboard.server.common.data.User;
@@ -89,13 +90,14 @@ public class UserController extends BaseController { @@ -89,13 +90,14 @@ public class UserController extends BaseController {
89 try { 90 try {
90 UserId userId = new UserId(toUUID(strUserId)); 91 UserId userId = new UserId(toUUID(strUserId));
91 User user = checkUserId(userId, Operation.READ); 92 User user = checkUserId(userId, Operation.READ);
92 - if(!user.getAdditionalInfo().isNull()) {  
93 - processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), DEFAULT_DASHBOARD);  
94 - processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), HOME_DASHBOARD);  
95 - }  
96 - UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());  
97 - if(userCredentials.isEnabled()) {  
98 - addUserCredentialsEnabled((ObjectNode) user.getAdditionalInfo()); 93 + if(user.getAdditionalInfo().isObject()) {
  94 + ObjectNode additionalInfo = (ObjectNode) user.getAdditionalInfo();
  95 + processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD);
  96 + processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD);
  97 + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
  98 + if(userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) {
  99 + additionalInfo.put("userCredentialsEnabled", true);
  100 + }
99 } 101 }
100 return user; 102 return user;
101 } catch (Exception e) { 103 } catch (Exception e) {
@@ -103,14 +105,6 @@ public class UserController extends BaseController { @@ -103,14 +105,6 @@ public class UserController extends BaseController {
103 } 105 }
104 } 106 }
105 107
106 - private void addUserCredentialsEnabled(ObjectNode additionalInfo) {  
107 - if(!additionalInfo.isNull()) {  
108 - if(!additionalInfo.has("userCredentialsEnabled")) {  
109 - additionalInfo.put("userCredentialsEnabled", true);  
110 - }  
111 - }  
112 - }  
113 -  
114 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 108 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
115 @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET) 109 @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET)
116 @ResponseBody 110 @ResponseBody
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
23 import org.springframework.beans.factory.annotation.Value; 23 import org.springframework.beans.factory.annotation.Value;
24 import org.springframework.context.annotation.Lazy; 24 import org.springframework.context.annotation.Lazy;
25 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
  26 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
26 import org.thingsboard.rule.engine.api.MailService; 27 import org.thingsboard.rule.engine.api.MailService;
27 import org.thingsboard.server.common.data.ApiFeature; 28 import org.thingsboard.server.common.data.ApiFeature;
28 import org.thingsboard.server.common.data.ApiUsageRecordKey; 29 import org.thingsboard.server.common.data.ApiUsageRecordKey;
@@ -56,7 +57,7 @@ import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; @@ -56,7 +57,7 @@ import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
56 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; 57 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
57 import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto; 58 import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto;
58 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 59 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
59 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 60 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
60 import org.thingsboard.server.queue.discovery.PartitionService; 61 import org.thingsboard.server.queue.discovery.PartitionService;
61 import org.thingsboard.server.queue.discovery.TbApplicationEventListener; 62 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
62 import org.thingsboard.server.queue.scheduler.SchedulerComponent; 63 import org.thingsboard.server.queue.scheduler.SchedulerComponent;
@@ -146,7 +147,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa @@ -146,7 +147,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
146 this.scheduler = scheduler; 147 this.scheduler = scheduler;
147 this.tenantProfileCache = tenantProfileCache; 148 this.tenantProfileCache = tenantProfileCache;
148 this.mailService = mailService; 149 this.mailService = mailService;
149 - this.mailExecutor = Executors.newSingleThreadExecutor(); 150 + this.mailExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("api-usage-svc-mail"));
150 } 151 }
151 152
152 @PostConstruct 153 @PostConstruct
@@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantProfileId; @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantProfileId;
23 import org.thingsboard.server.common.msg.queue.TbCallback; 23 import org.thingsboard.server.common.msg.queue.TbCallback;
24 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
25 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 25 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
26 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 26 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
27 27
28 public interface TbApiUsageStateService extends ApplicationListener<PartitionChangeEvent> { 28 public interface TbApiUsageStateService extends ApplicationListener<PartitionChangeEvent> {
29 29
@@ -55,7 +55,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceM @@ -55,7 +55,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceM
55 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; 55 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
56 import org.thingsboard.server.queue.TbQueueConsumer; 56 import org.thingsboard.server.queue.TbQueueConsumer;
57 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 57 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
58 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 58 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
59 import org.thingsboard.server.queue.provider.TbCoreQueueFactory; 59 import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
60 import org.thingsboard.server.queue.util.TbCoreComponent; 60 import org.thingsboard.server.queue.util.TbCoreComponent;
61 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 61 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
@@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
20 import org.springframework.beans.factory.annotation.Value; 20 import org.springframework.beans.factory.annotation.Value;
21 import org.springframework.scheduling.annotation.Scheduled; 21 import org.springframework.scheduling.annotation.Scheduled;
22 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
  23 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
23 import org.thingsboard.rule.engine.api.RpcError; 24 import org.thingsboard.rule.engine.api.RpcError;
24 import org.thingsboard.server.actors.ActorSystemContext; 25 import org.thingsboard.server.actors.ActorSystemContext;
25 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
@@ -38,7 +39,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; @@ -38,7 +39,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
38 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; 39 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
39 import org.thingsboard.server.queue.TbQueueConsumer; 40 import org.thingsboard.server.queue.TbQueueConsumer;
40 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 41 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
41 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 42 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
42 import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory; 43 import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory;
43 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; 44 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
44 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; 45 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
@@ -127,7 +128,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -127,7 +128,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
127 consumers.computeIfAbsent(configuration.getName(), queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration)); 128 consumers.computeIfAbsent(configuration.getName(), queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration));
128 consumerStats.put(configuration.getName(), new TbRuleEngineConsumerStats(configuration.getName(), statsFactory)); 129 consumerStats.put(configuration.getName(), new TbRuleEngineConsumerStats(configuration.getName(), statsFactory));
129 } 130 }
130 - submitExecutor = Executors.newSingleThreadExecutor(); 131 + submitExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-rule-engine-consumer-service-submit-executor"));
131 } 132 }
132 133
133 @PreDestroy 134 @PreDestroy
@@ -160,6 +161,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -160,6 +161,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
160 161
161 private void launchConsumer(TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer, TbRuleEngineQueueConfiguration configuration, TbRuleEngineConsumerStats stats) { 162 private void launchConsumer(TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer, TbRuleEngineQueueConfiguration configuration, TbRuleEngineConsumerStats stats) {
162 consumersExecutor.execute(() -> { 163 consumersExecutor.execute(() -> {
  164 + Thread.currentThread().setName("" + Thread.currentThread().getName() + "-" + configuration.getName());
163 while (!stopped) { 165 while (!stopped) {
164 try { 166 try {
165 List<TbProtoQueueMsg<ToRuleEngineMsg>> msgs = consumer.poll(pollDuration); 167 List<TbProtoQueueMsg<ToRuleEngineMsg>> msgs = consumer.poll(pollDuration);
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 package org.thingsboard.server.service.queue; 16 package org.thingsboard.server.service.queue;
17 17
18 import org.springframework.context.ApplicationListener; 18 import org.springframework.context.ApplicationListener;
19 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 19 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
20 20
21 public interface TbCoreConsumerService extends ApplicationListener<PartitionChangeEvent> { 21 public interface TbCoreConsumerService extends ApplicationListener<PartitionChangeEvent> {
22 22
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 package org.thingsboard.server.service.queue; 16 package org.thingsboard.server.service.queue;
17 17
18 import org.springframework.context.ApplicationListener; 18 import org.springframework.context.ApplicationListener;
19 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 19 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
20 20
21 public interface TbRuleEngineConsumerService extends ApplicationListener<PartitionChangeEvent> { 21 public interface TbRuleEngineConsumerService extends ApplicationListener<PartitionChangeEvent> {
22 22
@@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; @@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
35 import org.thingsboard.server.common.msg.queue.TbCallback; 35 import org.thingsboard.server.common.msg.queue.TbCallback;
36 import org.thingsboard.server.queue.TbQueueConsumer; 36 import org.thingsboard.server.queue.TbQueueConsumer;
37 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 37 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
38 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 38 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
39 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 39 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
40 import org.thingsboard.server.queue.discovery.TbApplicationEventListener; 40 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
41 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 41 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
@@ -185,7 +185,7 @@ public class DefaultTbResourceService implements TbResourceService { @@ -185,7 +185,7 @@ public class DefaultTbResourceService implements TbResourceService {
185 instance.setId(0); 185 instance.setId(0);
186 List<LwM2mResourceObserve> resources = new ArrayList<>(); 186 List<LwM2mResourceObserve> resources = new ArrayList<>();
187 obj.resources.forEach((k, v) -> { 187 obj.resources.forEach((k, v) -> {
188 - if (!v.operations.isExecutable()) { 188 + if (v.operations.isReadable()) {
189 LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false); 189 LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
190 resources.add(lwM2MResourceObserve); 190 resources.add(lwM2MResourceObserve);
191 } 191 }
@@ -25,6 +25,7 @@ import lombok.Getter; @@ -25,6 +25,7 @@ import lombok.Getter;
25 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
26 import org.springframework.beans.factory.annotation.Value; 26 import org.springframework.beans.factory.annotation.Value;
27 import org.springframework.scheduling.annotation.Scheduled; 27 import org.springframework.scheduling.annotation.Scheduled;
  28 +import org.thingsboard.common.util.ThingsBoardExecutors;
28 import org.thingsboard.server.queue.usagestats.TbApiUsageClient; 29 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
29 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 30 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
30 31
@@ -93,7 +94,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer @@ -93,7 +94,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
93 super.init(maxRequestsTimeout); 94 super.init(maxRequestsTimeout);
94 if (useJsSandbox()) { 95 if (useJsSandbox()) {
95 sandbox = NashornSandboxes.create(); 96 sandbox = NashornSandboxes.create();
96 - monitorExecutorService = Executors.newWorkStealingPool(getMonitorThreadPoolSize()); 97 + monitorExecutorService = ThingsBoardExecutors.newWorkStealingPool(getMonitorThreadPoolSize(), "nashorn-js-monitor");
97 sandbox.setExecutor(monitorExecutorService); 98 sandbox.setExecutor(monitorExecutorService);
98 sandbox.setMaxCPUTime(getMaxCpuTime()); 99 sandbox.setMaxCPUTime(getMaxCpuTime());
99 sandbox.allowNoBraces(false); 100 sandbox.allowNoBraces(false);
@@ -54,7 +54,7 @@ import org.thingsboard.server.dao.tenant.TenantService; @@ -54,7 +54,7 @@ import org.thingsboard.server.dao.tenant.TenantService;
54 import org.thingsboard.server.dao.timeseries.TimeseriesService; 54 import org.thingsboard.server.dao.timeseries.TimeseriesService;
55 import org.thingsboard.common.util.JacksonUtil; 55 import org.thingsboard.common.util.JacksonUtil;
56 import org.thingsboard.server.gen.transport.TransportProtos; 56 import org.thingsboard.server.gen.transport.TransportProtos;
57 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 57 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
58 import org.thingsboard.server.queue.discovery.PartitionService; 58 import org.thingsboard.server.queue.discovery.PartitionService;
59 import org.thingsboard.server.queue.discovery.TbApplicationEventListener; 59 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
60 import org.thingsboard.server.queue.util.TbCoreComponent; 60 import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -18,7 +18,7 @@ package org.thingsboard.server.service.state; @@ -18,7 +18,7 @@ package org.thingsboard.server.service.state;
18 import org.springframework.context.ApplicationListener; 18 import org.springframework.context.ApplicationListener;
19 import org.thingsboard.server.common.data.Device; 19 import org.thingsboard.server.common.data.Device;
20 import org.thingsboard.server.common.data.id.DeviceId; 20 import org.thingsboard.server.common.data.id.DeviceId;
21 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 21 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
22 import org.thingsboard.server.gen.transport.TransportProtos; 22 import org.thingsboard.server.gen.transport.TransportProtos;
23 import org.thingsboard.server.common.msg.queue.TbCallback; 23 import org.thingsboard.server.common.msg.queue.TbCallback;
24 24
@@ -46,7 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdate @@ -46,7 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdate
46 import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateValueListProto; 46 import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateValueListProto;
47 import org.thingsboard.server.queue.TbQueueProducer; 47 import org.thingsboard.server.queue.TbQueueProducer;
48 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 48 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
49 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 49 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
50 import org.thingsboard.server.queue.discovery.PartitionService; 50 import org.thingsboard.server.queue.discovery.PartitionService;
51 import org.thingsboard.server.queue.discovery.TbApplicationEventListener; 51 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
52 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 52 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
@@ -20,10 +20,10 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -20,10 +20,10 @@ import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.context.annotation.Lazy; 20 import org.springframework.context.annotation.Lazy;
21 import org.springframework.context.event.EventListener; 21 import org.springframework.context.event.EventListener;
22 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
23 -import org.thingsboard.common.util.ThingsBoardThreadFactory; 23 +import org.thingsboard.common.util.ThingsBoardExecutors;
24 import org.thingsboard.server.gen.transport.TransportProtos; 24 import org.thingsboard.server.gen.transport.TransportProtos;
25 -import org.thingsboard.server.queue.discovery.ClusterTopologyChangeEvent;  
26 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 25 +import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
  26 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
27 import org.thingsboard.server.queue.discovery.PartitionService; 27 import org.thingsboard.server.queue.discovery.PartitionService;
28 import org.thingsboard.server.common.msg.queue.ServiceType; 28 import org.thingsboard.server.common.msg.queue.ServiceType;
29 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 29 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
@@ -63,7 +63,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @@ -63,7 +63,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
63 private SubscriptionManagerService subscriptionManagerService; 63 private SubscriptionManagerService subscriptionManagerService;
64 64
65 private ExecutorService subscriptionUpdateExecutor; 65 private ExecutorService subscriptionUpdateExecutor;
66 - 66 +
67 private TbApplicationEventListener<PartitionChangeEvent> partitionChangeListener = new TbApplicationEventListener<>() { 67 private TbApplicationEventListener<PartitionChangeEvent> partitionChangeListener = new TbApplicationEventListener<>() {
68 @Override 68 @Override
69 protected void onTbApplicationEvent(PartitionChangeEvent event) { 69 protected void onTbApplicationEvent(PartitionChangeEvent event) {
@@ -94,7 +94,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @@ -94,7 +94,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
94 94
95 @PostConstruct 95 @PostConstruct
96 public void initExecutor() { 96 public void initExecutor() {
97 - subscriptionUpdateExecutor = Executors.newWorkStealingPool(20); 97 + subscriptionUpdateExecutor = ThingsBoardExecutors.newWorkStealingPool(20, getClass());
98 } 98 }
99 99
100 @PreDestroy 100 @PreDestroy
@@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.TenantId;
22 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 22 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
23 import org.thingsboard.server.common.data.kv.TsKvEntry; 23 import org.thingsboard.server.common.data.kv.TsKvEntry;
24 import org.thingsboard.server.common.msg.queue.TbCallback; 24 import org.thingsboard.server.common.msg.queue.TbCallback;
25 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 25 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
26 26
27 import java.util.List; 27 import java.util.List;
28 28
@@ -15,8 +15,8 @@ @@ -15,8 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.service.subscription; 16 package org.thingsboard.server.service.subscription;
17 17
18 -import org.thingsboard.server.queue.discovery.ClusterTopologyChangeEvent;  
19 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 18 +import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
  19 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
20 import org.thingsboard.server.common.msg.queue.TbCallback; 20 import org.thingsboard.server.common.msg.queue.TbCallback;
21 import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; 21 import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
22 import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; 22 import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;
@@ -22,35 +22,18 @@ import lombok.extern.slf4j.Slf4j; @@ -22,35 +22,18 @@ import lombok.extern.slf4j.Slf4j;
22 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
23 import org.springframework.context.ApplicationListener; 23 import org.springframework.context.ApplicationListener;
24 import org.springframework.context.event.EventListener; 24 import org.springframework.context.event.EventListener;
25 -import org.springframework.stereotype.Service;  
26 import org.thingsboard.common.util.ThingsBoardThreadFactory; 25 import org.thingsboard.common.util.ThingsBoardThreadFactory;
27 -import org.thingsboard.server.common.data.id.EntityId;  
28 -import org.thingsboard.server.common.data.id.TenantId;  
29 -import org.thingsboard.server.common.data.kv.AttributeKvEntry;  
30 -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;  
31 -import org.thingsboard.server.common.data.kv.BooleanDataEntry;  
32 -import org.thingsboard.server.common.data.kv.DoubleDataEntry;  
33 -import org.thingsboard.server.common.data.kv.LongDataEntry;  
34 -import org.thingsboard.server.common.data.kv.StringDataEntry;  
35 -import org.thingsboard.server.common.data.kv.TsKvEntry;  
36 import org.thingsboard.server.common.msg.queue.ServiceType; 26 import org.thingsboard.server.common.msg.queue.ServiceType;
37 -import org.thingsboard.server.common.msg.queue.TbCallback;  
38 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 27 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
39 -import org.thingsboard.server.dao.attributes.AttributesService;  
40 -import org.thingsboard.server.dao.timeseries.TimeseriesService;  
41 -import org.thingsboard.server.gen.transport.TransportProtos;  
42 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 28 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
43 import org.thingsboard.server.queue.discovery.PartitionService; 29 import org.thingsboard.server.queue.discovery.PartitionService;
44 import org.thingsboard.server.queue.discovery.TbApplicationEventListener; 30 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
45 import org.thingsboard.server.service.queue.TbClusterService; 31 import org.thingsboard.server.service.queue.TbClusterService;
46 import org.thingsboard.server.service.subscription.SubscriptionManagerService; 32 import org.thingsboard.server.service.subscription.SubscriptionManagerService;
47 -import org.thingsboard.server.service.subscription.TbSubscriptionUtils;  
48 33
49 import javax.annotation.Nullable; 34 import javax.annotation.Nullable;
50 import javax.annotation.PostConstruct; 35 import javax.annotation.PostConstruct;
51 import javax.annotation.PreDestroy; 36 import javax.annotation.PreDestroy;
52 -import java.util.Collections;  
53 -import java.util.List;  
54 import java.util.Optional; 37 import java.util.Optional;
55 import java.util.Set; 38 import java.util.Set;
56 import java.util.concurrent.ConcurrentHashMap; 39 import java.util.concurrent.ConcurrentHashMap;
@@ -17,8 +17,7 @@ package org.thingsboard.server.service.telemetry; @@ -17,8 +17,7 @@ package org.thingsboard.server.service.telemetry;
17 17
18 import org.springframework.context.ApplicationListener; 18 import org.springframework.context.ApplicationListener;
19 import org.thingsboard.rule.engine.api.RuleEngineAlarmService; 19 import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
20 -import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;  
21 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 20 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
22 21
23 /** 22 /**
24 * Created by ashvayka on 27.03.18. 23 * Created by ashvayka on 27.03.18.
@@ -28,6 +28,8 @@ import org.springframework.beans.factory.annotation.Value; @@ -28,6 +28,8 @@ import org.springframework.beans.factory.annotation.Value;
28 import org.springframework.stereotype.Service; 28 import org.springframework.stereotype.Service;
29 import org.springframework.util.StringUtils; 29 import org.springframework.util.StringUtils;
30 import org.springframework.web.socket.CloseStatus; 30 import org.springframework.web.socket.CloseStatus;
  31 +import org.thingsboard.common.util.ThingsBoardExecutors;
  32 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
31 import org.thingsboard.server.common.data.DataConstants; 33 import org.thingsboard.server.common.data.DataConstants;
32 import org.thingsboard.server.common.data.id.CustomerId; 34 import org.thingsboard.server.common.data.id.CustomerId;
33 import org.thingsboard.server.common.data.id.EntityId; 35 import org.thingsboard.server.common.data.id.EntityId;
@@ -157,9 +159,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -157,9 +159,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
157 @PostConstruct 159 @PostConstruct
158 public void initExecutor() { 160 public void initExecutor() {
159 serviceId = serviceInfoProvider.getServiceId(); 161 serviceId = serviceInfoProvider.getServiceId();
160 - executor = Executors.newWorkStealingPool(50); 162 + executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass());
161 163
162 - pingExecutor = Executors.newSingleThreadScheduledExecutor(); 164 + pingExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("telemetry-web-socket-ping"));
163 pingExecutor.scheduleWithFixedDelay(this::sendPing, 10000, 10000, TimeUnit.MILLISECONDS); 165 pingExecutor.scheduleWithFixedDelay(this::sendPing, 10000, 10000, TimeUnit.MILLISECONDS);
164 } 166 }
165 167
@@ -16,8 +16,7 @@ @@ -16,8 +16,7 @@
16 package org.thingsboard.server.service.telemetry; 16 package org.thingsboard.server.service.telemetry;
17 17
18 import org.springframework.context.ApplicationListener; 18 import org.springframework.context.ApplicationListener;
19 -import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;  
20 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 19 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
21 20
22 /** 21 /**
23 * Created by ashvayka on 27.03.18. 22 * Created by ashvayka on 27.03.18.
@@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.ApiUsageState; @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.ApiUsageState;
31 import org.thingsboard.server.common.data.DataConstants; 31 import org.thingsboard.server.common.data.DataConstants;
32 import org.thingsboard.server.common.data.Device; 32 import org.thingsboard.server.common.data.Device;
33 import org.thingsboard.server.common.data.DeviceProfile; 33 import org.thingsboard.server.common.data.DeviceProfile;
  34 +import org.thingsboard.server.common.data.DeviceTransportType;
34 import org.thingsboard.server.common.data.EntityType; 35 import org.thingsboard.server.common.data.EntityType;
35 import org.thingsboard.server.common.data.Firmware; 36 import org.thingsboard.server.common.data.Firmware;
36 import org.thingsboard.server.common.data.FirmwareInfo; 37 import org.thingsboard.server.common.data.FirmwareInfo;
@@ -47,6 +48,8 @@ import org.thingsboard.server.common.data.id.DeviceId; @@ -47,6 +48,8 @@ import org.thingsboard.server.common.data.id.DeviceId;
47 import org.thingsboard.server.common.data.id.DeviceProfileId; 48 import org.thingsboard.server.common.data.id.DeviceProfileId;
48 import org.thingsboard.server.common.data.id.FirmwareId; 49 import org.thingsboard.server.common.data.id.FirmwareId;
49 import org.thingsboard.server.common.data.id.TenantId; 50 import org.thingsboard.server.common.data.id.TenantId;
  51 +import org.thingsboard.server.common.data.page.PageData;
  52 +import org.thingsboard.server.common.data.page.PageLink;
50 import org.thingsboard.server.common.data.relation.EntityRelation; 53 import org.thingsboard.server.common.data.relation.EntityRelation;
51 import org.thingsboard.server.common.data.security.DeviceCredentials; 54 import org.thingsboard.server.common.data.security.DeviceCredentials;
52 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 55 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
@@ -66,11 +69,15 @@ import org.thingsboard.server.dao.relation.RelationService; @@ -66,11 +69,15 @@ import org.thingsboard.server.dao.relation.RelationService;
66 import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 69 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
67 import org.thingsboard.server.gen.transport.TransportProtos; 70 import org.thingsboard.server.gen.transport.TransportProtos;
68 import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; 71 import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
  72 +import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg;
  73 +import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceRequestMsg;
69 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg; 74 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
70 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg; 75 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
71 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; 76 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
72 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; 77 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
73 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg; 78 import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg;
  79 +import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesRequestMsg;
  80 +import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesResponseMsg;
74 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; 81 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
75 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; 82 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
76 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; 83 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
@@ -93,6 +100,7 @@ import java.util.concurrent.ConcurrentHashMap; @@ -93,6 +100,7 @@ import java.util.concurrent.ConcurrentHashMap;
93 import java.util.concurrent.ConcurrentMap; 100 import java.util.concurrent.ConcurrentMap;
94 import java.util.concurrent.locks.Lock; 101 import java.util.concurrent.locks.Lock;
95 import java.util.concurrent.locks.ReentrantLock; 102 import java.util.concurrent.locks.ReentrantLock;
  103 +import java.util.stream.Collectors;
96 104
97 /** 105 /**
98 * Created by ashvayka on 05.10.18. 106 * Created by ashvayka on 05.10.18.
@@ -146,43 +154,43 @@ public class DefaultTransportApiService implements TransportApiService { @@ -146,43 +154,43 @@ public class DefaultTransportApiService implements TransportApiService {
146 @Override 154 @Override
147 public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) { 155 public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) {
148 TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue(); 156 TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue();
  157 + ListenableFuture<TransportApiResponseMsg> result = null;
  158 +
149 if (transportApiRequestMsg.hasValidateTokenRequestMsg()) { 159 if (transportApiRequestMsg.hasValidateTokenRequestMsg()) {
150 ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg(); 160 ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg();
151 - return Futures.transform(validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN),  
152 - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 161 + result = validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN);
153 } else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) { 162 } else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) {
154 TransportProtos.ValidateBasicMqttCredRequestMsg msg = transportApiRequestMsg.getValidateBasicMqttCredRequestMsg(); 163 TransportProtos.ValidateBasicMqttCredRequestMsg msg = transportApiRequestMsg.getValidateBasicMqttCredRequestMsg();
155 - return Futures.transform(validateCredentials(msg),  
156 - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 164 + result = validateCredentials(msg);
157 } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) { 165 } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) {
158 ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg(); 166 ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg();
159 - return Futures.transform(validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE),  
160 - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 167 + result = validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE);
161 } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) { 168 } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) {
162 - return Futures.transform(handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()),  
163 - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 169 + result = handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg());
164 } else if (transportApiRequestMsg.hasEntityProfileRequestMsg()) { 170 } else if (transportApiRequestMsg.hasEntityProfileRequestMsg()) {
165 - return Futures.transform(handle(transportApiRequestMsg.getEntityProfileRequestMsg()),  
166 - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 171 + result = handle(transportApiRequestMsg.getEntityProfileRequestMsg());
167 } else if (transportApiRequestMsg.hasLwM2MRequestMsg()) { 172 } else if (transportApiRequestMsg.hasLwM2MRequestMsg()) {
168 - return Futures.transform(handle(transportApiRequestMsg.getLwM2MRequestMsg()),  
169 - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 173 + result = handle(transportApiRequestMsg.getLwM2MRequestMsg());
170 } else if (transportApiRequestMsg.hasValidateDeviceLwM2MCredentialsRequestMsg()) { 174 } else if (transportApiRequestMsg.hasValidateDeviceLwM2MCredentialsRequestMsg()) {
171 ValidateDeviceLwM2MCredentialsRequestMsg msg = transportApiRequestMsg.getValidateDeviceLwM2MCredentialsRequestMsg(); 175 ValidateDeviceLwM2MCredentialsRequestMsg msg = transportApiRequestMsg.getValidateDeviceLwM2MCredentialsRequestMsg();
172 - return Futures.transform(validateCredentials(msg.getCredentialsId(), DeviceCredentialsType.LWM2M_CREDENTIALS),  
173 - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 176 + result = validateCredentials(msg.getCredentialsId(), DeviceCredentialsType.LWM2M_CREDENTIALS);
174 } else if (transportApiRequestMsg.hasProvisionDeviceRequestMsg()) { 177 } else if (transportApiRequestMsg.hasProvisionDeviceRequestMsg()) {
175 - return Futures.transform(handle(transportApiRequestMsg.getProvisionDeviceRequestMsg()),  
176 - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 178 + result = handle(transportApiRequestMsg.getProvisionDeviceRequestMsg());
177 } else if (transportApiRequestMsg.hasResourceRequestMsg()) { 179 } else if (transportApiRequestMsg.hasResourceRequestMsg()) {
178 - return Futures.transform(handle(transportApiRequestMsg.getResourceRequestMsg()),  
179 - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 180 + result = handle(transportApiRequestMsg.getResourceRequestMsg());
  181 + } else if (transportApiRequestMsg.hasSnmpDevicesRequestMsg()) {
  182 + result = handle(transportApiRequestMsg.getSnmpDevicesRequestMsg());
  183 + } else if (transportApiRequestMsg.hasDeviceRequestMsg()) {
  184 + result = handle(transportApiRequestMsg.getDeviceRequestMsg());
  185 + } else if (transportApiRequestMsg.hasDeviceCredentialsRequestMsg()) {
  186 + result = handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg());
180 } else if (transportApiRequestMsg.hasFirmwareRequestMsg()) { 187 } else if (transportApiRequestMsg.hasFirmwareRequestMsg()) {
181 - return Futures.transform(handle(transportApiRequestMsg.getFirmwareRequestMsg()),  
182 - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 188 + result = handle(transportApiRequestMsg.getFirmwareRequestMsg());
183 } 189 }
184 - return Futures.transform(getEmptyTransportApiResponseFuture(),  
185 - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 190 +
  191 + return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture),
  192 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()),
  193 + MoreExecutors.directExecutor());
186 } 194 }
187 195
188 private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) { 196 private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) {
@@ -376,6 +384,39 @@ public class DefaultTransportApiService implements TransportApiService { @@ -376,6 +384,39 @@ public class DefaultTransportApiService implements TransportApiService {
376 return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build()); 384 return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build());
377 } 385 }
378 386
  387 + private ListenableFuture<TransportApiResponseMsg> handle(GetDeviceRequestMsg requestMsg) {
  388 + DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
  389 + Device device = deviceService.findDeviceById(TenantId.SYS_TENANT_ID, deviceId);
  390 +
  391 + TransportApiResponseMsg responseMsg;
  392 + if (device != null) {
  393 + UUID deviceProfileId = device.getDeviceProfileId().getId();
  394 + responseMsg = TransportApiResponseMsg.newBuilder()
  395 + .setDeviceResponseMsg(TransportProtos.GetDeviceResponseMsg.newBuilder()
  396 + .setDeviceProfileIdMSB(deviceProfileId.getMostSignificantBits())
  397 + .setDeviceProfileIdLSB(deviceProfileId.getLeastSignificantBits())
  398 + .setDeviceTransportConfiguration(ByteString.copyFrom(
  399 + dataDecodingEncodingService.encode(device.getDeviceData().getTransportConfiguration())
  400 + )))
  401 + .build();
  402 + } else {
  403 + responseMsg = TransportApiResponseMsg.getDefaultInstance();
  404 + }
  405 +
  406 + return Futures.immediateFuture(responseMsg);
  407 + }
  408 +
  409 + private ListenableFuture<TransportApiResponseMsg> handle(GetDeviceCredentialsRequestMsg requestMsg) {
  410 + DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
  411 + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, deviceId);
  412 +
  413 + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder()
  414 + .setDeviceCredentialsResponseMsg(TransportProtos.GetDeviceCredentialsResponseMsg.newBuilder()
  415 + .setDeviceCredentialsData(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceCredentials))))
  416 + .build());
  417 + }
  418 +
  419 +
379 private ListenableFuture<TransportApiResponseMsg> handle(GetResourceRequestMsg requestMsg) { 420 private ListenableFuture<TransportApiResponseMsg> handle(GetResourceRequestMsg requestMsg) {
380 TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); 421 TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
381 ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType()); 422 ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType());
@@ -394,6 +435,22 @@ public class DefaultTransportApiService implements TransportApiService { @@ -394,6 +435,22 @@ public class DefaultTransportApiService implements TransportApiService {
394 return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setResourceResponseMsg(builder).build()); 435 return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setResourceResponseMsg(builder).build());
395 } 436 }
396 437
  438 + private ListenableFuture<TransportApiResponseMsg> handle(GetSnmpDevicesRequestMsg requestMsg) {
  439 + PageLink pageLink = new PageLink(requestMsg.getPageSize(), requestMsg.getPage());
  440 + PageData<UUID> result = deviceService.findDevicesIdsByDeviceProfileTransportType(DeviceTransportType.SNMP, pageLink);
  441 +
  442 + GetSnmpDevicesResponseMsg responseMsg = GetSnmpDevicesResponseMsg.newBuilder()
  443 + .addAllIds(result.getData().stream()
  444 + .map(UUID::toString)
  445 + .collect(Collectors.toList()))
  446 + .setHasNextPage(result.hasNext())
  447 + .build();
  448 +
  449 + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder()
  450 + .setSnmpDevicesResponseMsg(responseMsg)
  451 + .build());
  452 + }
  453 +
397 private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) { 454 private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) {
398 return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, deviceId), device -> { 455 return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, deviceId), device -> {
399 if (device == null) { 456 if (device == null) {
@@ -21,6 +21,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -21,6 +21,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
21 import org.springframework.context.event.EventListener; 21 import org.springframework.context.event.EventListener;
22 import org.springframework.core.annotation.Order; 22 import org.springframework.core.annotation.Order;
23 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
  24 +import org.thingsboard.common.util.ThingsBoardExecutors;
24 import org.thingsboard.server.common.stats.MessagesStats; 25 import org.thingsboard.server.common.stats.MessagesStats;
25 import org.thingsboard.server.common.stats.StatsFactory; 26 import org.thingsboard.server.common.stats.StatsFactory;
26 import org.thingsboard.server.common.stats.StatsType; 27 import org.thingsboard.server.common.stats.StatsType;
@@ -70,7 +71,7 @@ public class TbCoreTransportApiService { @@ -70,7 +71,7 @@ public class TbCoreTransportApiService {
70 71
71 @PostConstruct 72 @PostConstruct
72 public void init() { 73 public void init() {
73 - this.transportCallbackExecutor = Executors.newWorkStealingPool(maxCallbackThreads); 74 + this.transportCallbackExecutor = ThingsBoardExecutors.newWorkStealingPool(maxCallbackThreads, getClass());
74 TbQueueProducer<TbProtoQueueMsg<TransportApiResponseMsg>> producer = tbCoreQueueFactory.createTransportApiResponseProducer(); 75 TbQueueProducer<TbProtoQueueMsg<TransportApiResponseMsg>> producer = tbCoreQueueFactory.createTransportApiResponseProducer();
75 TbQueueConsumer<TbProtoQueueMsg<TransportApiRequestMsg>> consumer = tbCoreQueueFactory.createTransportApiRequestConsumer(); 76 TbQueueConsumer<TbProtoQueueMsg<TransportApiRequestMsg>> consumer = tbCoreQueueFactory.createTransportApiRequestConsumer();
76 77
@@ -26,6 +26,7 @@ @@ -26,6 +26,7 @@
26 </appender> 26 </appender>
27 27
28 <logger name="org.thingsboard.server" level="INFO" /> 28 <logger name="org.thingsboard.server" level="INFO" />
  29 + <logger name="org.thingsboard.server.transport.snmp" level="TRACE" />
29 30
30 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />--> 31 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />-->
31 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />--> 32 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />-->
@@ -621,9 +621,9 @@ transport: @@ -621,9 +621,9 @@ transport:
621 key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}" 621 key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}"
622 # Key alias 622 # Key alias
623 key_alias: "${COAP_DTLS_KEY_ALIAS:serveralias}" 623 key_alias: "${COAP_DTLS_KEY_ALIAS:serveralias}"
624 - # Skip certificate validity check for client certificates.  
625 - skip_validity_check_for_client_cert: "${COAP_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"  
626 x509: 624 x509:
  625 + # Skip certificate validity check for client certificates.
  626 + skip_validity_check_for_client_cert: "${TB_COAP_X509_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
627 dtls_session_inactivity_timeout: "${TB_COAP_X509_DTLS_SESSION_INACTIVITY_TIMEOUT:86400000}" 627 dtls_session_inactivity_timeout: "${TB_COAP_X509_DTLS_SESSION_INACTIVITY_TIMEOUT:86400000}"
628 dtls_session_report_timeout: "${TB_COAP_X509_DTLS_SESSION_REPORT_TIMEOUT:1800000}" 628 dtls_session_report_timeout: "${TB_COAP_X509_DTLS_SESSION_REPORT_TIMEOUT:1800000}"
629 # Local LwM2M transport parameters 629 # Local LwM2M transport parameters
@@ -686,6 +686,13 @@ transport: @@ -686,6 +686,13 @@ transport:
686 alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}" 686 alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}"
687 # Use redis for Security and Registration stores 687 # Use redis for Security and Registration stores
688 redis.enabled: "${LWM2M_REDIS_ENABLED:false}" 688 redis.enabled: "${LWM2M_REDIS_ENABLED:false}"
  689 + snmp:
  690 + enabled: "${SNMP_ENABLED:true}"
  691 + response_processing:
  692 + # parallelism level for executor (workStealingPool) that is responsible for handling responses from SNMP devices
  693 + parallelism_level: "${SNMP_RESPONSE_PROCESSING_PARALLELISM_LEVEL:20}"
  694 + # to configure SNMP to work over UDP or TCP
  695 + underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}"
689 696
690 # Edges parameters 697 # Edges parameters
691 edges: 698 edges:
@@ -16,10 +16,10 @@ @@ -16,10 +16,10 @@
16 package org.thingsboard.server.service.queue; 16 package org.thingsboard.server.service.queue;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
19 import org.junit.Assert; 20 import org.junit.Assert;
20 import org.junit.Test; 21 import org.junit.Test;
21 import org.junit.runner.RunWith; 22 import org.junit.runner.RunWith;
22 -import org.mockito.Mockito;  
23 import org.mockito.junit.MockitoJUnitRunner; 23 import org.mockito.junit.MockitoJUnitRunner;
24 import org.thingsboard.server.gen.transport.TransportProtos; 24 import org.thingsboard.server.gen.transport.TransportProtos;
25 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 25 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
@@ -28,39 +28,74 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrateg @@ -28,39 +28,74 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrateg
28 import java.util.UUID; 28 import java.util.UUID;
29 import java.util.concurrent.ConcurrentHashMap; 29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.ConcurrentMap; 30 import java.util.concurrent.ConcurrentMap;
  31 +import java.util.concurrent.CountDownLatch;
31 import java.util.concurrent.ExecutorService; 32 import java.util.concurrent.ExecutorService;
32 import java.util.concurrent.Executors; 33 import java.util.concurrent.Executors;
33 import java.util.concurrent.TimeUnit; 34 import java.util.concurrent.TimeUnit;
34 35
  36 +import static org.junit.Assert.assertTrue;
  37 +import static org.mockito.ArgumentMatchers.any;
35 import static org.mockito.Mockito.mock; 38 import static org.mockito.Mockito.mock;
  39 +import static org.mockito.Mockito.times;
  40 +import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when; 41 import static org.mockito.Mockito.when;
37 42
38 @Slf4j 43 @Slf4j
39 @RunWith(MockitoJUnitRunner.class) 44 @RunWith(MockitoJUnitRunner.class)
40 public class TbMsgPackProcessingContextTest { 45 public class TbMsgPackProcessingContextTest {
41 46
  47 + public static final int TIMEOUT = 10;
  48 + ExecutorService executorService;
  49 +
  50 + @After
  51 + public void tearDown() {
  52 + if (executorService != null) {
  53 + executorService.shutdownNow();
  54 + }
  55 + }
  56 +
42 @Test 57 @Test
43 public void testHighConcurrencyCase() throws InterruptedException { 58 public void testHighConcurrencyCase() throws InterruptedException {
44 - TbRuleEngineSubmitStrategy strategyMock = mock(TbRuleEngineSubmitStrategy.class); 59 + //log.warn("preparing the test...");
45 int msgCount = 1000; 60 int msgCount = 1000;
46 int parallelCount = 5; 61 int parallelCount = 5;
47 - ExecutorService executorService = Executors.newFixedThreadPool(parallelCount);  
48 - try {  
49 - ConcurrentMap<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> messages = new ConcurrentHashMap<>();  
50 - for (int i = 0; i < msgCount; i++) {  
51 - messages.put(UUID.randomUUID(), new TbProtoQueueMsg<>(UUID.randomUUID(), null));  
52 - }  
53 - when(strategyMock.getPendingMap()).thenReturn(messages);  
54 - TbMsgPackProcessingContext context = new TbMsgPackProcessingContext("Main", strategyMock);  
55 - for (UUID uuid : messages.keySet()) {  
56 - for (int i = 0; i < parallelCount; i++) {  
57 - executorService.submit(() -> context.onSuccess(uuid));  
58 - } 62 + executorService = Executors.newFixedThreadPool(parallelCount);
  63 +
  64 + ConcurrentMap<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> messages = new ConcurrentHashMap<>(msgCount);
  65 + for (int i = 0; i < msgCount; i++) {
  66 + messages.put(UUID.randomUUID(), new TbProtoQueueMsg<>(UUID.randomUUID(), null));
  67 + }
  68 + TbRuleEngineSubmitStrategy strategyMock = mock(TbRuleEngineSubmitStrategy.class);
  69 + when(strategyMock.getPendingMap()).thenReturn(messages);
  70 +
  71 + TbMsgPackProcessingContext context = new TbMsgPackProcessingContext("Main", strategyMock);
  72 + for (UUID uuid : messages.keySet()) {
  73 + final CountDownLatch readyLatch = new CountDownLatch(parallelCount);
  74 + final CountDownLatch startLatch = new CountDownLatch(1);
  75 + final CountDownLatch finishLatch = new CountDownLatch(parallelCount);
  76 + for (int i = 0; i < parallelCount; i++) {
  77 + //final String taskName = "" + uuid + " " + i;
  78 + executorService.submit(() -> {
  79 + //log.warn("ready {}", taskName);
  80 + readyLatch.countDown();
  81 + try {
  82 + startLatch.await();
  83 + } catch (InterruptedException e) {
  84 + Assert.fail("failed to await");
  85 + }
  86 + //log.warn("go {}", taskName);
  87 +
  88 + context.onSuccess(uuid);
  89 +
  90 + finishLatch.countDown();
  91 + });
59 } 92 }
60 - Assert.assertTrue(context.await(10, TimeUnit.SECONDS));  
61 - Mockito.verify(strategyMock, Mockito.times(msgCount)).onSuccess(Mockito.any(UUID.class));  
62 - } finally {  
63 - executorService.shutdownNow(); 93 + assertTrue(readyLatch.await(TIMEOUT, TimeUnit.SECONDS));
  94 + Thread.yield();
  95 + startLatch.countDown(); //run all-at-once submitted tasks
  96 + assertTrue(finishLatch.await(TIMEOUT, TimeUnit.SECONDS));
64 } 97 }
  98 + assertTrue(context.await(TIMEOUT, TimeUnit.SECONDS));
  99 + verify(strategyMock, times(msgCount)).onSuccess(any(UUID.class));
65 } 100 }
66 } 101 }
@@ -19,11 +19,10 @@ import lombok.Getter; @@ -19,11 +19,10 @@ import lombok.Getter;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 import org.springframework.beans.factory.annotation.Autowired; 20 import org.springframework.beans.factory.annotation.Autowired;
21 import org.springframework.beans.factory.annotation.Value; 21 import org.springframework.beans.factory.annotation.Value;
22 -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;  
23 import org.springframework.stereotype.Component; 22 import org.springframework.stereotype.Component;
24 23
25 @Slf4j 24 @Slf4j
26 -@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") 25 +@TbCoapServerComponent
27 @Component 26 @Component
28 public class CoapServerContext { 27 public class CoapServerContext {
29 28
@@ -23,7 +23,6 @@ import org.eclipse.californium.core.server.resources.Resource; @@ -23,7 +23,6 @@ import org.eclipse.californium.core.server.resources.Resource;
23 import org.eclipse.californium.scandium.DTLSConnector; 23 import org.eclipse.californium.scandium.DTLSConnector;
24 import org.eclipse.californium.scandium.config.DtlsConnectorConfig; 24 import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
25 import org.springframework.beans.factory.annotation.Autowired; 25 import org.springframework.beans.factory.annotation.Autowired;
26 -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;  
27 import org.springframework.stereotype.Component; 26 import org.springframework.stereotype.Component;
28 27
29 import javax.annotation.PostConstruct; 28 import javax.annotation.PostConstruct;
@@ -39,7 +38,7 @@ import java.util.concurrent.TimeUnit; @@ -39,7 +38,7 @@ import java.util.concurrent.TimeUnit;
39 38
40 @Slf4j 39 @Slf4j
41 @Component 40 @Component
42 -@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") 41 +@TbCoapServerComponent
43 public class DefaultCoapServerService implements CoapServerService { 42 public class DefaultCoapServerService implements CoapServerService {
44 43
45 @Autowired 44 @Autowired
@@ -39,7 +39,6 @@ import java.util.Collections; @@ -39,7 +39,6 @@ import java.util.Collections;
39 import java.util.Optional; 39 import java.util.Optional;
40 40
41 @Slf4j 41 @Slf4j
42 -@ConditionalOnExpression("'${transport.coap.enabled}'=='true'")  
43 @ConditionalOnProperty(prefix = "transport.coap.dtls", value = "enabled", havingValue = "true", matchIfMissing = false) 42 @ConditionalOnProperty(prefix = "transport.coap.dtls", value = "enabled", havingValue = "true", matchIfMissing = false)
44 @Component 43 @Component
45 public class TbCoapDtlsSettings { 44 public class TbCoapDtlsSettings {
@@ -50,7 +49,7 @@ public class TbCoapDtlsSettings { @@ -50,7 +49,7 @@ public class TbCoapDtlsSettings {
50 @Value("${transport.coap.dtls.bind_port}") 49 @Value("${transport.coap.dtls.bind_port}")
51 private Integer port; 50 private Integer port;
52 51
53 - @Value("${transport.coap.dtls.mode}") 52 + @Value("${transport.coap.dtls.mode:NO_AUTH}")
54 private String mode; 53 private String mode;
55 54
56 @Value("${transport.coap.dtls.key_store}") 55 @Value("${transport.coap.dtls.key_store}")
@@ -65,13 +64,13 @@ public class TbCoapDtlsSettings { @@ -65,13 +64,13 @@ public class TbCoapDtlsSettings {
65 @Value("${transport.coap.dtls.key_alias}") 64 @Value("${transport.coap.dtls.key_alias}")
66 private String keyAlias; 65 private String keyAlias;
67 66
68 - @Value("${transport.coap.dtls.skip_validity_check_for_client_cert}") 67 + @Value("${transport.coap.dtls.x509.skip_validity_check_for_client_cert:false}")
69 private boolean skipValidityCheckForClientCert; 68 private boolean skipValidityCheckForClientCert;
70 69
71 - @Value("${transport.coap.dtls.x509.dtls_session_inactivity_timeout}") 70 + @Value("${transport.coap.dtls.x509.dtls_session_inactivity_timeout:86400000}")
72 private long dtlsSessionInactivityTimeout; 71 private long dtlsSessionInactivityTimeout;
73 72
74 - @Value("${transport.coap.dtls.x509.dtls_session_report_timeout}") 73 + @Value("${transport.coap.dtls.x509.dtls_session_report_timeout:1800000}")
75 private long dtlsSessionReportTimeout; 74 private long dtlsSessionReportTimeout;
76 75
77 @Autowired 76 @Autowired
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.coapserver;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  19 +
  20 +import java.lang.annotation.Retention;
  21 +import java.lang.annotation.RetentionPolicy;
  22 +
  23 +@Retention(RetentionPolicy.RUNTIME)
  24 +@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')")
  25 +public @interface TbCoapServerComponent {
  26 +}
@@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
19 import org.thingsboard.server.common.data.Device; 19 import org.thingsboard.server.common.data.Device;
20 import org.thingsboard.server.common.data.DeviceInfo; 20 import org.thingsboard.server.common.data.DeviceInfo;
21 import org.thingsboard.server.common.data.DeviceProfile; 21 import org.thingsboard.server.common.data.DeviceProfile;
  22 +import org.thingsboard.server.common.data.DeviceTransportType;
22 import org.thingsboard.server.common.data.EntitySubtype; 23 import org.thingsboard.server.common.data.EntitySubtype;
23 import org.thingsboard.server.common.data.device.DeviceSearchQuery; 24 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
24 import org.thingsboard.server.common.data.id.CustomerId; 25 import org.thingsboard.server.common.data.id.CustomerId;
@@ -32,6 +33,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; @@ -32,6 +33,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
32 import org.thingsboard.server.dao.device.provision.ProvisionRequest; 33 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
33 34
34 import java.util.List; 35 import java.util.List;
  36 +import java.util.UUID;
35 37
36 public interface DeviceService { 38 public interface DeviceService {
37 39
@@ -95,6 +97,8 @@ public interface DeviceService { @@ -95,6 +97,8 @@ public interface DeviceService {
95 97
96 Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile); 98 Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile);
97 99
  100 + PageData<UUID> findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType, PageLink pageLink);
  101 +
98 Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); 102 Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
99 103
100 Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); 104 Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
@@ -87,6 +87,10 @@ @@ -87,6 +87,10 @@
87 <groupId>org.thingsboard</groupId> 87 <groupId>org.thingsboard</groupId>
88 <artifactId>protobuf-dynamic</artifactId> 88 <artifactId>protobuf-dynamic</artifactId>
89 </dependency> 89 </dependency>
  90 + <dependency>
  91 + <groupId>org.apache.commons</groupId>
  92 + <artifactId>commons-lang3</artifactId>
  93 + </dependency>
90 </dependencies> 94 </dependencies>
91 95
92 <build> 96 <build>
@@ -93,6 +93,25 @@ public class DataConstants { @@ -93,6 +93,25 @@ public class DataConstants {
93 public static final String USERNAME = "username"; 93 public static final String USERNAME = "username";
94 public static final String PASSWORD = "password"; 94 public static final String PASSWORD = "password";
95 95
  96 +//<<<<<<< HEAD
  97 +//=======
  98 +// //firmware
  99 +// //telemetry
  100 +// public static final String CURRENT_FIRMWARE_TITLE = "current_fw_title";
  101 +// public static final String CURRENT_FIRMWARE_VERSION = "current_fw_version";
  102 +// public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";
  103 +// public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";
  104 +// public static final String TARGET_FIRMWARE_TS = "target_fw_ts";
  105 +// public static final String FIRMWARE_STATE = "fw_state";
  106 +//
  107 +// //attributes
  108 +// //telemetry
  109 +// public static final String FIRMWARE_TITLE = "fw_title";
  110 +// public static final String FIRMWARE_VERSION = "fw_version";
  111 +// public static final String FIRMWARE_SIZE = "fw_size";
  112 +// public static final String FIRMWARE_CHECKSUM = "fw_checksum";
  113 +// public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm";
  114 +//>>>>>>> origin/master
96 public static final String EDGE_MSG_SOURCE = "edge"; 115 public static final String EDGE_MSG_SOURCE = "edge";
97 public static final String MSG_SOURCE_KEY = "source"; 116 public static final String MSG_SOURCE_KEY = "source";
98 117
@@ -18,6 +18,7 @@ package org.thingsboard.server.common.data; @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data;
18 public enum DeviceTransportType { 18 public enum DeviceTransportType {
19 DEFAULT, 19 DEFAULT,
20 MQTT, 20 MQTT,
  21 + COAP,
21 LWM2M, 22 LWM2M,
22 - COAP 23 + SNMP
23 } 24 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data;
  17 +
  18 +public interface TbTransportService {
  19 + String getName();
  20 +}
@@ -21,6 +21,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; @@ -21,6 +21,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
21 import com.fasterxml.jackson.annotation.JsonTypeInfo; 21 import com.fasterxml.jackson.annotation.JsonTypeInfo;
22 import org.thingsboard.server.common.data.DeviceTransportType; 22 import org.thingsboard.server.common.data.DeviceTransportType;
23 23
  24 +import java.io.Serializable;
  25 +
24 @JsonIgnoreProperties(ignoreUnknown = true) 26 @JsonIgnoreProperties(ignoreUnknown = true)
25 @JsonTypeInfo( 27 @JsonTypeInfo(
26 use = JsonTypeInfo.Id.NAME, 28 use = JsonTypeInfo.Id.NAME,
@@ -29,11 +31,14 @@ import org.thingsboard.server.common.data.DeviceTransportType; @@ -29,11 +31,14 @@ import org.thingsboard.server.common.data.DeviceTransportType;
29 @JsonSubTypes({ 31 @JsonSubTypes({
30 @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"), 32 @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"),
31 @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"), 33 @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"),
  34 + @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP"),
32 @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"), 35 @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"),
33 - @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP")})  
34 -public interface DeviceTransportConfiguration {  
35 - 36 + @JsonSubTypes.Type(value = SnmpDeviceTransportConfiguration.class, name = "SNMP")})
  37 +public interface DeviceTransportConfiguration extends Serializable {
36 @JsonIgnore 38 @JsonIgnore
37 DeviceTransportType getType(); 39 DeviceTransportType getType();
38 40
  41 + default void validate() {
  42 + }
  43 +
39 } 44 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.data;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.Data;
  20 +import lombok.ToString;
  21 +import org.apache.commons.lang3.ObjectUtils;
  22 +import org.apache.commons.lang3.StringUtils;
  23 +import org.thingsboard.server.common.data.DeviceTransportType;
  24 +import org.thingsboard.server.common.data.transport.snmp.AuthenticationProtocol;
  25 +import org.thingsboard.server.common.data.transport.snmp.PrivacyProtocol;
  26 +import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
  27 +
  28 +import java.util.Objects;
  29 +
  30 +@Data
  31 +@ToString(of = {"host", "port", "protocolVersion"})
  32 +public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration {
  33 + private String host;
  34 + private Integer port;
  35 + private SnmpProtocolVersion protocolVersion;
  36 +
  37 + /*
  38 + * For SNMP v1 and v2c
  39 + * */
  40 + private String community;
  41 +
  42 + /*
  43 + * For SNMP v3
  44 + * */
  45 + private String username;
  46 + private String securityName;
  47 + private String contextName;
  48 + private AuthenticationProtocol authenticationProtocol;
  49 + private String authenticationPassphrase;
  50 + private PrivacyProtocol privacyProtocol;
  51 + private String privacyPassphrase;
  52 + private String engineId;
  53 +
  54 + @Override
  55 + public DeviceTransportType getType() {
  56 + return DeviceTransportType.SNMP;
  57 + }
  58 +
  59 + @Override
  60 + public void validate() {
  61 + if (!isValid()) {
  62 + throw new IllegalArgumentException("Transport configuration is not valid");
  63 + }
  64 + }
  65 +
  66 + @JsonIgnore
  67 + private boolean isValid() {
  68 + boolean isValid = StringUtils.isNotBlank(host) && port != null && protocolVersion != null;
  69 + if (isValid) {
  70 + switch (protocolVersion) {
  71 + case V1:
  72 + case V2C:
  73 + isValid = StringUtils.isNotEmpty(community);
  74 + break;
  75 + case V3:
  76 + isValid = StringUtils.isNotBlank(username) && StringUtils.isNotBlank(securityName)
  77 + && contextName != null && authenticationProtocol != null
  78 + && StringUtils.isNotBlank(authenticationPassphrase)
  79 + && privacyProtocol != null && privacyPassphrase != null && engineId != null;
  80 + break;
  81 + }
  82 + }
  83 + return isValid;
  84 + }
  85 +}
@@ -29,13 +29,18 @@ import java.io.Serializable; @@ -29,13 +29,18 @@ import java.io.Serializable;
29 include = JsonTypeInfo.As.PROPERTY, 29 include = JsonTypeInfo.As.PROPERTY,
30 property = "type") 30 property = "type")
31 @JsonSubTypes({ 31 @JsonSubTypes({
32 - @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),  
33 - @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),  
34 - @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"),  
35 - @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP")}) 32 + @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),
  33 + @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
  34 + @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"),
  35 + @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP"),
  36 + @JsonSubTypes.Type(value = SnmpDeviceProfileTransportConfiguration.class, name = "SNMP")
  37 +})
36 public interface DeviceProfileTransportConfiguration extends Serializable { 38 public interface DeviceProfileTransportConfiguration extends Serializable {
37 39
38 @JsonIgnore 40 @JsonIgnore
39 DeviceTransportType getType(); 41 DeviceTransportType getType();
40 42
  43 + default void validate() {
  44 + }
  45 +
41 } 46 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.profile;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.DeviceTransportType;
  21 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  22 +import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
  23 +
  24 +import java.util.List;
  25 +
  26 +@Data
  27 +public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
  28 + private Integer timeoutMs;
  29 + private Integer retries;
  30 + private List<SnmpCommunicationConfig> communicationConfigs;
  31 +
  32 + @Override
  33 + public DeviceTransportType getType() {
  34 + return DeviceTransportType.SNMP;
  35 + }
  36 +
  37 + @Override
  38 + public void validate() {
  39 + if (!isValid()) {
  40 + throw new IllegalArgumentException("SNMP transport configuration is not valid");
  41 + }
  42 + }
  43 +
  44 + @JsonIgnore
  45 + private boolean isValid() {
  46 + return timeoutMs != null && timeoutMs >= 0 && retries != null && retries >= 0
  47 + && communicationConfigs != null
  48 + && communicationConfigs.stream().allMatch(config -> config != null && config.isValid())
  49 + && communicationConfigs.stream().flatMap(config -> config.getAllMappings().stream()).map(SnmpMapping::getOid)
  50 + .distinct().count() == communicationConfigs.stream().mapToInt(config -> config.getAllMappings().size()).sum();
  51 + }
  52 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +import java.util.Arrays;
  19 +import java.util.Optional;
  20 +
  21 +public enum AuthenticationProtocol {
  22 + SHA_1("1.3.6.1.6.3.10.1.1.3"),
  23 + SHA_224("1.3.6.1.6.3.10.1.1.4"),
  24 + SHA_256("1.3.6.1.6.3.10.1.1.5"),
  25 + SHA_384("1.3.6.1.6.3.10.1.1.6"),
  26 + SHA_512("1.3.6.1.6.3.10.1.1.7"),
  27 + MD5("1.3.6.1.6.3.10.1.1.2");
  28 +
  29 + // oids taken from org.snmp4j.security.SecurityProtocol implementations
  30 + private final String oid;
  31 +
  32 + AuthenticationProtocol(String oid) {
  33 + this.oid = oid;
  34 + }
  35 +
  36 + public String getOid() {
  37 + return oid;
  38 + }
  39 +
  40 + public static Optional<AuthenticationProtocol> forName(String name) {
  41 + return Arrays.stream(values())
  42 + .filter(protocol -> protocol.name().equalsIgnoreCase(name))
  43 + .findFirst();
  44 + }
  45 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +import java.util.Arrays;
  19 +import java.util.Optional;
  20 +
  21 +public enum PrivacyProtocol {
  22 + DES("1.3.6.1.6.3.10.1.2.2"),
  23 + AES_128("1.3.6.1.6.3.10.1.2.4"),
  24 + AES_192("1.3.6.1.4.1.4976.2.2.1.1.1"),
  25 + AES_256("1.3.6.1.4.1.4976.2.2.1.1.2");
  26 +
  27 + // oids taken from org.snmp4j.security.SecurityProtocol implementations
  28 + private final String oid;
  29 +
  30 + PrivacyProtocol(String oid) {
  31 + this.oid = oid;
  32 + }
  33 +
  34 + public String getOid() {
  35 + return oid;
  36 + }
  37 +
  38 + public static Optional<PrivacyProtocol> forName(String name) {
  39 + return Arrays.stream(values())
  40 + .filter(protocol -> protocol.name().equalsIgnoreCase(name))
  41 + .findFirst();
  42 + }
  43 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +public enum SnmpCommunicationSpec {
  19 + TELEMETRY_QUERYING,
  20 +
  21 + CLIENT_ATTRIBUTES_QUERYING,
  22 + SHARED_ATTRIBUTES_SETTING,
  23 +
  24 + TO_DEVICE_RPC_REQUEST,
  25 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.AllArgsConstructor;
  20 +import lombok.Data;
  21 +import lombok.NoArgsConstructor;
  22 +import org.apache.commons.lang3.StringUtils;
  23 +import org.thingsboard.server.common.data.kv.DataType;
  24 +
  25 +import java.util.regex.Pattern;
  26 +
  27 +@Data
  28 +@AllArgsConstructor
  29 +@NoArgsConstructor
  30 +public class SnmpMapping {
  31 + private String oid;
  32 + private String key;
  33 + private DataType dataType;
  34 +
  35 + private static final Pattern OID_PATTERN = Pattern.compile("^\\.?([0-2])((\\.0)|(\\.[1-9][0-9]*))*$");
  36 +
  37 + @JsonIgnore
  38 + public boolean isValid() {
  39 + return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() && StringUtils.isNotBlank(key);
  40 + }
  41 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +public enum SnmpMethod {
  19 + GET(-96),
  20 + SET(-93);
  21 +
  22 + // codes taken from org.snmp4j.PDU class
  23 + private final int code;
  24 +
  25 + SnmpMethod(int code) {
  26 + this.code = code;
  27 + }
  28 +
  29 + public int getCode() {
  30 + return code;
  31 + }
  32 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp;
  17 +
  18 +public enum SnmpProtocolVersion {
  19 + V1(0),
  20 + V2C(1),
  21 + V3(3);
  22 +
  23 + private final int code;
  24 +
  25 + SnmpProtocolVersion(int code) {
  26 + this.code = code;
  27 + }
  28 +
  29 + public int getCode() {
  30 + return code;
  31 + }
  32 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  20 +
  21 +import java.util.List;
  22 +
  23 +@Data
  24 +public abstract class MultipleMappingsSnmpCommunicationConfig implements SnmpCommunicationConfig {
  25 + protected List<SnmpMapping> mappings;
  26 +
  27 + @Override
  28 + public boolean isValid() {
  29 + return mappings != null && !mappings.isEmpty() && mappings.stream().allMatch(mapping -> mapping != null && mapping.isValid());
  30 + }
  31 +
  32 + @Override
  33 + public List<SnmpMapping> getAllMappings() {
  34 + return mappings;
  35 + }
  36 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config;
  17 +
  18 +import lombok.Data;
  19 +import lombok.EqualsAndHashCode;
  20 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  21 +
  22 +@EqualsAndHashCode(callSuper = true)
  23 +@Data
  24 +public abstract class RepeatingQueryingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
  25 + private Long queryingFrequencyMs;
  26 +
  27 + @Override
  28 + public SnmpMethod getMethod() {
  29 + return SnmpMethod.GET;
  30 + }
  31 +
  32 + @Override
  33 + public boolean isValid() {
  34 + return queryingFrequencyMs != null && queryingFrequencyMs > 0 && super.isValid();
  35 + }
  36 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  20 +import com.fasterxml.jackson.annotation.JsonSubTypes;
  21 +import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
  22 +import com.fasterxml.jackson.annotation.JsonTypeInfo;
  23 +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  24 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  25 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  26 +import org.thingsboard.server.common.data.transport.snmp.config.impl.ClientAttributesQueryingSnmpCommunicationConfig;
  27 +import org.thingsboard.server.common.data.transport.snmp.config.impl.SharedAttributesSettingSnmpCommunicationConfig;
  28 +import org.thingsboard.server.common.data.transport.snmp.config.impl.TelemetryQueryingSnmpCommunicationConfig;
  29 +import org.thingsboard.server.common.data.transport.snmp.config.impl.ToDeviceRpcRequestSnmpCommunicationConfig;
  30 +
  31 +import java.util.List;
  32 +
  33 +@JsonIgnoreProperties(ignoreUnknown = true)
  34 +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "spec")
  35 +@JsonSubTypes({
  36 + @Type(value = TelemetryQueryingSnmpCommunicationConfig.class, name = "TELEMETRY_QUERYING"),
  37 + @Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"),
  38 + @Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING"),
  39 + @Type(value = ToDeviceRpcRequestSnmpCommunicationConfig.class, name = "TO_DEVICE_RPC_REQUEST")
  40 +})
  41 +public interface SnmpCommunicationConfig {
  42 +
  43 + SnmpCommunicationSpec getSpec();
  44 +
  45 + @JsonIgnore
  46 + default SnmpMethod getMethod() {
  47 + return null;
  48 + }
  49 +
  50 + @JsonIgnore
  51 + List<SnmpMapping> getAllMappings();
  52 +
  53 + @JsonIgnore
  54 + boolean isValid();
  55 +
  56 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config.impl;
  17 +
  18 +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  19 +import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
  20 +
  21 +public class ClientAttributesQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
  22 +
  23 + @Override
  24 + public SnmpCommunicationSpec getSpec() {
  25 + return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING;
  26 + }
  27 +
  28 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config.impl;
  17 +
  18 +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  19 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  20 +import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig;
  21 +
  22 +public class SharedAttributesSettingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
  23 +
  24 + @Override
  25 + public SnmpCommunicationSpec getSpec() {
  26 + return SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING;
  27 + }
  28 +
  29 + @Override
  30 + public SnmpMethod getMethod() {
  31 + return SnmpMethod.SET;
  32 + }
  33 +
  34 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config.impl;
  17 +
  18 +import lombok.Data;
  19 +import lombok.EqualsAndHashCode;
  20 +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  21 +import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
  22 +
  23 +@EqualsAndHashCode(callSuper = true)
  24 +@Data
  25 +public class TelemetryQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
  26 +
  27 + @Override
  28 + public SnmpCommunicationSpec getSpec() {
  29 + return SnmpCommunicationSpec.TELEMETRY_QUERYING;
  30 + }
  31 +
  32 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.transport.snmp.config.impl;
  17 +
  18 +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  19 +import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig;
  20 +
  21 +public class ToDeviceRpcRequestSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
  22 + @Override
  23 + public SnmpCommunicationSpec getSpec() {
  24 + return SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST;
  25 + }
  26 +}
@@ -38,6 +38,7 @@ import static java.util.Collections.emptyList; @@ -38,6 +38,7 @@ import static java.util.Collections.emptyList;
38 @Slf4j 38 @Slf4j
39 public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> implements TbQueueConsumer<T> { 39 public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> implements TbQueueConsumer<T> {
40 40
  41 + public static final long ONE_MILLISECOND_IN_NANOS = TimeUnit.MILLISECONDS.toNanos(1);
41 private volatile boolean subscribed; 42 private volatile boolean subscribed;
42 protected volatile boolean stopped = false; 43 protected volatile boolean stopped = false;
43 protected volatile Set<TopicPartitionInfo> partitions; 44 protected volatile Set<TopicPartitionInfo> partitions;
@@ -83,7 +84,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i @@ -83,7 +84,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
83 } 84 }
84 85
85 if (consumerLock.isLocked()) { 86 if (consumerLock.isLocked()) {
86 - log.error("poll. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock", new RuntimeException("stacktrace")); 87 + log.error("poll. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace"));
87 } 88 }
88 89
89 consumerLock.lock(); 90 consumerLock.lock();
@@ -131,9 +132,12 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i @@ -131,9 +132,12 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
131 List<T> sleepAndReturnEmpty(final long startNanos, final long durationInMillis) { 132 List<T> sleepAndReturnEmpty(final long startNanos, final long durationInMillis) {
132 long durationNanos = TimeUnit.MILLISECONDS.toNanos(durationInMillis); 133 long durationNanos = TimeUnit.MILLISECONDS.toNanos(durationInMillis);
133 long spentNanos = System.nanoTime() - startNanos; 134 long spentNanos = System.nanoTime() - startNanos;
134 - if (spentNanos < durationNanos) { 135 + long nanosLeft = durationNanos - spentNanos;
  136 + if (nanosLeft >= ONE_MILLISECOND_IN_NANOS) {
135 try { 137 try {
136 - Thread.sleep(Math.max(TimeUnit.NANOSECONDS.toMillis(durationNanos - spentNanos), 1)); 138 + long sleepMs = TimeUnit.NANOSECONDS.toMillis(nanosLeft);
  139 + log.trace("Going to sleep after poll: topic {} for {}ms", topic, sleepMs);
  140 + Thread.sleep(sleepMs);
137 } catch (InterruptedException e) { 141 } catch (InterruptedException e) {
138 if (!stopped) { 142 if (!stopped) {
139 log.error("Failed to wait", e); 143 log.error("Failed to wait", e);
@@ -146,7 +150,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i @@ -146,7 +150,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
146 @Override 150 @Override
147 public void commit() { 151 public void commit() {
148 if (consumerLock.isLocked()) { 152 if (consumerLock.isLocked()) {
149 - log.error("commit. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock", new RuntimeException("stacktrace")); 153 + log.error("commit. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace"));
150 } 154 }
151 consumerLock.lock(); 155 consumerLock.lock();
152 try { 156 try {
@@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture;
20 import com.google.common.util.concurrent.SettableFuture; 20 import com.google.common.util.concurrent.SettableFuture;
21 import lombok.Builder; 21 import lombok.Builder;
22 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
  23 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
23 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 24 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
24 import org.thingsboard.server.queue.TbQueueAdmin; 25 import org.thingsboard.server.queue.TbQueueAdmin;
25 import org.thingsboard.server.queue.TbQueueCallback; 26 import org.thingsboard.server.queue.TbQueueCallback;
@@ -77,7 +78,7 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response @@ -77,7 +78,7 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
77 this.executor = executor; 78 this.executor = executor;
78 } else { 79 } else {
79 internalExecutor = true; 80 internalExecutor = true;
80 - this.executor = Executors.newSingleThreadExecutor(); 81 + this.executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-queue-request-template-" + responseTemplate.getTopic()));
81 } 82 }
82 } 83 }
83 84
@@ -17,6 +17,7 @@ package org.thingsboard.server.queue.common; @@ -17,6 +17,7 @@ package org.thingsboard.server.queue.common;
17 17
18 import lombok.Builder; 18 import lombok.Builder;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
20 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 21 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
21 import org.thingsboard.server.queue.TbQueueConsumer; 22 import org.thingsboard.server.queue.TbQueueConsumer;
22 import org.thingsboard.server.queue.TbQueueHandler; 23 import org.thingsboard.server.queue.TbQueueHandler;
@@ -70,8 +71,8 @@ public class DefaultTbQueueResponseTemplate<Request extends TbQueueMsg, Response @@ -70,8 +71,8 @@ public class DefaultTbQueueResponseTemplate<Request extends TbQueueMsg, Response
70 this.requestTimeout = requestTimeout; 71 this.requestTimeout = requestTimeout;
71 this.callbackExecutor = executor; 72 this.callbackExecutor = executor;
72 this.stats = stats; 73 this.stats = stats;
73 - this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor();  
74 - this.loopExecutor = Executors.newSingleThreadExecutor(); 74 + this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("tb-queue-response-template-timeout-" + requestTemplate.getTopic()));
  75 + this.loopExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-queue-response-template-loop-" + requestTemplate.getTopic()));
75 } 76 }
76 77
77 @Override 78 @Override
@@ -19,19 +19,23 @@ import lombok.Getter; @@ -19,19 +19,23 @@ import lombok.Getter;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 import org.springframework.beans.factory.annotation.Autowired; 20 import org.springframework.beans.factory.annotation.Autowired;
21 import org.springframework.beans.factory.annotation.Value; 21 import org.springframework.beans.factory.annotation.Value;
  22 +import org.springframework.context.ApplicationContext;
22 import org.springframework.stereotype.Component; 23 import org.springframework.stereotype.Component;
23 import org.springframework.util.StringUtils; 24 import org.springframework.util.StringUtils;
  25 +import org.thingsboard.server.common.data.TbTransportService;
24 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
25 import org.thingsboard.server.common.msg.queue.ServiceType; 27 import org.thingsboard.server.common.msg.queue.ServiceType;
26 import org.thingsboard.server.gen.transport.TransportProtos; 28 import org.thingsboard.server.gen.transport.TransportProtos;
27 import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; 29 import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
28 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; 30 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
29 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; 31 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
  32 +import org.thingsboard.server.queue.util.AfterContextReady;
30 33
31 import javax.annotation.PostConstruct; 34 import javax.annotation.PostConstruct;
32 import java.net.InetAddress; 35 import java.net.InetAddress;
33 import java.net.UnknownHostException; 36 import java.net.UnknownHostException;
34 import java.util.Arrays; 37 import java.util.Arrays;
  38 +import java.util.Collection;
35 import java.util.Collections; 39 import java.util.Collections;
36 import java.util.List; 40 import java.util.List;
37 import java.util.Optional; 41 import java.util.Optional;
@@ -56,6 +60,8 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { @@ -56,6 +60,8 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
56 60
57 @Autowired(required = false) 61 @Autowired(required = false)
58 private TbQueueRuleEngineSettings ruleEngineSettings; 62 private TbQueueRuleEngineSettings ruleEngineSettings;
  63 + @Autowired
  64 + private ApplicationContext applicationContext;
59 65
60 private List<ServiceType> serviceTypes; 66 private List<ServiceType> serviceTypes;
61 private ServiceInfo serviceInfo; 67 private ServiceInfo serviceInfo;
@@ -102,6 +108,19 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { @@ -102,6 +108,19 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
102 serviceInfo = builder.build(); 108 serviceInfo = builder.build();
103 } 109 }
104 110
  111 + @AfterContextReady
  112 + public void setTransports() {
  113 + serviceInfo = ServiceInfo.newBuilder(serviceInfo)
  114 + .addAllTransports(getTransportServices().stream()
  115 + .map(TbTransportService::getName)
  116 + .collect(Collectors.toSet()))
  117 + .build();
  118 + }
  119 +
  120 + private Collection<TbTransportService> getTransportServices() {
  121 + return applicationContext.getBeansOfType(TbTransportService.class).values();
  122 + }
  123 +
105 @Override 124 @Override
106 public ServiceInfo getServiceInfo() { 125 public ServiceInfo getServiceInfo() {
107 return serviceInfo; 126 return serviceInfo;
@@ -15,26 +15,27 @@ @@ -15,26 +15,27 @@
15 */ 15 */
16 package org.thingsboard.server.queue.discovery; 16 package org.thingsboard.server.queue.discovery;
17 17
18 -import com.google.common.hash.HashCode;  
19 import com.google.common.hash.HashFunction; 18 import com.google.common.hash.HashFunction;
  19 +import com.google.common.hash.Hasher;
20 import com.google.common.hash.Hashing; 20 import com.google.common.hash.Hashing;
21 -import lombok.Getter;  
22 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
23 import org.springframework.beans.factory.annotation.Value; 22 import org.springframework.beans.factory.annotation.Value;
24 import org.springframework.context.ApplicationEventPublisher; 23 import org.springframework.context.ApplicationEventPublisher;
25 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
26 import org.thingsboard.server.common.data.id.EntityId; 25 import org.thingsboard.server.common.data.id.EntityId;
27 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
28 -import org.thingsboard.server.common.msg.queue.ServiceQueueKey;  
29 import org.thingsboard.server.common.msg.queue.ServiceQueue; 27 import org.thingsboard.server.common.msg.queue.ServiceQueue;
  28 +import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
30 import org.thingsboard.server.common.msg.queue.ServiceType; 29 import org.thingsboard.server.common.msg.queue.ServiceType;
31 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 30 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
32 import org.thingsboard.server.gen.transport.TransportProtos; 31 import org.thingsboard.server.gen.transport.TransportProtos;
33 import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; 32 import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
  33 +import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
  34 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
  35 +import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
34 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; 36 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
35 37
36 import javax.annotation.PostConstruct; 38 import javax.annotation.PostConstruct;
37 -import java.nio.charset.StandardCharsets;  
38 import java.util.ArrayList; 39 import java.util.ArrayList;
39 import java.util.Collections; 40 import java.util.Collections;
40 import java.util.Comparator; 41 import java.util.Comparator;
@@ -46,7 +47,6 @@ import java.util.Set; @@ -46,7 +47,6 @@ import java.util.Set;
46 import java.util.UUID; 47 import java.util.UUID;
47 import java.util.concurrent.ConcurrentHashMap; 48 import java.util.concurrent.ConcurrentHashMap;
48 import java.util.concurrent.ConcurrentMap; 49 import java.util.concurrent.ConcurrentMap;
49 -import java.util.concurrent.ConcurrentNavigableMap;  
50 import java.util.stream.Collectors; 50 import java.util.stream.Collectors;
51 51
52 @Service 52 @Service
@@ -186,6 +186,8 @@ public class HashPartitionService implements PartitionService { @@ -186,6 +186,8 @@ public class HashPartitionService implements PartitionService {
186 applicationEventPublisher.publishEvent(new ClusterTopologyChangeEvent(this, changes)); 186 applicationEventPublisher.publishEvent(new ClusterTopologyChangeEvent(this, changes));
187 } 187 }
188 } 188 }
  189 +
  190 + applicationEventPublisher.publishEvent(new ServiceListChangedEvent(otherServices, currentService));
189 } 191 }
190 192
191 @Override 193 @Override
@@ -219,6 +221,14 @@ public class HashPartitionService implements PartitionService { @@ -219,6 +221,14 @@ public class HashPartitionService implements PartitionService {
219 } 221 }
220 } 222 }
221 223
  224 + @Override
  225 + public int resolvePartitionIndex(UUID entityId, int partitions) {
  226 + int hash = hashFunction.newHasher()
  227 + .putLong(entityId.getMostSignificantBits())
  228 + .putLong(entityId.getLeastSignificantBits()).hash().asInt();
  229 + return Math.abs(hash % partitions);
  230 + }
  231 +
222 private Map<ServiceQueueKey, List<ServiceInfo>> getServiceKeyListMap(List<ServiceInfo> services) { 232 private Map<ServiceQueueKey, List<ServiceInfo>> getServiceKeyListMap(List<ServiceInfo> services) {
223 final Map<ServiceQueueKey, List<ServiceInfo>> currentMap = new HashMap<>(); 233 final Map<ServiceQueueKey, List<ServiceInfo>> currentMap = new HashMap<>();
224 services.forEach(serviceInfo -> { 234 services.forEach(serviceInfo -> {
@@ -20,9 +20,11 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -20,9 +20,11 @@ import org.thingsboard.server.common.data.id.TenantId;
20 import org.thingsboard.server.common.msg.queue.ServiceType; 20 import org.thingsboard.server.common.msg.queue.ServiceType;
21 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 21 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
22 import org.thingsboard.server.gen.transport.TransportProtos; 22 import org.thingsboard.server.gen.transport.TransportProtos;
  23 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
23 24
24 import java.util.List; 25 import java.util.List;
25 import java.util.Set; 26 import java.util.Set;
  27 +import java.util.UUID;
26 28
27 /** 29 /**
28 * Once application is ready or cluster topology changes, this Service will produce {@link PartitionChangeEvent} 30 * Once application is ready or cluster topology changes, this Service will produce {@link PartitionChangeEvent}
@@ -55,4 +57,6 @@ public interface PartitionService { @@ -55,4 +57,6 @@ public interface PartitionService {
55 * @return 57 * @return
56 */ 58 */
57 TopicPartitionInfo getNotificationsTopic(ServiceType serviceType, String serviceId); 59 TopicPartitionInfo getNotificationsTopic(ServiceType serviceType, String serviceId);
  60 +
  61 + int resolvePartitionIndex(UUID entityId, int partitions);
58 } 62 }
@@ -17,6 +17,7 @@ package org.thingsboard.server.queue.discovery; @@ -17,6 +17,7 @@ package org.thingsboard.server.queue.discovery;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.context.ApplicationListener; 19 import org.springframework.context.ApplicationListener;
  20 +import org.thingsboard.server.queue.discovery.event.TbApplicationEvent;
20 21
21 import java.util.concurrent.locks.Lock; 22 import java.util.concurrent.locks.Lock;
22 import java.util.concurrent.locks.ReentrantLock; 23 import java.util.concurrent.locks.ReentrantLock;
@@ -33,12 +33,14 @@ import org.apache.zookeeper.KeeperException; @@ -33,12 +33,14 @@ import org.apache.zookeeper.KeeperException;
33 import org.springframework.beans.factory.annotation.Value; 33 import org.springframework.beans.factory.annotation.Value;
34 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 34 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
35 import org.springframework.boot.context.event.ApplicationReadyEvent; 35 import org.springframework.boot.context.event.ApplicationReadyEvent;
  36 +import org.springframework.context.ApplicationEventPublisher;
36 import org.springframework.context.event.EventListener; 37 import org.springframework.context.event.EventListener;
37 import org.springframework.core.annotation.Order; 38 import org.springframework.core.annotation.Order;
38 import org.springframework.stereotype.Service; 39 import org.springframework.stereotype.Service;
39 import org.springframework.util.Assert; 40 import org.springframework.util.Assert;
40 import org.thingsboard.common.util.ThingsBoardThreadFactory; 41 import org.thingsboard.common.util.ThingsBoardThreadFactory;
41 import org.thingsboard.server.gen.transport.TransportProtos; 42 import org.thingsboard.server.gen.transport.TransportProtos;
  43 +import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
42 44
43 import javax.annotation.PostConstruct; 45 import javax.annotation.PostConstruct;
44 import javax.annotation.PreDestroy; 46 import javax.annotation.PreDestroy;
@@ -77,7 +79,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi @@ -77,7 +79,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
77 79
78 private volatile boolean stopped = true; 80 private volatile boolean stopped = true;
79 81
80 - public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider, PartitionService partitionService) { 82 + public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider,
  83 + PartitionService partitionService) {
81 this.serviceInfoProvider = serviceInfoProvider; 84 this.serviceInfoProvider = serviceInfoProvider;
82 this.partitionService = partitionService; 85 this.partitionService = partitionService;
83 } 86 }
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ClusterTopologyChangeEvent.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/discovery/ClusterTopologyChangeEvent.java
@@ -13,10 +13,9 @@ @@ -13,10 +13,9 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.queue.discovery; 16 +package org.thingsboard.server.queue.discovery.event;
17 17
18 import lombok.Getter; 18 import lombok.Getter;
19 -import org.springframework.context.ApplicationEvent;  
20 import org.thingsboard.server.common.msg.queue.ServiceQueueKey; 19 import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
21 20
22 import java.util.Set; 21 import java.util.Set;
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionChangeEvent.java
@@ -13,10 +13,9 @@ @@ -13,10 +13,9 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.queue.discovery; 16 +package org.thingsboard.server.queue.discovery.event;
17 17
18 import lombok.Getter; 18 import lombok.Getter;
19 -import org.springframework.context.ApplicationEvent;  
20 import org.thingsboard.server.common.msg.queue.ServiceQueueKey; 19 import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
21 import org.thingsboard.server.common.msg.queue.ServiceType; 20 import org.thingsboard.server.common.msg.queue.ServiceType;
22 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 21 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.queue.discovery.event;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.ToString;
  20 +import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
  21 +
  22 +import java.util.List;
  23 +
  24 +@Getter
  25 +@ToString
  26 +public class ServiceListChangedEvent extends TbApplicationEvent {
  27 + private final List<ServiceInfo> otherServices;
  28 + private final ServiceInfo currentService;
  29 +
  30 + public ServiceListChangedEvent(List<ServiceInfo> otherServices, ServiceInfo currentService) {
  31 + super(otherServices);
  32 + this.otherServices = otherServices;
  33 + this.currentService = currentService;
  34 + }
  35 +}
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/TbApplicationEvent.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEvent.java
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.queue.discovery; 16 +package org.thingsboard.server.queue.discovery.event;
17 17
18 import lombok.Getter; 18 import lombok.Getter;
19 import org.springframework.context.ApplicationEvent; 19 import org.springframework.context.ApplicationEvent;
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.queue.util;
  17 +
  18 +import org.springframework.context.event.ContextRefreshedEvent;
  19 +import org.springframework.context.event.EventListener;
  20 +import org.springframework.core.annotation.AliasFor;
  21 +import org.springframework.core.annotation.Order;
  22 +
  23 +import java.lang.annotation.ElementType;
  24 +import java.lang.annotation.Retention;
  25 +import java.lang.annotation.RetentionPolicy;
  26 +import java.lang.annotation.Target;
  27 +
  28 +@Retention(RetentionPolicy.RUNTIME)
  29 +@Target(ElementType.METHOD)
  30 +@EventListener(ContextRefreshedEvent.class)
  31 +@Order
  32 +public @interface AfterContextReady {
  33 + @AliasFor(annotation = Order.class, attribute = "value")
  34 + int order() default Integer.MAX_VALUE;
  35 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.queue.util;
  17 +
  18 +import org.springframework.boot.context.event.ApplicationReadyEvent;
  19 +import org.springframework.context.event.EventListener;
  20 +import org.springframework.core.annotation.AliasFor;
  21 +import org.springframework.core.annotation.Order;
  22 +
  23 +import java.lang.annotation.ElementType;
  24 +import java.lang.annotation.Retention;
  25 +import java.lang.annotation.RetentionPolicy;
  26 +import java.lang.annotation.Target;
  27 +
  28 +@Retention(RetentionPolicy.RUNTIME)
  29 +@Target(ElementType.METHOD)
  30 +@EventListener(ApplicationReadyEvent.class)
  31 +@Order
  32 +public @interface AfterStartUp {
  33 + @AliasFor(annotation = Order.class, attribute = "value")
  34 + int order() default Integer.MAX_VALUE;
  35 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.queue.util;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  19 +
  20 +import java.lang.annotation.ElementType;
  21 +import java.lang.annotation.Retention;
  22 +import java.lang.annotation.RetentionPolicy;
  23 +import java.lang.annotation.Target;
  24 +
  25 +@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')")
  26 +@Retention(RetentionPolicy.RUNTIME)
  27 +@Target({ElementType.TYPE, ElementType.METHOD})
  28 +public @interface TbSnmpTransportComponent {
  29 +}
@@ -34,6 +34,7 @@ message ServiceInfo { @@ -34,6 +34,7 @@ message ServiceInfo {
34 int64 tenantIdMSB = 3; 34 int64 tenantIdMSB = 3;
35 int64 tenantIdLSB = 4; 35 int64 tenantIdLSB = 4;
36 repeated QueueInfo ruleEngineQueues = 5; 36 repeated QueueInfo ruleEngineQueues = 5;
  37 + repeated string transports = 6;
37 } 38 }
38 39
39 /** 40 /**
@@ -246,6 +247,36 @@ message GetEntityProfileResponseMsg { @@ -246,6 +247,36 @@ message GetEntityProfileResponseMsg {
246 bytes apiState = 3; 247 bytes apiState = 3;
247 } 248 }
248 249
  250 +message GetDeviceRequestMsg {
  251 + int64 deviceIdMSB = 1;
  252 + int64 deviceIdLSB = 2;
  253 +}
  254 +
  255 +message GetDeviceResponseMsg {
  256 + int64 deviceProfileIdMSB = 1;
  257 + int64 deviceProfileIdLSB = 2;
  258 + bytes deviceTransportConfiguration = 3;
  259 +}
  260 +
  261 +message GetDeviceCredentialsRequestMsg {
  262 + int64 deviceIdMSB = 1;
  263 + int64 deviceIdLSB = 2;
  264 +}
  265 +
  266 +message GetDeviceCredentialsResponseMsg {
  267 + bytes deviceCredentialsData = 1;
  268 +}
  269 +
  270 +message GetSnmpDevicesRequestMsg {
  271 + int32 page = 1;
  272 + int32 pageSize = 2;
  273 +}
  274 +
  275 +message GetSnmpDevicesResponseMsg {
  276 + repeated string ids = 1;
  277 + bool hasNextPage = 2;
  278 +}
  279 +
249 message EntityUpdateMsg { 280 message EntityUpdateMsg {
250 string entityType = 1; 281 string entityType = 1;
251 bytes data = 2; 282 bytes data = 2;
@@ -592,6 +623,9 @@ message TransportApiRequestMsg { @@ -592,6 +623,9 @@ message TransportApiRequestMsg {
592 ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8; 623 ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8;
593 GetResourceRequestMsg resourceRequestMsg = 9; 624 GetResourceRequestMsg resourceRequestMsg = 9;
594 GetFirmwareRequestMsg firmwareRequestMsg = 10; 625 GetFirmwareRequestMsg firmwareRequestMsg = 10;
  626 + GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11;
  627 + GetDeviceRequestMsg deviceRequestMsg = 12;
  628 + GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13;
595 } 629 }
596 630
597 /* Response from ThingsBoard Core Service to Transport Service */ 631 /* Response from ThingsBoard Core Service to Transport Service */
@@ -600,9 +634,12 @@ message TransportApiResponseMsg { @@ -600,9 +634,12 @@ message TransportApiResponseMsg {
600 GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2; 634 GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2;
601 GetEntityProfileResponseMsg entityProfileResponseMsg = 3; 635 GetEntityProfileResponseMsg entityProfileResponseMsg = 3;
602 ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4; 636 ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4;
  637 + GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5;
603 LwM2MResponseMsg lwM2MResponseMsg = 6; 638 LwM2MResponseMsg lwM2MResponseMsg = 6;
604 GetResourceResponseMsg resourceResponseMsg = 7; 639 GetResourceResponseMsg resourceResponseMsg = 7;
605 GetFirmwareResponseMsg firmwareResponseMsg = 8; 640 GetFirmwareResponseMsg firmwareResponseMsg = 8;
  641 + GetDeviceResponseMsg deviceResponseMsg = 9;
  642 + GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10;
606 } 643 }
607 644
608 /* Messages that are handled by ThingsBoard Core Service */ 645 /* Messages that are handled by ThingsBoard Core Service */
@@ -21,7 +21,9 @@ import org.eclipse.californium.core.CoapServer; @@ -21,7 +21,9 @@ import org.eclipse.californium.core.CoapServer;
21 import org.springframework.beans.factory.annotation.Autowired; 21 import org.springframework.beans.factory.annotation.Autowired;
22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
23 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
  24 +import org.thingsboard.server.common.data.TbTransportService;
24 import org.thingsboard.server.coapserver.CoapServerService; 25 import org.thingsboard.server.coapserver.CoapServerService;
  26 +import org.thingsboard.server.coapserver.TbCoapServerComponent;
25 import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource; 27 import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource;
26 28
27 import javax.annotation.PostConstruct; 29 import javax.annotation.PostConstruct;
@@ -29,9 +31,9 @@ import javax.annotation.PreDestroy; @@ -29,9 +31,9 @@ import javax.annotation.PreDestroy;
29 import java.net.UnknownHostException; 31 import java.net.UnknownHostException;
30 32
31 @Service("CoapTransportService") 33 @Service("CoapTransportService")
32 -@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") 34 +@TbCoapServerComponent
33 @Slf4j 35 @Slf4j
34 -public class CoapTransportService { 36 +public class CoapTransportService implements TbTransportService {
35 37
36 private static final String V1 = "v1"; 38 private static final String V1 = "v1";
37 private static final String API = "api"; 39 private static final String API = "api";
@@ -65,4 +67,9 @@ public class CoapTransportService { @@ -65,4 +67,9 @@ public class CoapTransportService {
65 public void shutdown() { 67 public void shutdown() {
66 log.info("CoAP transport stopped!"); 68 log.info("CoAP transport stopped!");
67 } 69 }
  70 +
  71 + @Override
  72 + public String getName() {
  73 + return "COAP";
  74 + }
68 } 75 }
@@ -35,6 +35,7 @@ import org.springframework.web.bind.annotation.RestController; @@ -35,6 +35,7 @@ import org.springframework.web.bind.annotation.RestController;
35 import org.springframework.web.context.request.async.DeferredResult; 35 import org.springframework.web.context.request.async.DeferredResult;
36 import org.thingsboard.server.common.data.DeviceTransportType; 36 import org.thingsboard.server.common.data.DeviceTransportType;
37 import org.thingsboard.server.common.data.firmware.FirmwareType; 37 import org.thingsboard.server.common.data.firmware.FirmwareType;
  38 +import org.thingsboard.server.common.data.TbTransportService;
38 import org.thingsboard.server.common.data.id.DeviceId; 39 import org.thingsboard.server.common.data.id.DeviceId;
39 import org.thingsboard.server.common.transport.SessionMsgListener; 40 import org.thingsboard.server.common.transport.SessionMsgListener;
40 import org.thingsboard.server.common.transport.TransportContext; 41 import org.thingsboard.server.common.transport.TransportContext;
@@ -71,7 +72,7 @@ import java.util.function.Consumer; @@ -71,7 +72,7 @@ import java.util.function.Consumer;
71 @ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.http.enabled}'=='true')") 72 @ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.http.enabled}'=='true')")
72 @RequestMapping("/api/v1") 73 @RequestMapping("/api/v1")
73 @Slf4j 74 @Slf4j
74 -public class DeviceApiController { 75 +public class DeviceApiController implements TbTransportService {
75 76
76 @Autowired 77 @Autowired
77 private HttpTransportContext transportContext; 78 private HttpTransportContext transportContext;
@@ -422,4 +423,9 @@ public class DeviceApiController { @@ -422,4 +423,9 @@ public class DeviceApiController {
422 } 423 }
423 } 424 }
424 425
  426 + @Override
  427 + public String getName() {
  428 + return "HTTP";
  429 + }
  430 +
425 } 431 }
@@ -55,7 +55,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE @@ -55,7 +55,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
55 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; 55 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
56 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; 56 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
57 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; 57 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
58 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig; 58 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
59 59
60 @Slf4j 60 @Slf4j
61 @Component 61 @Component
@@ -93,7 +93,6 @@ public class LwM2MTransportBootstrapServerConfiguration { @@ -93,7 +93,6 @@ public class LwM2MTransportBootstrapServerConfiguration {
93 builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort)); 93 builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort));
94 94
95 /** Define model provider (Create Models )*/ 95 /** Define model provider (Create Models )*/
96 -// builder.setModel(new StaticModel(contextS.getLwM2MTransportConfigServer().getModelsValueCommon()));  
97 96
98 /** Create credentials */ 97 /** Create credentials */
99 this.setServerWithCredentials(builder); 98 this.setServerWithCredentials(builder);
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server;
  17 +
  18 +import org.eclipse.californium.core.network.config.NetworkConfig;
  19 +
  20 +public class LwM2mNetworkConfig {
  21 +
  22 + public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) {
  23 + NetworkConfig coapConfig = new NetworkConfig();
  24 + coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec);
  25 + coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort);
  26 + /**
  27 + * Example:Property for large packet:
  28 + * #NetworkConfig config = new NetworkConfig();
  29 + * #config.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE,32);
  30 + * #config.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE,32);
  31 + * #config.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE,2048);
  32 + * #config.setInt(NetworkConfig.Keys.MAX_RETRANSMIT,3);
  33 + * #config.setInt(NetworkConfig.Keys.MAX_TRANSMIT_WAIT,120000);
  34 + */
  35 +
  36 + /**
  37 + * Property to indicate if the response should always include the Block2 option \
  38 + * when client request early blockwise negociation but the response can be sent on one packet.
  39 + * - value of false indicate that the server will respond without block2 option if no further blocks are required.
  40 + * - value of true indicate that the server will response with block2 option event if no further blocks are required.
  41 + * CoAP client will try to use block mode
  42 + * or adapt the block size when receiving a 4.13 Entity too large response code
  43 + */
  44 + coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
  45 + /***
  46 + * Property to indicate if the response should always include the Block2 option \
  47 + * when client request early blockwise negociation but the response can be sent on one packet.
  48 + * - value of false indicate that the server will respond without block2 option if no further blocks are required.
  49 + * - value of true indicate that the server will response with block2 option event if no further blocks are required.
  50 + */
  51 + coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
  52 +
  53 + coapConfig.setInt(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, 300000);
  54 + /**
  55 + * !!! REQUEST_ENTITY_TOO_LARGE CODE=4.13
  56 + * The maximum size of a resource body (in bytes) that will be accepted
  57 + * as the payload of a POST/PUT or the response to a GET request in a
  58 + * transparent> blockwise transfer.
  59 + * This option serves as a safeguard against excessive memory
  60 + * consumption when many resources contain large bodies that cannot be
  61 + * transferred in a single CoAP message. This option has no impact on
  62 + * *manually* managed blockwise transfers in which the blocks are handled individually.
  63 + * Note that this option does not prevent local clients or resource
  64 + * implementations from sending large bodies as part of a request or response to a peer.
  65 + * The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192
  66 + * A value of {@code 0} turns off transparent handling of blockwise transfers altogether.
  67 + */
  68 +// coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 8192);
  69 + coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 16384);
  70 + /**
  71 + * The default DTLS response matcher.
  72 + * Supported values are STRICT, RELAXED, or PRINCIPAL.
  73 + * The default value is STRICT.
  74 + * Create new instance of udp endpoint context matcher.
  75 + * Params:
  76 + * checkAddress
  77 + * – true with address check, (STRICT, UDP)
  78 + * - false, without
  79 + */
  80 + coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT");
  81 + /**
  82 + * https://tools.ietf.org/html/rfc7959#section-2.9.3
  83 + * The block size (number of bytes) to use when doing a blockwise transfer. \
  84 + * This value serves as the upper limit for block size in blockwise transfers
  85 + */
  86 + coapConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 512);
  87 + /**
  88 + * The maximum payload size (in bytes) that can be transferred in a
  89 + * single message, i.e. without requiring a blockwise transfer.
  90 + * NB: this value MUST be adapted to the maximum message size supported by the transport layer.
  91 + * In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol
  92 + * DEFAULT_VALUE = 1024
  93 + */
  94 + coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 512);
  95 +
  96 + coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4);
  97 +
  98 + return coapConfig;
  99 + }
  100 +}
@@ -26,6 +26,7 @@ import org.eclipse.leshan.server.registration.RegistrationUpdate; @@ -26,6 +26,7 @@ import org.eclipse.leshan.server.registration.RegistrationUpdate;
26 26
27 import java.util.Collection; 27 import java.util.Collection;
28 28
  29 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
29 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer; 30 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer;
30 31
31 @Slf4j 32 @Slf4j
@@ -85,17 +86,19 @@ public class LwM2mServerListener { @@ -85,17 +86,19 @@ public class LwM2mServerListener {
85 86
86 @Override 87 @Override
87 public void cancelled(Observation observation) { 88 public void cancelled(Observation observation) {
88 - log.info("Received notification cancelled from [{}] ", observation.getPath()); 89 + String msg = String.format("%s: Cancel Observation %s.", LOG_LW2M_INFO, observation.getPath());
  90 + service.sendLogsToThingsboard(msg, observation.getRegistrationId());
  91 + log.trace(msg);
89 } 92 }
90 93
91 @Override 94 @Override
92 public void onResponse(Observation observation, Registration registration, ObserveResponse response) { 95 public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
93 if (registration != null) { 96 if (registration != null) {
94 try { 97 try {
95 - service.onObservationResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), 98 + service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
96 registration), response, null); 99 registration), response, null);
97 } catch (Exception e) { 100 } catch (Exception e) {
98 - log.error("[{}] onResponse", e.toString()); 101 + log.error("Observation/Read onResponse", e);
99 102
100 } 103 }
101 } 104 }
@@ -108,7 +111,10 @@ public class LwM2mServerListener { @@ -108,7 +111,10 @@ public class LwM2mServerListener {
108 111
109 @Override 112 @Override
110 public void newObservation(Observation observation, Registration registration) { 113 public void newObservation(Observation observation, Registration registration) {
111 - log.info("Received newObservation from [{}] endpoint [{}] ", observation.getPath(), registration.getEndpoint()); 114 + String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO,
  115 + observation.getPath());
  116 + service.sendLogsToThingsboard(msg, registration.getId());
  117 + log.trace(msg);
112 } 118 }
113 }; 119 };
114 120
@@ -25,7 +25,6 @@ import com.google.gson.JsonSyntaxException; @@ -25,7 +25,6 @@ import com.google.gson.JsonSyntaxException;
25 import com.google.gson.reflect.TypeToken; 25 import com.google.gson.reflect.TypeToken;
26 import lombok.extern.slf4j.Slf4j; 26 import lombok.extern.slf4j.Slf4j;
27 import org.apache.commons.lang3.StringUtils; 27 import org.apache.commons.lang3.StringUtils;
28 -import org.eclipse.californium.core.network.config.NetworkConfig;  
29 import org.eclipse.leshan.core.attributes.Attribute; 28 import org.eclipse.leshan.core.attributes.Attribute;
30 import org.eclipse.leshan.core.attributes.AttributeSet; 29 import org.eclipse.leshan.core.attributes.AttributeSet;
31 import org.eclipse.leshan.core.model.ObjectModel; 30 import org.eclipse.leshan.core.model.ObjectModel;
@@ -40,7 +39,6 @@ import org.eclipse.leshan.core.node.codec.CodecException; @@ -40,7 +39,6 @@ import org.eclipse.leshan.core.node.codec.CodecException;
40 import org.eclipse.leshan.core.request.DownlinkRequest; 39 import org.eclipse.leshan.core.request.DownlinkRequest;
41 import org.eclipse.leshan.core.request.WriteAttributesRequest; 40 import org.eclipse.leshan.core.request.WriteAttributesRequest;
42 import org.eclipse.leshan.core.util.Hex; 41 import org.eclipse.leshan.core.util.Hex;
43 -import org.eclipse.leshan.server.californium.LeshanServerBuilder;  
44 import org.eclipse.leshan.server.registration.Registration; 42 import org.eclipse.leshan.server.registration.Registration;
45 import org.nustaq.serialization.FSTConfiguration; 43 import org.nustaq.serialization.FSTConfiguration;
46 import org.thingsboard.server.common.data.DeviceProfile; 44 import org.thingsboard.server.common.data.DeviceProfile;
@@ -50,7 +48,6 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; @@ -50,7 +48,6 @@ import org.thingsboard.server.common.transport.TransportServiceCallback;
50 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; 48 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
51 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; 49 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
52 50
53 -import java.io.File;  
54 import java.io.IOException; 51 import java.io.IOException;
55 import java.util.ArrayList; 52 import java.util.ArrayList;
56 import java.util.Arrays; 53 import java.util.Arrays;
@@ -72,7 +69,6 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA @@ -72,7 +69,6 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA
72 @Slf4j 69 @Slf4j
73 public class LwM2mTransportHandler { 70 public class LwM2mTransportHandler {
74 71
75 - // public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me";  
76 public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0"; 72 public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0";
77 public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings"; 73 public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";
78 public static final String BOOTSTRAP = "bootstrap"; 74 public static final String BOOTSTRAP = "bootstrap";
@@ -85,19 +81,12 @@ public class LwM2mTransportHandler { @@ -85,19 +81,12 @@ public class LwM2mTransportHandler {
85 public static final String KEY_NAME = "keyName"; 81 public static final String KEY_NAME = "keyName";
86 public static final String OBSERVE_LWM2M = "observe"; 82 public static final String OBSERVE_LWM2M = "observe";
87 public static final String ATTRIBUTE_LWM2M = "attributeLwm2m"; 83 public static final String ATTRIBUTE_LWM2M = "attributeLwm2m";
88 -// public static final String RESOURCE_VALUE = "resValue";  
89 -// public static final String RESOURCE_TYPE = "resType";  
90 84
91 private static final String REQUEST = "/request"; 85 private static final String REQUEST = "/request";
92 - // private static final String RESPONSE = "/response";  
93 private static final String ATTRIBUTES = "/" + ATTRIBUTE; 86 private static final String ATTRIBUTES = "/" + ATTRIBUTE;
94 public static final String TELEMETRIES = "/" + TELEMETRY; 87 public static final String TELEMETRIES = "/" + TELEMETRY;
95 - // public static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE;  
96 public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST; 88 public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST;
97 - // public static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/";  
98 public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/"; 89 public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/";
99 -// public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES;  
100 -// public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRIES;  
101 90
102 public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms 91 public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
103 92
@@ -112,6 +101,11 @@ public class LwM2mTransportHandler { @@ -112,6 +101,11 @@ public class LwM2mTransportHandler {
112 101
113 public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized"; 102 public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized";
114 103
  104 + public static final Integer FR_OBJECT_ID = 5;
  105 + public static final Integer FR_RESOURCE_VER_ID = 7;
  106 + public static final String FR_PATH_RESOURCE_VER_ID = LWM2M_SEPARATOR_PATH + FR_OBJECT_ID + LWM2M_SEPARATOR_PATH
  107 + + "0" + LWM2M_SEPARATOR_PATH + FR_RESOURCE_VER_ID;
  108 +
115 public enum LwM2mTypeServer { 109 public enum LwM2mTypeServer {
116 BOOTSTRAP(0, "bootstrap"), 110 BOOTSTRAP(0, "bootstrap"),
117 CLIENT(1, "client"); 111 CLIENT(1, "client");
@@ -168,6 +162,8 @@ public class LwM2mTransportHandler { @@ -168,6 +162,8 @@ public class LwM2mTransportHandler {
168 WRITE_ATTRIBUTES(8, "WriteAttributes"), 162 WRITE_ATTRIBUTES(8, "WriteAttributes"),
169 DELETE(9, "Delete"); 163 DELETE(9, "Delete");
170 164
  165 +// READ_INFO_FW(10, "ReadInfoFirmware");
  166 +
171 public int code; 167 public int code;
172 public String type; 168 public String type;
173 169
@@ -190,21 +186,6 @@ public class LwM2mTransportHandler { @@ -190,21 +186,6 @@ public class LwM2mTransportHandler {
190 public static final String SERVICE_CHANNEL = "SERVICE"; 186 public static final String SERVICE_CHANNEL = "SERVICE";
191 public static final String RESPONSE_CHANNEL = "RESP"; 187 public static final String RESPONSE_CHANNEL = "RESP";
192 188
193 - public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) {  
194 - NetworkConfig coapConfig;  
195 - File configFile = new File(NetworkConfig.DEFAULT_FILE_NAME);  
196 - if (configFile.isFile()) {  
197 - coapConfig = new NetworkConfig();  
198 - coapConfig.load(configFile);  
199 - } else {  
200 - coapConfig = LeshanServerBuilder.createDefaultNetworkConfig();  
201 - coapConfig.store(configFile);  
202 - }  
203 - coapConfig.setString("COAP_PORT", Integer.toString(serverPortNoSec));  
204 - coapConfig.setString("COAP_SECURE_PORT", Integer.toString(serverSecurePort));  
205 - return coapConfig;  
206 - }  
207 -  
208 public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException { 189 public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException {
209 switch (type) { 190 switch (type) {
210 case BOOLEAN: 191 case BOOLEAN:
@@ -67,6 +67,7 @@ import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; @@ -67,6 +67,7 @@ import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
67 import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST; 67 import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST;
68 import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND; 68 import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND;
69 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT; 69 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT;
  70 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.FR_PATH_RESOURCE_VER_ID;
70 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; 71 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
71 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; 72 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
72 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; 73 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE;
@@ -125,7 +126,6 @@ public class LwM2mTransportRequest { @@ -125,7 +126,6 @@ public class LwM2mTransportRequest {
125 public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper, 126 public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
126 String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { 127 String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
127 try { 128 try {
128 -  
129 String target = convertPathFromIdVerToObjectId(targetIdVer); 129 String target = convertPathFromIdVerToObjectId(targetIdVer);
130 DownlinkRequest request = null; 130 DownlinkRequest request = null;
131 ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT; 131 ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT;
@@ -145,11 +145,11 @@ public class LwM2mTransportRequest { @@ -145,11 +145,11 @@ public class LwM2mTransportRequest {
145 break; 145 break;
146 case OBSERVE: 146 case OBSERVE:
147 if (resultIds.isResource()) { 147 if (resultIds.isResource()) {
148 - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); 148 + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
149 } else if (resultIds.isObjectInstance()) { 149 } else if (resultIds.isObjectInstance()) {
150 - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId()); 150 + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId());
151 } else if (resultIds.getObjectId() >= 0) { 151 } else if (resultIds.getObjectId() >= 0) {
152 - request = new ObserveRequest(resultIds.getObjectId()); 152 + request = new ObserveRequest(contentFormat, resultIds.getObjectId());
153 } 153 }
154 break; 154 break;
155 case OBSERVE_CANCEL: 155 case OBSERVE_CANCEL:
@@ -171,8 +171,6 @@ public class LwM2mTransportRequest { @@ -171,8 +171,6 @@ public class LwM2mTransportRequest {
171 break; 171 break;
172 case WRITE_REPLACE: 172 case WRITE_REPLACE:
173 // Request to write a <b>String Single-Instance Resource</b> using the TLV content format. 173 // Request to write a <b>String Single-Instance Resource</b> using the TLV content format.
174 -// resource = lwM2MClient.getResourceModel(targetIdVer);  
175 -// if (contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {  
176 resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() 174 resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
177 .getModelProvider()); 175 .getModelProvider());
178 if (contentFormat.equals(ContentFormat.TLV)) { 176 if (contentFormat.equals(ContentFormat.TLV)) {
@@ -181,7 +179,6 @@ public class LwM2mTransportRequest { @@ -181,7 +179,6 @@ public class LwM2mTransportRequest {
181 registration, rpcRequest); 179 registration, rpcRequest);
182 } 180 }
183 // Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON) 181 // Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON)
184 -// else if (!contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {  
185 else if (!contentFormat.equals(ContentFormat.TLV)) { 182 else if (!contentFormat.equals(ContentFormat.TLV)) {
186 request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), 183 request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
187 resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type, 184 resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type,
@@ -215,13 +212,16 @@ public class LwM2mTransportRequest { @@ -215,13 +212,16 @@ public class LwM2mTransportRequest {
215 long finalTimeoutInMs = timeoutInMs; 212 long finalTimeoutInMs = timeoutInMs;
216 lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest)); 213 lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest));
217 } catch (Exception e) { 214 } catch (Exception e) {
218 - log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper, e); 215 + log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e);
  216 + }
  217 + } else if (OBSERVE_CANCEL == typeOper) {
  218 + log.trace("[{}], [{}] - [{}] SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
  219 + if (rpcRequest != null) {
  220 + rpcRequest.setInfoMsg(null);
  221 + serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null);
219 } 222 }
220 - } else if (OBSERVE_CANCEL == typeOper && rpcRequest != null) {  
221 - rpcRequest.setInfoMsg(null);  
222 - serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null);  
223 } else { 223 } else {
224 - log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper, targetIdVer); 224 + log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
225 if (rpcRequest != null) { 225 if (rpcRequest != null) {
226 String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; 226 String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
227 serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); 227 serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
@@ -236,8 +236,8 @@ public class LwM2mTransportRequest { @@ -236,8 +236,8 @@ public class LwM2mTransportRequest {
236 Set<String> observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); 236 Set<String> observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
237 String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO, 237 String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO,
238 OBSERVE_READ_ALL.type, observationPaths); 238 OBSERVE_READ_ALL.type, observationPaths);
239 - serviceImpl.sendLogsToThingsboard(msg, registration);  
240 - log.info("[{}], [{}]", registration.getEndpoint(), msg); 239 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  240 + log.trace("[{}] [{}], [{}]", typeOper.name(), registration.getEndpoint(), msg);
241 if (rpcRequest != null) { 241 if (rpcRequest != null) {
242 String valueMsg = String.format("Observation paths - %s", observationPaths); 242 String valueMsg = String.format("Observation paths - %s", observationPaths);
243 serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); 243 serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
@@ -246,7 +246,7 @@ public class LwM2mTransportRequest { @@ -246,7 +246,7 @@ public class LwM2mTransportRequest {
246 } catch (Exception e) { 246 } catch (Exception e) {
247 String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, 247 String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
248 typeOper.name(), e.getMessage()); 248 typeOper.name(), e.getMessage());
249 - serviceImpl.sendLogsToThingsboard(msg, registration); 249 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
250 throw new Exception(e); 250 throw new Exception(e);
251 } 251 }
252 } 252 }
@@ -261,45 +261,53 @@ public class LwM2mTransportRequest { @@ -261,45 +261,53 @@ public class LwM2mTransportRequest {
261 private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { 261 private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
262 leshanServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> { 262 leshanServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
263 if (!lwM2MClient.isInit()) { 263 if (!lwM2MClient.isInit()) {
264 - lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); 264 + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
265 } 265 }
266 if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { 266 if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
267 this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest); 267 this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest);
268 - if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) {  
269 - LwM2mNode node = ((WriteRequest) request).getNode();  
270 - Object value = this.converter.convertValue(((LwM2mSingleResource) node).getValue(),  
271 - ((LwM2mSingleResource) node).getType(), ResourceModel.Type.STRING, request.getPath());  
272 - String msg = String.format("%s: sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client",  
273 - LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(),  
274 - response.getCode().getName(), request.getPath().toString(), value);  
275 - serviceImpl.sendLogsToThingsboard(msg, registration);  
276 - log.info("[{}] [{}] - [{}] [{}] Update SendRequest[{}]", registration.getEndpoint(),  
277 - ((Response) response.getCoapResponse()).getCode(), response.getCode(),  
278 - request.getPath().toString(), value);  
279 - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);  
280 - }  
281 } else { 268 } else {
282 - String msg = String.format("%s: sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", LOG_LW2M_ERROR, 269 + String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(),
283 ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); 270 ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
284 - serviceImpl.sendLogsToThingsboard(msg, registration);  
285 - log.error("[{}], [{}] - [{}] [{}] error SendRequest", registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); 271 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  272 + log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(),
  273 + ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
  274 + if (!lwM2MClient.isInit()) {
  275 + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
  276 + }
286 if (rpcRequest != null) { 277 if (rpcRequest != null) {
287 serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR); 278 serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
288 } 279 }
  280 + /** Not Found
  281 + * set setClient_fw_version = empty
  282 + **/
  283 + if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
  284 + lwM2MClient.setUpdateFw(false);
  285 + lwM2MClient.getFrUpdate().setClientFwVersion("");
  286 + log.warn("updateFirmwareClient1");
  287 + serviceImpl.updateFirmwareClient(lwM2MClient);
  288 + }
289 } 289 }
290 }, e -> { 290 }, e -> {
  291 + /** version == null
  292 + * set setClient_fw_version = empty
  293 + **/
  294 + if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
  295 + lwM2MClient.setUpdateFw(false);
  296 + lwM2MClient.getFrUpdate().setClientFwVersion("");
  297 + log.warn("updateFirmwareClient2");
  298 + serviceImpl.updateFirmwareClient(lwM2MClient);
  299 + }
291 if (!lwM2MClient.isInit()) { 300 if (!lwM2MClient.isInit()) {
292 - lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); 301 + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
293 } 302 }
294 - String msg = String.format("%s: sendRequest: Resource path - %s msg error - %s SendRequest to Client",  
295 - LOG_LW2M_ERROR, request.getPath().toString(), e.getMessage());  
296 - serviceImpl.sendLogsToThingsboard(msg, registration);  
297 - log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString()); 303 + String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s",
  304 + LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage());
  305 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  306 + log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
298 if (rpcRequest != null) { 307 if (rpcRequest != null) {
299 serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); 308 serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
300 } 309 }
301 }); 310 });
302 -  
303 } 311 }
304 312
305 private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, 313 private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId,
@@ -323,7 +331,9 @@ public class LwM2mTransportRequest { @@ -323,7 +331,9 @@ public class LwM2mTransportRequest {
323 Date date = new Date(Long.decode(value.toString())); 331 Date date = new Date(Long.decode(value.toString()));
324 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date); 332 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date);
325 case OPAQUE: // byte[] value, base64 333 case OPAQUE: // byte[] value, base64
326 - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())); 334 + byte[] valueRequest = value instanceof byte[] ? (byte[]) value : Hex.decodeHex(value.toString().toCharArray());
  335 + return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueRequest) :
  336 + new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueRequest);
327 default: 337 default:
328 } 338 }
329 } 339 }
@@ -337,7 +347,7 @@ public class LwM2mTransportRequest { @@ -337,7 +347,7 @@ public class LwM2mTransportRequest {
337 String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; 347 String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
338 String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", 348 String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
339 patn, type, value, e.toString()); 349 patn, type, value, e.toString());
340 - serviceImpl.sendLogsToThingsboard(msg, registration); 350 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
341 log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); 351 log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
342 if (rpcRequest != null) { 352 if (rpcRequest != null) {
343 String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); 353 String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
@@ -369,7 +379,7 @@ public class LwM2mTransportRequest { @@ -369,7 +379,7 @@ public class LwM2mTransportRequest {
369 DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { 379 DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
370 String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); 380 String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
371 if (response instanceof ReadResponse) { 381 if (response instanceof ReadResponse) {
372 - serviceImpl.onObservationResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); 382 + serviceImpl.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
373 } else if (response instanceof CancelObservationResponse) { 383 } else if (response instanceof CancelObservationResponse) {
374 log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response); 384 log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response);
375 385
@@ -389,14 +399,33 @@ public class LwM2mTransportRequest { @@ -389,14 +399,33 @@ public class LwM2mTransportRequest {
389 } else if (response instanceof WriteAttributesResponse) { 399 } else if (response instanceof WriteAttributesResponse) {
390 log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response); 400 log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response);
391 } else if (response instanceof WriteResponse) { 401 } else if (response instanceof WriteResponse) {
392 - log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", pathIdVer, response); 402 + log.info("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response);
  403 + this.infoWriteResponse(registration, response, request);
393 serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); 404 serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
394 } 405 }
395 - if (rpcRequest != null && (response instanceof ExecuteResponse  
396 - || response instanceof WriteAttributesResponse  
397 - || response instanceof DeleteResponse)) {  
398 - rpcRequest.setInfoMsg(null);  
399 - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null); 406 + if (rpcRequest != null) {
  407 + if (response instanceof ExecuteResponse
  408 + || response instanceof WriteAttributesResponse
  409 + || response instanceof DeleteResponse) {
  410 + rpcRequest.setInfoMsg(null);
  411 + serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null);
  412 + } else if (response instanceof WriteResponse) {
  413 + serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
  414 + }
400 } 415 }
401 } 416 }
  417 +
  418 + private void infoWriteResponse(Registration registration, LwM2mResponse response,
  419 + DownlinkRequest request) {
  420 + LwM2mNode node = ((WriteRequest) request).getNode();
  421 + Object value = this.converter.convertValue(((LwM2mSingleResource) node).getValue(),
  422 + ((LwM2mSingleResource) node).getType(), ResourceModel.Type.STRING, request.getPath());
  423 + String msg = String.format("%s: Update finished successfully: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s",
  424 + LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(),
  425 + response.getCode().getName(), request.getPath().toString(), value);
  426 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  427 + log.warn("[{}] [{}] [{}] - [{}] [{}] Update finished successfully: [{}]", request.getClass().getName().toString(), registration.getEndpoint(),
  428 + ((Response) response.getCoapResponse()).getCode(), response.getCode(),
  429 + request.getPath().toString(), value);
  430 + }
402 } 431 }
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 package org.thingsboard.server.transport.lwm2m.server; 16 package org.thingsboard.server.transport.lwm2m.server;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.californium.core.network.config.NetworkConfig;
  20 +import org.eclipse.californium.core.network.stack.BlockwiseLayer;
19 import org.eclipse.californium.scandium.config.DtlsConnectorConfig; 21 import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
20 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; 22 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
21 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; 23 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
@@ -57,7 +59,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE @@ -57,7 +59,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
57 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; 59 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
58 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; 60 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
59 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; 61 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
60 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig; 62 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
61 63
62 @Slf4j 64 @Slf4j
63 @Component 65 @Component
@@ -92,7 +94,10 @@ public class LwM2mTransportServerConfiguration { @@ -92,7 +94,10 @@ public class LwM2mTransportServerConfiguration {
92 /** Use a magic converter to support bad type send by the UI. */ 94 /** Use a magic converter to support bad type send by the UI. */
93 builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); 95 builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
94 96
  97 +
95 /** Create CoAP Config */ 98 /** Create CoAP Config */
  99 + NetworkConfig networkConfig = getCoapConfig(serverPortNoSec, serverSecurePort);
  100 + BlockwiseLayer blockwiseLayer = new BlockwiseLayer(networkConfig);
96 builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort)); 101 builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort));
97 102
98 /** Define model provider (Create Models )*/ 103 /** Define model provider (Create Models )*/
@@ -110,6 +115,7 @@ public class LwM2mTransportServerConfiguration { @@ -110,6 +115,7 @@ public class LwM2mTransportServerConfiguration {
110 115
111 /** Create DTLS Config */ 116 /** Create DTLS Config */
112 DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); 117 DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
  118 + dtlsConfig.setServerOnly(true);
113 dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups()); 119 dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups());
114 dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers()); 120 dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers());
115 if (this.pskMode) { 121 if (this.pskMode) {
@@ -20,13 +20,14 @@ import org.eclipse.leshan.core.response.ReadResponse; @@ -20,13 +20,14 @@ import org.eclipse.leshan.core.response.ReadResponse;
20 import org.eclipse.leshan.server.registration.Registration; 20 import org.eclipse.leshan.server.registration.Registration;
21 import org.thingsboard.server.common.data.Device; 21 import org.thingsboard.server.common.data.Device;
22 import org.thingsboard.server.common.data.DeviceProfile; 22 import org.thingsboard.server.common.data.DeviceProfile;
  23 +import org.thingsboard.server.common.data.TbTransportService;
23 import org.thingsboard.server.gen.transport.TransportProtos; 24 import org.thingsboard.server.gen.transport.TransportProtos;
24 import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; 25 import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
25 26
26 import java.util.Collection; 27 import java.util.Collection;
27 import java.util.Optional; 28 import java.util.Optional;
28 29
29 -public interface LwM2mTransportService { 30 +public interface LwM2mTransportService extends TbTransportService {
30 31
31 void onRegistered(Registration registration, Collection<Observation> previousObsersations); 32 void onRegistered(Registration registration, Collection<Observation> previousObsersations);
32 33
@@ -38,7 +39,7 @@ public interface LwM2mTransportService { @@ -38,7 +39,7 @@ public interface LwM2mTransportService {
38 39
39 void setCancelObservations(Registration registration); 40 void setCancelObservations(Registration registration);
40 41
41 - void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest); 42 + void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest);
42 43
43 void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo); 44 void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo);
44 45
@@ -59,6 +60,4 @@ public interface LwM2mTransportService { @@ -59,6 +60,4 @@ public interface LwM2mTransportService {
59 void doTrigger(Registration registration, String path); 60 void doTrigger(Registration registration, String path);
60 61
61 void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); 62 void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
62 -  
63 -  
64 } 63 }
@@ -38,9 +38,15 @@ import org.eclipse.leshan.server.registration.Registration; @@ -38,9 +38,15 @@ import org.eclipse.leshan.server.registration.Registration;
38 import org.springframework.context.annotation.Lazy; 38 import org.springframework.context.annotation.Lazy;
39 import org.springframework.stereotype.Service; 39 import org.springframework.stereotype.Service;
40 import org.thingsboard.common.util.JacksonUtil; 40 import org.thingsboard.common.util.JacksonUtil;
  41 +import org.thingsboard.server.cache.firmware.FirmwareDataCache;
41 import org.thingsboard.server.common.data.Device; 42 import org.thingsboard.server.common.data.Device;
42 import org.thingsboard.server.common.data.DeviceProfile; 43 import org.thingsboard.server.common.data.DeviceProfile;
  44 +import org.thingsboard.server.common.data.firmware.FirmwareKey;
  45 +import org.thingsboard.server.common.data.firmware.FirmwareType;
  46 +import org.thingsboard.server.common.data.firmware.FirmwareUtil;
  47 +import org.thingsboard.server.common.data.id.FirmwareId;
43 import org.thingsboard.server.common.transport.TransportService; 48 import org.thingsboard.server.common.transport.TransportService;
  49 +import org.thingsboard.server.common.transport.TransportServiceCallback;
44 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 50 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
45 import org.thingsboard.server.common.transport.service.DefaultTransportService; 51 import org.thingsboard.server.common.transport.service.DefaultTransportService;
46 import org.thingsboard.server.gen.transport.TransportProtos; 52 import org.thingsboard.server.gen.transport.TransportProtos;
@@ -76,9 +82,11 @@ import java.util.stream.Collectors; @@ -76,9 +82,11 @@ import java.util.stream.Collectors;
76 82
77 import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; 83 import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST;
78 import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; 84 import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
  85 +import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
79 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; 86 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
80 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED; 87 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED;
81 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST; 88 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST;
  89 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.FR_PATH_RESOURCE_VER_ID;
82 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; 90 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
83 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; 91 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
84 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; 92 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE;
@@ -109,6 +117,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -109,6 +117,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
109 private ExecutorService executorUpdateRegistered; 117 private ExecutorService executorUpdateRegistered;
110 private ExecutorService executorUnRegistered; 118 private ExecutorService executorUnRegistered;
111 private LwM2mValueConverterImpl converter; 119 private LwM2mValueConverterImpl converter;
  120 + private FirmwareDataCache firmwareDataCache;
  121 +
112 122
113 private final TransportService transportService; 123 private final TransportService transportService;
114 124
@@ -120,12 +130,15 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -120,12 +130,15 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
120 130
121 private final LwM2mTransportRequest lwM2mTransportRequest; 131 private final LwM2mTransportRequest lwM2mTransportRequest;
122 132
123 - public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer, LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, @Lazy LwM2mTransportRequest lwM2mTransportRequest) { 133 + public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer,
  134 + LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer,
  135 + @Lazy LwM2mTransportRequest lwM2mTransportRequest, FirmwareDataCache firmwareDataCache) {
124 this.transportService = transportService; 136 this.transportService = transportService;
125 this.lwM2mTransportContextServer = lwM2mTransportContextServer; 137 this.lwM2mTransportContextServer = lwM2mTransportContextServer;
126 this.lwM2mClientContext = lwM2mClientContext; 138 this.lwM2mClientContext = lwM2mClientContext;
127 this.leshanServer = leshanServer; 139 this.leshanServer = leshanServer;
128 this.lwM2mTransportRequest = lwM2mTransportRequest; 140 this.lwM2mTransportRequest = lwM2mTransportRequest;
  141 + this.firmwareDataCache = firmwareDataCache;
129 } 142 }
130 143
131 @PostConstruct 144 @PostConstruct
@@ -167,8 +180,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -167,8 +180,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
167 transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); 180 transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
168 transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); 181 transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
169 transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null); 182 transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null);
  183 + this.getInfoFirmwareUpdate(lwM2MClient);
170 this.initLwM2mFromClientValue(registration, lwM2MClient); 184 this.initLwM2mFromClientValue(registration, lwM2MClient);
171 - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration); 185 + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId());
172 } else { 186 } else {
173 log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); 187 log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
174 } 188 }
@@ -223,7 +237,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -223,7 +237,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
223 executorUnRegistered.submit(() -> { 237 executorUnRegistered.submit(() -> {
224 try { 238 try {
225 this.setCancelObservations(registration); 239 this.setCancelObservations(registration);
226 - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); 240 + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
227 this.closeClientSession(registration); 241 this.closeClientSession(registration);
228 } catch (Throwable t) { 242 } catch (Throwable t) {
229 log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); 243 log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
@@ -256,7 +270,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -256,7 +270,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
256 @Override 270 @Override
257 public void onSleepingDev(Registration registration) { 271 public void onSleepingDev(Registration registration) {
258 log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); 272 log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
259 - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration); 273 + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration.getId());
260 274
261 //TODO: associate endpointId with device information. 275 //TODO: associate endpointId with device information.
262 } 276 }
@@ -279,7 +293,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -279,7 +293,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
279 * @param response - observe 293 * @param response - observe
280 */ 294 */
281 @Override 295 @Override
282 - public void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) { 296 + public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) {
283 if (response.getContent() != null) { 297 if (response.getContent() != null) {
284 if (response.getContent() instanceof LwM2mObject) { 298 if (response.getContent() instanceof LwM2mObject) {
285 LwM2mObject lwM2mObject = (LwM2mObject) response.getContent(); 299 LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
@@ -303,20 +317,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -303,20 +317,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
303 317
304 /** 318 /**
305 * Update - send request in change value resources in Client 319 * Update - send request in change value resources in Client
306 - * Path to resources from profile equal keyName or from ModelObject equal name  
307 - * Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)  
308 - * Delete - nothing * 320 + * 1. FirmwareUpdate:
  321 + * - If msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0
  322 + * 2. Shared Other AttributeUpdate
  323 + * -- Path to resources from profile equal keyName or from ModelObject equal name
  324 + * -- Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
  325 + * 3. Delete - nothing
309 * 326 *
310 * @param msg - 327 * @param msg -
311 */ 328 */
312 @Override 329 @Override
313 public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { 330 public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
  331 + LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
314 if (msg.getSharedUpdatedCount() > 0) { 332 if (msg.getSharedUpdatedCount() > 0) {
315 msg.getSharedUpdatedList().forEach(tsKvProto -> { 333 msg.getSharedUpdatedList().forEach(tsKvProto -> {
316 String pathName = tsKvProto.getKv().getKey(); 334 String pathName = tsKvProto.getKv().getKey();
317 String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); 335 String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
318 Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); 336 Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv());
319 - LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); 337 + if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
  338 + this.getInfoFirmwareUpdate(lwM2MClient);
  339 + }
320 if (pathIdVer != null) { 340 if (pathIdVer != null) {
321 ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() 341 ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
322 .getModelProvider()); 342 .getModelProvider());
@@ -326,19 +346,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -326,19 +346,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
326 log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); 346 log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew);
327 String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", 347 String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
328 LOG_LW2M_ERROR, pathIdVer, valueNew); 348 LOG_LW2M_ERROR, pathIdVer, valueNew);
329 - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); 349 + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
330 } 350 }
331 } else { 351 } else {
332 log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); 352 log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew);
333 String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", 353 String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
334 LOG_LW2M_ERROR, pathName, valueNew); 354 LOG_LW2M_ERROR, pathName, valueNew);
335 - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); 355 + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
336 } 356 }
  357 +
337 }); 358 });
338 } else if (msg.getSharedDeletedCount() > 0) { 359 } else if (msg.getSharedDeletedCount() > 0) {
  360 + msg.getSharedUpdatedList().forEach(tsKvProto -> {
  361 + String pathName = tsKvProto.getKv().getKey();
  362 + Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv());
  363 + if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
  364 + lwM2MClient.getFrUpdate().setCurrentFwVersion((String) valueNew);
  365 + }
  366 + });
339 log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); 367 log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
340 } 368 }
341 -  
342 } 369 }
343 370
344 /** 371 /**
@@ -454,23 +481,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -454,23 +481,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
454 } 481 }
455 if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) { 482 if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) {
456 lwm2mClientRpcRequest.setParams(new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey) 483 lwm2mClientRpcRequest.setParams(new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey)
457 - .getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {  
458 - }.getType())); 484 + .getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {
  485 + }.getType()));
459 } 486 }
460 lwm2mClientRpcRequest.setSessionInfo(sessionInfo); 487 lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
461 if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) { 488 if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) {
462 lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " + 489 lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " +
463 lwm2mClientRpcRequest.keyNameKey + " is null or bad format"); 490 lwm2mClientRpcRequest.keyNameKey + " is null or bad format");
464 - }  
465 - else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper() 491 + } else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
466 || WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper()) 492 || WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper())
467 - && lwm2mClientRpcRequest.getTargetIdVer() !=null 493 + && lwm2mClientRpcRequest.getTargetIdVer() != null
468 && !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource() 494 && !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource()
469 || new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) { 495 || new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) {
470 - lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey 496 + lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey
471 + ". Only Resource or ResourceInstance can be this operation"); 497 + ". Only Resource or ResourceInstance can be this operation");
472 - }  
473 - else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()){ 498 + } else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()) {
474 lwm2mClientRpcRequest.setErrorMsg("Procedures In Development..."); 499 lwm2mClientRpcRequest.setErrorMsg("Procedures In Development...");
475 } 500 }
476 } else { 501 } else {
@@ -482,23 +507,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -482,23 +507,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
482 return lwm2mClientRpcRequest; 507 return lwm2mClientRpcRequest;
483 } 508 }
484 509
485 - public void sentRpcRequest (Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { 510 + public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) {
486 rpcRequest.setResponseCode(requestCode); 511 rpcRequest.setResponseCode(requestCode);
487 - if (LOG_LW2M_ERROR.equals(typeMsg)) {  
488 - rpcRequest.setInfoMsg(null);  
489 - rpcRequest.setValueMsg(null);  
490 - if (rpcRequest.getErrorMsg() == null) {  
491 - msg = msg.isEmpty() ? null : msg;  
492 - rpcRequest.setErrorMsg(msg);  
493 - }  
494 - } else if (LOG_LW2M_INFO.equals(typeMsg)) {  
495 - if (rpcRequest.getInfoMsg() == null) {  
496 - rpcRequest.setInfoMsg(msg);  
497 - }  
498 - } else if (LOG_LW2M_VALUE.equals(typeMsg)) {  
499 - if (rpcRequest.getValueMsg() == null) {  
500 - rpcRequest.setValueMsg(msg);  
501 - } 512 + if (LOG_LW2M_ERROR.equals(typeMsg)) {
  513 + rpcRequest.setInfoMsg(null);
  514 + rpcRequest.setValueMsg(null);
  515 + if (rpcRequest.getErrorMsg() == null) {
  516 + msg = msg.isEmpty() ? null : msg;
  517 + rpcRequest.setErrorMsg(msg);
  518 + }
  519 + } else if (LOG_LW2M_INFO.equals(typeMsg)) {
  520 + if (rpcRequest.getInfoMsg() == null) {
  521 + rpcRequest.setInfoMsg(msg);
  522 + }
  523 + } else if (LOG_LW2M_VALUE.equals(typeMsg)) {
  524 + if (rpcRequest.getValueMsg() == null) {
  525 + rpcRequest.setValueMsg(msg);
  526 + }
502 } 527 }
503 this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo()); 528 this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo());
504 } 529 }
@@ -555,7 +580,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -555,7 +580,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
555 */ 580 */
556 protected void onAwakeDev(Registration registration) { 581 protected void onAwakeDev(Registration registration) {
557 log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); 582 log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
558 - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration); 583 + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId());
559 //TODO: associate endpointId with device information. 584 //TODO: associate endpointId with device information.
560 } 585 }
561 586
@@ -582,10 +607,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -582,10 +607,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
582 607
583 /** 608 /**
584 * @param logMsg - text msg 609 * @param logMsg - text msg
585 - * @param registration - Id of Registration LwM2M Client 610 + * @param registrationId - Id of Registration LwM2M Client
586 */ 611 */
587 - public void sendLogsToThingsboard(String logMsg, Registration registration) {  
588 - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); 612 + public void sendLogsToThingsboard(String logMsg, String registrationId) {
  613 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId);
589 if (logMsg != null && sessionInfo != null) { 614 if (logMsg != null && sessionInfo != null) {
590 this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo); 615 this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo);
591 } 616 }
@@ -609,7 +634,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -609,7 +634,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
609 if (clientObjects != null && clientObjects.size() > 0) { 634 if (clientObjects != null && clientObjects.size() > 0) {
610 if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { 635 if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
611 // #2 636 // #2
612 - lwM2MClient.getPendingRequests().addAll(clientObjects); 637 + lwM2MClient.getPendingReadRequests().addAll(clientObjects);
613 clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(), 638 clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(),
614 null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); 639 null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null));
615 } 640 }
@@ -651,6 +676,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -651,6 +676,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
651 * Sending observe value of resources to thingsboard 676 * Sending observe value of resources to thingsboard
652 * #1 Return old Value Resource from LwM2MClient 677 * #1 Return old Value Resource from LwM2MClient
653 * #2 Update new Resources (replace old Resource Value on new Resource Value) 678 * #2 Update new Resources (replace old Resource Value on new Resource Value)
  679 + * #3 If fr_update -> UpdateFirmware
  680 + * #4 updateAttrTelemetry
654 * 681 *
655 * @param registration - Registration LwM2M Client 682 * @param registration - Registration LwM2M Client
656 * @param lwM2mResource - LwM2mSingleResource response.getContent() 683 * @param lwM2mResource - LwM2mSingleResource response.getContent()
@@ -660,6 +687,19 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -660,6 +687,19 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
660 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); 687 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
661 if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() 688 if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
662 .getModelProvider())) { 689 .getModelProvider())) {
  690 + if (FR_PATH_RESOURCE_VER_ID.equals(convertPathFromIdVerToObjectId(path)) &&
  691 + lwM2MClient.getFrUpdate().getCurrentFwVersion() != null
  692 + && !lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())
  693 + && lwM2MClient.isUpdateFw()) {
  694 +
  695 + /** version != null
  696 + * set setClient_fw_version = value
  697 + **/
  698 + lwM2MClient.setUpdateFw(false);
  699 + lwM2MClient.getFrUpdate().setClientFwVersion(lwM2mResource.getValue().toString());
  700 + log.warn("updateFirmwareClient3");
  701 + this.updateFirmwareClient(lwM2MClient);
  702 + }
663 Set<String> paths = new HashSet<>(); 703 Set<String> paths = new HashSet<>();
664 paths.add(path); 704 paths.add(path);
665 this.updateAttrTelemetry(registration, paths); 705 this.updateAttrTelemetry(registration, paths);
@@ -668,6 +708,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -668,6 +708,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
668 } 708 }
669 } 709 }
670 710
  711 +
671 /** 712 /**
672 * send Attribute and Telemetry to Thingsboard 713 * send Attribute and Telemetry to Thingsboard
673 * #1 - get AttrName/TelemetryName with value from LwM2MClient: 714 * #1 - get AttrName/TelemetryName with value from LwM2MClient:
@@ -722,22 +763,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -722,22 +763,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
722 params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()); 763 params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
723 result = params.keySet(); 764 result = params.keySet();
724 } 765 }
725 - if (!result.isEmpty()) { 766 + if (result != null && !result.isEmpty()) {
726 // #1 767 // #1
727 Set<String> pathSend = result.stream().filter(target -> { 768 Set<String> pathSend = result.stream().filter(target -> {
728 return target.split(LWM2M_SEPARATOR_PATH).length < 3 ? 769 return target.split(LWM2M_SEPARATOR_PATH).length < 3 ?
729 clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) : 770 clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) :
730 clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]); 771 clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]);
731 } 772 }
732 - )  
733 - .collect(Collectors.toUnmodifiableSet()); 773 + ).collect(Collectors.toUnmodifiableSet());
734 if (!pathSend.isEmpty()) { 774 if (!pathSend.isEmpty()) {
735 - lwM2MClient.getPendingRequests().addAll(pathSend); 775 + lwM2MClient.getPendingReadRequests().addAll(pathSend);
736 ConcurrentHashMap<String, Object> finalParams = params; 776 ConcurrentHashMap<String, Object> finalParams = params;
737 - pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),  
738 - finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); 777 + pathSend.forEach(target -> {
  778 + lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),
  779 + finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
  780 + });
739 if (OBSERVE.equals(typeOper)) { 781 if (OBSERVE.equals(typeOper)) {
740 - lwM2MClient.initValue(this, null); 782 + lwM2MClient.initReadValue(this, null);
741 } 783 }
742 } 784 }
743 } 785 }
@@ -968,7 +1010,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -968,7 +1010,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
968 // update value in Resources 1010 // update value in Resources
969 registrationIds.forEach(registrationId -> { 1011 registrationIds.forEach(registrationId -> {
970 Registration registration = lwM2mClientContext.getRegistration(registrationId); 1012 Registration registration = lwM2mClientContext.getRegistration(registrationId);
971 - this.readResourceValueObserve(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ); 1013 + this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
972 // send attr/telemetry to tingsboard for new path 1014 // send attr/telemetry to tingsboard for new path
973 this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd()); 1015 this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd());
974 }); 1016 });
@@ -997,12 +1039,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -997,12 +1039,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
997 registrationIds.forEach(registrationId -> { 1039 registrationIds.forEach(registrationId -> {
998 Registration registration = lwM2mClientContext.getRegistration(registrationId); 1040 Registration registration = lwM2mClientContext.getRegistration(registrationId);
999 if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) { 1041 if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) {
1000 - this.readResourceValueObserve(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE); 1042 + this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
1001 } 1043 }
1002 // 5.3 del 1044 // 5.3 del
1003 // send Request cancel observe to Client 1045 // send Request cancel observe to Client
1004 if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) { 1046 if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) {
1005 - this.cancelObserveIsValue(registration, postObserveAnalyzer.getPathPostParametersDel()); 1047 + this.cancelObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersDel());
1006 } 1048 }
1007 }); 1049 });
1008 } 1050 }
@@ -1042,7 +1084,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1042,7 +1084,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1042 * @param registration - Registration LwM2M Client 1084 * @param registration - Registration LwM2M Client
1043 * @param targets - path Resources == [ "/2/0/0", "/2/0/1"] 1085 * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
1044 */ 1086 */
1045 - private void readResourceValueObserve(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) { 1087 + private void readObserveFromProfile(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) {
1046 targets.forEach(target -> { 1088 targets.forEach(target -> {
1047 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target)); 1089 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target));
1048 if (pathIds.isResource()) { 1090 if (pathIds.isResource()) {
@@ -1132,7 +1174,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1132,7 +1174,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1132 1174
1133 } 1175 }
1134 1176
1135 - private void cancelObserveIsValue(Registration registration, Set<String> paramAnallyzer) { 1177 + private void cancelObserveFromProfile(Registration registration, Set<String> paramAnallyzer) {
1136 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); 1178 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
1137 paramAnallyzer.forEach(pathIdVer -> { 1179 paramAnallyzer.forEach(pathIdVer -> {
1138 if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) { 1180 if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) {
@@ -1152,7 +1194,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1152,7 +1194,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1152 log.error("Failed update resource [{}] [{}]", path, valueNew); 1194 log.error("Failed update resource [{}] [{}]", path, valueNew);
1153 String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad", 1195 String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad",
1154 LOG_LW2M_ERROR, path, valueNew); 1196 LOG_LW2M_ERROR, path, valueNew);
1155 - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); 1197 + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
1156 log.info("Failed update resource [{}] [{}]", path, valueNew); 1198 log.info("Failed update resource [{}] [{}]", path, valueNew);
1157 } 1199 }
1158 } 1200 }
@@ -1181,8 +1223,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1181,8 +1223,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1181 } 1223 }
1182 1224
1183 /** 1225 /**
1184 - * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values  
1185 - * #1 Get path resource by result attributesResponse 1226 + * 1. FirmwareUpdate:
  1227 + * - msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0
  1228 + * 2. Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
  1229 + * - Get path resource by result attributesResponse
1186 * 1230 *
1187 * @param attributesResponse - 1231 * @param attributesResponse -
1188 * @param sessionInfo - 1232 * @param sessionInfo -
@@ -1190,6 +1234,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1190,6 +1234,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1190 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { 1234 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
1191 try { 1235 try {
1192 List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList(); 1236 List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList();
  1237 +
1193 this.updateAttriuteFromThingsboard(tsKvProtos, sessionInfo); 1238 this.updateAttriuteFromThingsboard(tsKvProtos, sessionInfo);
1194 } catch (Exception e) { 1239 } catch (Exception e) {
1195 log.error(String.valueOf(e)); 1240 log.error(String.valueOf(e));
@@ -1273,7 +1318,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1273,7 +1318,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1273 */ 1318 */
1274 private SessionInfoProto getValidateSessionInfo(String registrationId) { 1319 private SessionInfoProto getValidateSessionInfo(String registrationId) {
1275 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(null, registrationId); 1320 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(null, registrationId);
1276 - return getNewSessionInfoProto(lwM2MClient); 1321 + return lwM2MClient != null ? this.getNewSessionInfoProto(lwM2MClient) : null;
1277 } 1322 }
1278 1323
1279 /** 1324 /**
@@ -1292,28 +1337,88 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1292,28 +1337,88 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1292 } 1337 }
1293 1338
1294 /** 1339 /**
1295 - * !!! sharedAttr === profileAttr !!!  
1296 - * If there is a difference in values between the current resource values and the shared attribute values  
1297 - * when the client connects to the server  
1298 - * #1 get attributes name from profile include name resources in ModelObject if resource isWritable  
1299 - * #2.1 #1 size > 0 => send Request getAttributes to thingsboard 1340 + * #1. !!! sharedAttr === profileAttr !!!
  1341 + * - If there is a difference in values between the current resource values and the shared attribute values
  1342 + * - when the client connects to the server
  1343 + * #1.1 get attributes name from profile include name resources in ModelObject if resource isWritable
  1344 + * #1.2 #1 size > 0 => send Request getAttributes to thingsboard
  1345 + * #2. FirmwareAttribute subscribe:
1300 * 1346 *
1301 * @param lwM2MClient - LwM2M Client 1347 * @param lwM2MClient - LwM2M Client
1302 */ 1348 */
1303 public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) { 1349 public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) {
1304 SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration()); 1350 SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
1305 if (sessionInfo != null) { 1351 if (sessionInfo != null) {
1306 - //#1.1 + #1.2  
1307 - List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);  
1308 - if (attrSharedNames.size() > 0) {  
1309 - //#2.1 1352 + //#1.1
  1353 + ConcurrentMap<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient);
  1354 + if (keyNamesMap.values().size() > 0) {
1310 try { 1355 try {
1311 - TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, attrSharedNames); 1356 + //#1.2
  1357 + TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, keyNamesMap.values());
1312 transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); 1358 transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
1313 } catch (AdaptorException e) { 1359 } catch (AdaptorException e) {
1314 log.warn("Failed to decode get attributes request", e); 1360 log.warn("Failed to decode get attributes request", e);
1315 } 1361 }
1316 } 1362 }
  1363 +
  1364 + }
  1365 + }
  1366 +
  1367 + public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) {
  1368 + SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
  1369 + if (sessionInfo != null) {
  1370 + TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
  1371 + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
  1372 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
  1373 + .setTenantIdMSB(sessionInfo.getTenantIdMSB())
  1374 + .setTenantIdLSB(sessionInfo.getTenantIdLSB())
  1375 + .build();
  1376 + transportService.process(sessionInfo, getFirmwareRequestMsg,
  1377 + new TransportServiceCallback<>() {
  1378 + @Override
  1379 + public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) {
  1380 + if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
  1381 + lwM2MClient.getFrUpdate().setCurrentFwVersion(response.getVersion());
  1382 + lwM2MClient.getFrUpdate().setCurrentFwId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
  1383 + lwM2MClient.setUpdateFw(true);
  1384 + readRequestToClientFirmwareVer(lwM2MClient.getRegistration());
  1385 + } else {
  1386 + log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
  1387 + }
  1388 + }
  1389 +
  1390 + @Override
  1391 + public void onError(Throwable e) {
  1392 + log.trace("Failed to process credentials ", e);
  1393 + }
  1394 + });
  1395 + }
  1396 + }
  1397 +
  1398 + /**
  1399 + * @param registration
  1400 + */
  1401 + public void readRequestToClientFirmwareVer(Registration registration) {
  1402 + String pathIdVer = convertPathFromObjectIdToIdVer(FR_PATH_RESOURCE_VER_ID, registration);
  1403 + lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, READ, ContentFormat.TLV.getName(),
  1404 + null, lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
  1405 + }
  1406 +
  1407 + /**
  1408 + *
  1409 + * @param lwM2MClient -
  1410 + */
  1411 + public void updateFirmwareClient(LwM2mClient lwM2MClient) {
  1412 + if (!lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())) {
  1413 + int chunkSize = 0;
  1414 + int chunk = 0;
  1415 + byte[] firmwareChunk = firmwareDataCache.get(lwM2MClient.getFrUpdate().getCurrentFwId().toString(), chunkSize, chunk);
  1416 + Integer objectId = 5;
  1417 + String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(objectId);
  1418 + String targetIdVer = LWM2M_SEPARATOR_PATH + objectId + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0;
  1419 + lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.TLV.getName(),
  1420 + firmwareChunk, lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null);
  1421 + log.warn("updateFirmwareClient [{}] [{}]", lwM2MClient.getFrUpdate().getCurrentFwVersion(), lwM2MClient.getFrUpdate().getClientFwVersion());
1317 } 1422 }
1318 } 1423 }
1319 1424
@@ -1325,23 +1430,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1325,23 +1430,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1325 * @param lwM2MClient - 1430 * @param lwM2MClient -
1326 * @return ArrayList keyNames from profile profileAttr && IsWritable 1431 * @return ArrayList keyNames from profile profileAttr && IsWritable
1327 */ 1432 */
1328 - private List<String> getNamesAttrFromProfileIsWritable(LwM2mClient lwM2MClient) { 1433 + private ConcurrentMap<String, String> getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) {
  1434 +
1329 LwM2mClientProfile profile = lwM2mClientContext.getProfile(lwM2MClient.getProfileId()); 1435 LwM2mClientProfile profile = lwM2mClientContext.getProfile(lwM2MClient.getProfileId());
1330 - Set<String> attrSet = new Gson().fromJson(profile.getPostAttributeProfile(),  
1331 - new TypeToken<HashSet<String>>() {  
1332 - }.getType());  
1333 - ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), 1436 + return new Gson().fromJson(profile.getPostKeyNameProfile().toString(),
1334 new TypeToken<ConcurrentHashMap<String, String>>() { 1437 new TypeToken<ConcurrentHashMap<String, String>>() {
1335 }.getType()); 1438 }.getType());
1336 -  
1337 - ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()  
1338 - .stream()  
1339 - .filter(e -> (attrSet.contains(e.getKey()) && validateResourceInModel(lwM2MClient, e.getKey(), true)))  
1340 - .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));  
1341 -  
1342 - Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();  
1343 - namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));  
1344 - return new ArrayList<>(namesIsWritable);  
1345 } 1439 }
1346 1440
1347 private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { 1441 private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) {
@@ -1353,4 +1447,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @@ -1353,4 +1447,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1353 objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() : 1447 objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() :
1354 objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId))); 1448 objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)));
1355 } 1449 }
  1450 +
  1451 + @Override
  1452 + public String getName() {
  1453 + return "LWM2M";
  1454 + }
  1455 +
1356 } 1456 }
@@ -24,11 +24,8 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException; @@ -24,11 +24,8 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException;
24 import org.thingsboard.server.common.transport.adaptor.JsonConverter; 24 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
25 import org.thingsboard.server.gen.transport.TransportProtos; 25 import org.thingsboard.server.gen.transport.TransportProtos;
26 26
27 -import java.util.Arrays;  
28 -import java.util.HashSet;  
29 -import java.util.List; 27 +import java.util.Collection;
30 import java.util.Random; 28 import java.util.Random;
31 -import java.util.Set;  
32 29
33 @Slf4j 30 @Slf4j
34 @Component("LwM2MJsonAdaptor") 31 @Component("LwM2MJsonAdaptor")
@@ -54,11 +51,7 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor { @@ -54,11 +51,7 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
54 } 51 }
55 52
56 @Override 53 @Override
57 - public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException {  
58 - return processGetAttributeRequestMsg(clientKeys, sharedKeys);  
59 - }  
60 -  
61 - protected TransportProtos.GetAttributeRequestMsg processGetAttributeRequestMsg(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException { 54 + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection<String> clientKeys, Collection<String> sharedKeys) throws AdaptorException {
62 try { 55 try {
63 TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); 56 TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
64 Random random = new Random(); 57 Random random = new Random();
@@ -75,14 +68,4 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor { @@ -75,14 +68,4 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
75 throw new AdaptorException(e); 68 throw new AdaptorException(e);
76 } 69 }
77 } 70 }
78 -  
79 - private Set<String> toStringSet(JsonElement requestBody, String name) {  
80 - JsonElement element = requestBody.getAsJsonObject().get(name);  
81 - if (element != null) {  
82 - return new HashSet<>(Arrays.asList(element.getAsString().split(",")));  
83 - } else {  
84 - return null;  
85 - }  
86 - }  
87 -  
88 } 71 }
@@ -19,7 +19,7 @@ import com.google.gson.JsonElement; @@ -19,7 +19,7 @@ import com.google.gson.JsonElement;
19 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 19 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
20 import org.thingsboard.server.gen.transport.TransportProtos; 20 import org.thingsboard.server.gen.transport.TransportProtos;
21 21
22 -import java.util.List; 22 +import java.util.Collection;
23 23
24 public interface LwM2MTransportAdaptor { 24 public interface LwM2MTransportAdaptor {
25 25
@@ -27,5 +27,5 @@ public interface LwM2MTransportAdaptor { @@ -27,5 +27,5 @@ public interface LwM2MTransportAdaptor {
27 27
28 TransportProtos.PostAttributeMsg convertToPostAttributes(JsonElement jsonElement) throws AdaptorException; 28 TransportProtos.PostAttributeMsg convertToPostAttributes(JsonElement jsonElement) throws AdaptorException;
29 29
30 - TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException; 30 + TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection<String> clientKeys, Collection<String> sharedKeys) throws AdaptorException;
31 } 31 }
@@ -57,13 +57,15 @@ public class LwM2mClient implements Cloneable { @@ -57,13 +57,15 @@ public class LwM2mClient implements Cloneable {
57 private UUID deviceId; 57 private UUID deviceId;
58 private UUID sessionId; 58 private UUID sessionId;
59 private UUID profileId; 59 private UUID profileId;
  60 + private volatile LwM2mFirmwareUpdate frUpdate;
60 private Registration registration; 61 private Registration registration;
61 private ValidateDeviceCredentialsResponseMsg credentialsResponse; 62 private ValidateDeviceCredentialsResponseMsg credentialsResponse;
62 private final Map<String, ResourceValue> resources; 63 private final Map<String, ResourceValue> resources;
63 private final Map<String, TransportProtos.TsKvProto> delayedRequests; 64 private final Map<String, TransportProtos.TsKvProto> delayedRequests;
64 - private final List<String> pendingRequests; 65 + private final List<String> pendingReadRequests;
65 private final Queue<LwM2mQueuedRequest> queuedRequests; 66 private final Queue<LwM2mQueuedRequest> queuedRequests;
66 private boolean init; 67 private boolean init;
  68 + private volatile boolean updateFw;
67 69
68 public Object clone() throws CloneNotSupportedException { 70 public Object clone() throws CloneNotSupportedException {
69 return super.clone(); 71 return super.clone();
@@ -75,12 +77,14 @@ public class LwM2mClient implements Cloneable { @@ -75,12 +77,14 @@ public class LwM2mClient implements Cloneable {
75 this.securityInfo = securityInfo; 77 this.securityInfo = securityInfo;
76 this.credentialsResponse = credentialsResponse; 78 this.credentialsResponse = credentialsResponse;
77 this.delayedRequests = new ConcurrentHashMap<>(); 79 this.delayedRequests = new ConcurrentHashMap<>();
78 - this.pendingRequests = new CopyOnWriteArrayList<>(); 80 + this.pendingReadRequests = new CopyOnWriteArrayList<>();
79 this.resources = new ConcurrentHashMap<>(); 81 this.resources = new ConcurrentHashMap<>();
80 this.profileId = profileId; 82 this.profileId = profileId;
81 this.sessionId = sessionId; 83 this.sessionId = sessionId;
82 this.init = false; 84 this.init = false;
  85 + this.updateFw = false;
83 this.queuedRequests = new ConcurrentLinkedQueue<>(); 86 this.queuedRequests = new ConcurrentLinkedQueue<>();
  87 + this.frUpdate = new LwM2mFirmwareUpdate();
84 } 88 }
85 89
86 public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) { 90 public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) {
@@ -103,15 +107,13 @@ public class LwM2mClient implements Cloneable { @@ -103,15 +107,13 @@ public class LwM2mClient implements Cloneable {
103 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez)); 107 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez));
104 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); 108 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
105 String verRez = getVerFromPathIdVerOrId(pathRez); 109 String verRez = getVerFromPathIdVerOrId(pathRez);
106 - return (verRez == null || verSupportedObject.equals(verRez)) ? modelProvider.getObjectModel(registration) 110 + return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration)
107 .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null; 111 .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null;
108 } 112 }
109 113
110 public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, LwM2mModelProvider modelProvider, 114 public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, LwM2mModelProvider modelProvider,
111 LwM2mValueConverterImpl converter) { 115 LwM2mValueConverterImpl converter) {
112 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer)); 116 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer));
113 - String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());  
114 - String verRez = getVerFromPathIdVerOrId(pathRezIdVer);  
115 Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet(); 117 Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet();
116 Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration) 118 Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration)
117 .getObjectModel(pathIds.getObjectId()).resources; 119 .getObjectModel(pathIds.getObjectId()).resources;
@@ -170,11 +172,11 @@ public class LwM2mClient implements Cloneable { @@ -170,11 +172,11 @@ public class LwM2mClient implements Cloneable {
170 .collect(Collectors.toSet()); 172 .collect(Collectors.toSet());
171 } 173 }
172 174
173 - public void initValue(LwM2mTransportServiceImpl serviceImpl, String path) { 175 + public void initReadValue(LwM2mTransportServiceImpl serviceImpl, String path) {
174 if (path != null) { 176 if (path != null) {
175 - this.pendingRequests.remove(path); 177 + this.pendingReadRequests.remove(path);
176 } 178 }
177 - if (this.pendingRequests.size() == 0) { 179 + if (this.pendingReadRequests.size() == 0) {
178 this.init = true; 180 this.init = true;
179 serviceImpl.putDelayedUpdateResourcesThingsboard(this); 181 serviceImpl.putDelayedUpdateResourcesThingsboard(this);
180 } 182 }
@@ -82,12 +82,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @@ -82,12 +82,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
82 82
83 @Override 83 @Override
84 public LwM2mClient getLwM2mClientWithReg(Registration registration, String registrationId) { 84 public LwM2mClient getLwM2mClientWithReg(Registration registration, String registrationId) {
85 - LwM2mClient client = registrationId != null ? 85 + LwM2mClient client = registrationId != null && this.lwM2mClients.containsKey(registrationId) ?
86 this.lwM2mClients.get(registrationId) : 86 this.lwM2mClients.get(registrationId) :
87 - this.lwM2mClients.containsKey(registration.getId()) ?  
88 - this.lwM2mClients.get(registration.getId()) :  
89 - this.lwM2mClients.get(registration.getEndpoint());  
90 - return client != null ? client : updateInSessionsLwM2MClient(registration); 87 + registration !=null && this.lwM2mClients.containsKey(registration.getId()) ?
  88 + this.lwM2mClients.get(registration.getId()) : registration !=null && this.lwM2mClients.containsKey(registration) ?
  89 + this.lwM2mClients.get(registration.getEndpoint()) : null;
  90 + return client != null ? client : registration!= null ? updateInSessionsLwM2MClient(registration) : null;
91 } 91 }
92 92
93 @Override 93 @Override
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.client;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import java.util.UUID;
  21 +
  22 +@Data
  23 +public class LwM2mFirmwareUpdate {
  24 + private volatile String clientFwVersion;
  25 + private volatile String currentFwVersion;
  26 + private volatile UUID currentFwId;
  27 +}
@@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value; @@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value;
28 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 28 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
29 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 29 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
30 import org.springframework.stereotype.Service; 30 import org.springframework.stereotype.Service;
  31 +import org.thingsboard.server.common.data.TbTransportService;
31 32
32 import javax.annotation.PostConstruct; 33 import javax.annotation.PostConstruct;
33 import javax.annotation.PreDestroy; 34 import javax.annotation.PreDestroy;
@@ -38,7 +39,7 @@ import javax.annotation.PreDestroy; @@ -38,7 +39,7 @@ import javax.annotation.PreDestroy;
38 @Service("MqttTransportService") 39 @Service("MqttTransportService")
39 @ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')") 40 @ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')")
40 @Slf4j 41 @Slf4j
41 -public class MqttTransportService { 42 +public class MqttTransportService implements TbTransportService {
42 43
43 @Value("${transport.mqtt.bind_address}") 44 @Value("${transport.mqtt.bind_address}")
44 private String host; 45 private String host;
@@ -90,4 +91,9 @@ public class MqttTransportService { @@ -90,4 +91,9 @@ public class MqttTransportService {
90 } 91 }
91 log.info("MQTT transport stopped!"); 92 log.info("MQTT transport stopped!");
92 } 93 }
  94 +
  95 + @Override
  96 + public String getName() {
  97 + return "MQTT";
  98 + }
93 } 99 }
@@ -40,6 +40,7 @@ @@ -40,6 +40,7 @@
40 <module>http</module> 40 <module>http</module>
41 <module>coap</module> 41 <module>coap</module>
42 <module>lwm2m</module> 42 <module>lwm2m</module>
  43 + <module>snmp</module>
43 </modules> 44 </modules>
44 45
45 </project> 46 </project>
  1 +<!--
  2 +
  3 + Copyright © 2016-2021 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  20 + <modelVersion>4.0.0</modelVersion>
  21 +
  22 + <parent>
  23 + <groupId>org.thingsboard.common</groupId>
  24 + <version>3.3.0-SNAPSHOT</version>
  25 + <artifactId>transport</artifactId>
  26 + </parent>
  27 +
  28 + <groupId>org.thingsboard.common.transport</groupId>
  29 + <artifactId>snmp</artifactId>
  30 + <packaging>jar</packaging>
  31 +
  32 + <name>Thingsboard SNMP Transport Common</name>
  33 + <url>https://thingsboard.io</url>
  34 +
  35 + <properties>
  36 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  37 + <main.dir>${basedir}/../../..</main.dir>
  38 + </properties>
  39 +
  40 + <dependencies>
  41 + <dependency>
  42 + <groupId>org.thingsboard.common.transport</groupId>
  43 + <artifactId>transport-api</artifactId>
  44 + </dependency>
  45 + <dependency>
  46 + <groupId>org.springframework</groupId>
  47 + <artifactId>spring-context-support</artifactId>
  48 + </dependency>
  49 + <dependency>
  50 + <groupId>org.springframework</groupId>
  51 + <artifactId>spring-context</artifactId>
  52 + </dependency>
  53 + <dependency>
  54 + <groupId>org.slf4j</groupId>
  55 + <artifactId>slf4j-api</artifactId>
  56 + </dependency>
  57 + <dependency>
  58 + <groupId>org.snmp4j</groupId>
  59 + <artifactId>snmp4j</artifactId>
  60 + </dependency>
  61 + <dependency>
  62 + <groupId>org.snmp4j</groupId>
  63 + <artifactId>snmp4j-agent</artifactId>
  64 + <version>3.3.6</version>
  65 + <scope>test</scope>
  66 + </dependency>
  67 + </dependencies>
  68 +</project>
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.RequiredArgsConstructor;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.context.event.EventListener;
  22 +import org.springframework.stereotype.Component;
  23 +import org.thingsboard.server.common.data.Device;
  24 +import org.thingsboard.server.common.data.DeviceProfile;
  25 +import org.thingsboard.server.common.data.DeviceTransportType;
  26 +import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
  27 +import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
  28 +import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
  29 +import org.thingsboard.server.common.data.id.DeviceId;
  30 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  31 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  32 +import org.thingsboard.server.common.data.security.DeviceCredentialsType;
  33 +import org.thingsboard.server.common.transport.DeviceUpdatedEvent;
  34 +import org.thingsboard.server.common.transport.TransportContext;
  35 +import org.thingsboard.server.common.transport.TransportDeviceProfileCache;
  36 +import org.thingsboard.server.common.transport.TransportService;
  37 +import org.thingsboard.server.common.transport.TransportServiceCallback;
  38 +import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
  39 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
  40 +import org.thingsboard.server.gen.transport.TransportProtos;
  41 +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
  42 +import org.thingsboard.server.queue.util.AfterStartUp;
  43 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  44 +import org.thingsboard.server.transport.snmp.service.ProtoTransportEntityService;
  45 +import org.thingsboard.server.transport.snmp.service.SnmpAuthService;
  46 +import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
  47 +import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
  48 +import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
  49 +
  50 +import java.util.Collection;
  51 +import java.util.LinkedList;
  52 +import java.util.List;
  53 +import java.util.Map;
  54 +import java.util.Optional;
  55 +import java.util.UUID;
  56 +import java.util.concurrent.ConcurrentHashMap;
  57 +import java.util.concurrent.ConcurrentLinkedDeque;
  58 +
  59 +@TbSnmpTransportComponent
  60 +@Component
  61 +@Slf4j
  62 +@RequiredArgsConstructor
  63 +public class SnmpTransportContext extends TransportContext {
  64 + @Getter
  65 + private final SnmpTransportService snmpTransportService;
  66 + private final TransportDeviceProfileCache deviceProfileCache;
  67 + private final TransportService transportService;
  68 + private final ProtoTransportEntityService protoEntityService;
  69 + private final SnmpTransportBalancingService balancingService;
  70 + @Getter
  71 + private final SnmpAuthService snmpAuthService;
  72 +
  73 + private final Map<DeviceId, DeviceSessionContext> sessions = new ConcurrentHashMap<>();
  74 + private final Collection<DeviceId> allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
  75 +
  76 + @AfterStartUp(order = 2)
  77 + public void fetchDevicesAndEstablishSessions() {
  78 + log.info("Initializing SNMP devices sessions");
  79 +
  80 + int batchIndex = 0;
  81 + int batchSize = 512;
  82 + boolean nextBatchExists = true;
  83 +
  84 + while (nextBatchExists) {
  85 + TransportProtos.GetSnmpDevicesResponseMsg snmpDevicesResponse = protoEntityService.getSnmpDevicesIds(batchIndex, batchSize);
  86 + snmpDevicesResponse.getIdsList().stream()
  87 + .map(id -> new DeviceId(UUID.fromString(id)))
  88 + .peek(allSnmpDevicesIds::add)
  89 + .filter(deviceId -> balancingService.isManagedByCurrentTransport(deviceId.getId()))
  90 + .map(protoEntityService::getDeviceById)
  91 + .forEach(device -> getExecutor().execute(() -> establishDeviceSession(device)));
  92 +
  93 + nextBatchExists = snmpDevicesResponse.getHasNextPage();
  94 + batchIndex++;
  95 + }
  96 +
  97 + log.debug("Found all SNMP devices ids: {}", allSnmpDevicesIds);
  98 + }
  99 +
  100 + private void establishDeviceSession(Device device) {
  101 + if (device == null) return;
  102 + log.info("Establishing SNMP session for device {}", device.getId());
  103 +
  104 + DeviceProfileId deviceProfileId = device.getDeviceProfileId();
  105 + DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId);
  106 +
  107 + DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
  108 + if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
  109 + log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType());
  110 + return;
  111 + }
  112 +
  113 + SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
  114 + SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
  115 +
  116 + DeviceSessionContext deviceSessionContext;
  117 + try {
  118 + deviceSessionContext = new DeviceSessionContext(
  119 + device, deviceProfile, credentials.getCredentialsId(),
  120 + profileTransportConfiguration, deviceTransportConfiguration, this
  121 + );
  122 + registerSessionMsgListener(deviceSessionContext);
  123 + } catch (Exception e) {
  124 + log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.toString());
  125 + return;
  126 + }
  127 + sessions.put(device.getId(), deviceSessionContext);
  128 + snmpTransportService.createQueryingTasks(deviceSessionContext);
  129 + log.info("Established SNMP device session for device {}", device.getId());
  130 + }
  131 +
  132 + private void updateDeviceSession(DeviceSessionContext sessionContext, Device device, DeviceProfile deviceProfile) {
  133 + log.info("Updating SNMP session for device {}", device.getId());
  134 +
  135 + DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
  136 + if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
  137 + log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType());
  138 + destroyDeviceSession(sessionContext);
  139 + return;
  140 + }
  141 +
  142 + SnmpDeviceProfileTransportConfiguration newProfileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
  143 + SnmpDeviceTransportConfiguration newDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
  144 +
  145 + try {
  146 + if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {
  147 + sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);
  148 + sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
  149 + snmpTransportService.cancelQueryingTasks(sessionContext);
  150 + snmpTransportService.createQueryingTasks(sessionContext);
  151 + } else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
  152 + sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);
  153 + sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
  154 + } else {
  155 + log.trace("Configuration of the device {} was not updated", device);
  156 + }
  157 + } catch (Exception e) {
  158 + log.error("Failed to update session for SNMP device {}: {}", sessionContext.getDeviceId(), e.getMessage());
  159 + destroyDeviceSession(sessionContext);
  160 + }
  161 + }
  162 +
  163 + private void destroyDeviceSession(DeviceSessionContext sessionContext) {
  164 + if (sessionContext == null) return;
  165 + log.info("Destroying SNMP device session for device {}", sessionContext.getDevice().getId());
  166 + sessionContext.close();
  167 + snmpAuthService.cleanUpSnmpAuthInfo(sessionContext);
  168 + transportService.deregisterSession(sessionContext.getSessionInfo());
  169 + snmpTransportService.cancelQueryingTasks(sessionContext);
  170 + sessions.remove(sessionContext.getDeviceId());
  171 + log.trace("Unregistered and removed session");
  172 + }
  173 +
  174 + private void registerSessionMsgListener(DeviceSessionContext deviceSessionContext) {
  175 + transportService.process(DeviceTransportType.SNMP,
  176 + TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceSessionContext.getToken()).build(),
  177 + new TransportServiceCallback<>() {
  178 + @Override
  179 + public void onSuccess(ValidateDeviceCredentialsResponse msg) {
  180 + if (msg.hasDeviceInfo()) {
  181 + SessionInfoProto sessionInfo = SessionInfoCreator.create(
  182 + msg, SnmpTransportContext.this, UUID.randomUUID()
  183 + );
  184 +
  185 + transportService.registerAsyncSession(sessionInfo, deviceSessionContext);
  186 + transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
  187 + transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
  188 +
  189 + deviceSessionContext.setSessionInfo(sessionInfo);
  190 + deviceSessionContext.setDeviceInfo(msg.getDeviceInfo());
  191 + } else {
  192 + log.warn("[{}] Failed to process device auth", deviceSessionContext.getDeviceId());
  193 + }
  194 + }
  195 +
  196 + @Override
  197 + public void onError(Throwable e) {
  198 + log.warn("[{}] Failed to process device auth: {}", deviceSessionContext.getDeviceId(), e);
  199 + }
  200 + });
  201 + }
  202 +
  203 + @EventListener(DeviceUpdatedEvent.class)
  204 + public void onDeviceUpdatedOrCreated(DeviceUpdatedEvent deviceUpdatedEvent) {
  205 + Device device = deviceUpdatedEvent.getDevice();
  206 + log.trace("Got creating or updating device event for device {}", device);
  207 + DeviceTransportType transportType = Optional.ofNullable(device.getDeviceData().getTransportConfiguration())
  208 + .map(DeviceTransportConfiguration::getType)
  209 + .orElse(null);
  210 + if (!allSnmpDevicesIds.contains(device.getId())) {
  211 + if (transportType != DeviceTransportType.SNMP) {
  212 + return;
  213 + }
  214 + allSnmpDevicesIds.add(device.getId());
  215 + if (balancingService.isManagedByCurrentTransport(device.getId().getId())) {
  216 + establishDeviceSession(device);
  217 + }
  218 + } else {
  219 + if (balancingService.isManagedByCurrentTransport(device.getId().getId())) {
  220 + DeviceSessionContext sessionContext = sessions.get(device.getId());
  221 + if (transportType == DeviceTransportType.SNMP) {
  222 + if (sessionContext != null) {
  223 + updateDeviceSession(sessionContext, device, deviceProfileCache.get(device.getDeviceProfileId()));
  224 + } else {
  225 + establishDeviceSession(device);
  226 + }
  227 + } else {
  228 + log.trace("Transport type was changed to {}", transportType);
  229 + destroyDeviceSession(sessionContext);
  230 + }
  231 + }
  232 + }
  233 + }
  234 +
  235 + public void onDeviceDeleted(DeviceSessionContext sessionContext) {
  236 + destroyDeviceSession(sessionContext);
  237 + }
  238 +
  239 + public void onDeviceProfileUpdated(DeviceProfile deviceProfile, DeviceSessionContext sessionContext) {
  240 + updateDeviceSession(sessionContext, sessionContext.getDevice(), deviceProfile);
  241 + }
  242 +
  243 + public void onSnmpTransportListChanged() {
  244 + log.trace("SNMP transport list changed. Updating sessions");
  245 + List<DeviceId> deleted = new LinkedList<>();
  246 + for (DeviceId deviceId : allSnmpDevicesIds) {
  247 + if (balancingService.isManagedByCurrentTransport(deviceId.getId())) {
  248 + if (!sessions.containsKey(deviceId)) {
  249 + Device device = protoEntityService.getDeviceById(deviceId);
  250 + if (device != null) {
  251 + log.info("SNMP device {} is now managed by current transport node", deviceId);
  252 + establishDeviceSession(device);
  253 + } else {
  254 + deleted.add(deviceId);
  255 + }
  256 + }
  257 + } else {
  258 + Optional.ofNullable(sessions.get(deviceId))
  259 + .ifPresent(sessionContext -> {
  260 + log.info("SNMP session for device {} is not managed by current transport node anymore", deviceId);
  261 + destroyDeviceSession(sessionContext);
  262 + });
  263 + }
  264 + }
  265 + log.trace("Removing deleted SNMP devices: {}", deleted);
  266 + allSnmpDevicesIds.removeAll(deleted);
  267 + }
  268 +
  269 +
  270 + public Collection<DeviceSessionContext> getSessions() {
  271 + return sessions.values();
  272 + }
  273 +
  274 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.event;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import org.springframework.stereotype.Component;
  20 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
  21 +import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
  22 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  23 +import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
  24 +
  25 +@TbSnmpTransportComponent
  26 +@Component
  27 +@RequiredArgsConstructor
  28 +public class ServiceListChangedEventListener extends TbApplicationEventListener<ServiceListChangedEvent> {
  29 + private final SnmpTransportBalancingService snmpTransportBalancingService;
  30 +
  31 + @Override
  32 + protected void onTbApplicationEvent(ServiceListChangedEvent event) {
  33 + snmpTransportBalancingService.onServiceListChanged(event);
  34 + }
  35 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.event;
  17 +
  18 +import org.thingsboard.server.queue.discovery.event.TbApplicationEvent;
  19 +
  20 +public class SnmpTransportListChangedEvent extends TbApplicationEvent {
  21 + public SnmpTransportListChangedEvent() {
  22 + super(new Object());
  23 + }
  24 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.event;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import org.springframework.stereotype.Component;
  20 +import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
  21 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  22 +import org.thingsboard.server.transport.snmp.SnmpTransportContext;
  23 +
  24 +@TbSnmpTransportComponent
  25 +@Component
  26 +@RequiredArgsConstructor
  27 +public class SnmpTransportListChangedEventListener extends TbApplicationEventListener<SnmpTransportListChangedEvent> {
  28 + private final SnmpTransportContext snmpTransportContext;
  29 +
  30 + @Override
  31 + protected void onTbApplicationEvent(SnmpTransportListChangedEvent event) {
  32 + snmpTransportContext.onSnmpTransportListChanged();
  33 + }
  34 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import com.google.gson.JsonObject;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.snmp4j.PDU;
  21 +import org.snmp4j.ScopedPDU;
  22 +import org.snmp4j.smi.Integer32;
  23 +import org.snmp4j.smi.Null;
  24 +import org.snmp4j.smi.OID;
  25 +import org.snmp4j.smi.OctetString;
  26 +import org.snmp4j.smi.Variable;
  27 +import org.snmp4j.smi.VariableBinding;
  28 +import org.springframework.stereotype.Service;
  29 +import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
  30 +import org.thingsboard.server.common.data.kv.DataType;
  31 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  32 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  33 +import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
  34 +import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
  35 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  36 +import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
  37 +
  38 +import java.util.HashMap;
  39 +import java.util.List;
  40 +import java.util.Map;
  41 +import java.util.Objects;
  42 +import java.util.Optional;
  43 +import java.util.stream.Collectors;
  44 +import java.util.stream.IntStream;
  45 +
  46 +@TbSnmpTransportComponent
  47 +@Service
  48 +@Slf4j
  49 +public class PduService {
  50 + public PDU createPdu(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
  51 + PDU pdu = setUpPdu(sessionContext);
  52 +
  53 + pdu.setType(communicationConfig.getMethod().getCode());
  54 + pdu.addAll(communicationConfig.getAllMappings().stream()
  55 + .filter(mapping -> values.isEmpty() || values.containsKey(mapping.getKey()))
  56 + .map(mapping -> Optional.ofNullable(values.get(mapping.getKey()))
  57 + .map(value -> {
  58 + Variable variable = toSnmpVariable(value, mapping.getDataType());
  59 + return new VariableBinding(new OID(mapping.getOid()), variable);
  60 + })
  61 + .orElseGet(() -> new VariableBinding(new OID(mapping.getOid()))))
  62 + .collect(Collectors.toList()));
  63 +
  64 + return pdu;
  65 + }
  66 +
  67 + public PDU createSingleVariablePdu(DeviceSessionContext sessionContext, SnmpMethod snmpMethod, String oid, String value, DataType dataType) {
  68 + PDU pdu = setUpPdu(sessionContext);
  69 + pdu.setType(snmpMethod.getCode());
  70 +
  71 + Variable variable = value == null ? Null.instance : toSnmpVariable(value, dataType);
  72 + pdu.add(new VariableBinding(new OID(oid), variable));
  73 +
  74 + return pdu;
  75 + }
  76 +
  77 + private Variable toSnmpVariable(String value, DataType dataType) {
  78 + dataType = dataType == null ? DataType.STRING : dataType;
  79 + Variable variable;
  80 + switch (dataType) {
  81 + case LONG:
  82 + try {
  83 + variable = new Integer32(Integer.parseInt(value));
  84 + break;
  85 + } catch (NumberFormatException ignored) {
  86 + }
  87 + case DOUBLE:
  88 + case BOOLEAN:
  89 + case STRING:
  90 + case JSON:
  91 + default:
  92 + variable = new OctetString(value);
  93 + }
  94 + return variable;
  95 + }
  96 +
  97 + private PDU setUpPdu(DeviceSessionContext sessionContext) {
  98 + PDU pdu;
  99 + SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
  100 + SnmpProtocolVersion snmpVersion = deviceTransportConfiguration.getProtocolVersion();
  101 + switch (snmpVersion) {
  102 + case V1:
  103 + case V2C:
  104 + pdu = new PDU();
  105 + break;
  106 + case V3:
  107 + ScopedPDU scopedPdu = new ScopedPDU();
  108 + scopedPdu.setContextName(new OctetString(deviceTransportConfiguration.getContextName()));
  109 + scopedPdu.setContextEngineID(new OctetString(deviceTransportConfiguration.getEngineId()));
  110 + pdu = scopedPdu;
  111 + break;
  112 + default:
  113 + throw new UnsupportedOperationException("SNMP version " + snmpVersion + " is not supported");
  114 + }
  115 + return pdu;
  116 + }
  117 +
  118 +
  119 + public JsonObject processPdu(PDU pdu, List<SnmpMapping> responseMappings) {
  120 + Map<OID, String> values = processPdu(pdu);
  121 +
  122 + Map<OID, SnmpMapping> mappings = new HashMap<>();
  123 + if (responseMappings != null) {
  124 + for (SnmpMapping mapping : responseMappings) {
  125 + OID oid = new OID(mapping.getOid());
  126 + mappings.put(oid, mapping);
  127 + }
  128 + }
  129 +
  130 + JsonObject data = new JsonObject();
  131 + values.forEach((oid, value) -> {
  132 + log.trace("Processing variable binding: {} - {}", oid, value);
  133 +
  134 + SnmpMapping mapping = mappings.get(oid);
  135 + if (mapping == null) {
  136 + log.debug("No SNMP mapping for oid {}", oid);
  137 + return;
  138 + }
  139 +
  140 + processValue(mapping.getKey(), mapping.getDataType(), value, data);
  141 + });
  142 +
  143 + return data;
  144 + }
  145 +
  146 + public Map<OID, String> processPdu(PDU pdu) {
  147 + return IntStream.range(0, pdu.size())
  148 + .mapToObj(pdu::get)
  149 + .filter(Objects::nonNull)
  150 + .filter(variableBinding -> !(variableBinding.getVariable() instanceof Null))
  151 + .collect(Collectors.toMap(VariableBinding::getOid, VariableBinding::toValueString));
  152 + }
  153 +
  154 + public void processValue(String key, DataType dataType, String value, JsonObject result) {
  155 + switch (dataType) {
  156 + case LONG:
  157 + result.addProperty(key, Long.parseLong(value));
  158 + break;
  159 + case BOOLEAN:
  160 + result.addProperty(key, Boolean.parseBoolean(value));
  161 + break;
  162 + case DOUBLE:
  163 + result.addProperty(key, Double.parseDouble(value));
  164 + break;
  165 + case STRING:
  166 + case JSON:
  167 + default:
  168 + result.addProperty(key, value);
  169 + }
  170 + }
  171 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import org.springframework.stereotype.Service;
  20 +import org.thingsboard.server.common.data.Device;
  21 +import org.thingsboard.server.common.data.device.data.DeviceData;
  22 +import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
  23 +import org.thingsboard.server.common.data.id.DeviceId;
  24 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  25 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  26 +import org.thingsboard.server.common.transport.TransportService;
  27 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
  28 +import org.thingsboard.server.gen.transport.TransportProtos;
  29 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  30 +
  31 +import java.util.ArrayList;
  32 +import java.util.List;
  33 +import java.util.UUID;
  34 +import java.util.stream.Collectors;
  35 +
  36 +@TbSnmpTransportComponent
  37 +@Service
  38 +@RequiredArgsConstructor
  39 +public class ProtoTransportEntityService {
  40 + private final TransportService transportService;
  41 + private final DataDecodingEncodingService dataDecodingEncodingService;
  42 +
  43 + public Device getDeviceById(DeviceId id) {
  44 + TransportProtos.GetDeviceResponseMsg deviceProto = transportService.getDevice(TransportProtos.GetDeviceRequestMsg.newBuilder()
  45 + .setDeviceIdMSB(id.getId().getMostSignificantBits())
  46 + .setDeviceIdLSB(id.getId().getLeastSignificantBits())
  47 + .build());
  48 +
  49 + if (deviceProto == null) {
  50 + return null;
  51 + }
  52 +
  53 + DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(
  54 + deviceProto.getDeviceProfileIdMSB(), deviceProto.getDeviceProfileIdLSB())
  55 + );
  56 +
  57 + Device device = new Device();
  58 + device.setId(id);
  59 + device.setDeviceProfileId(deviceProfileId);
  60 +
  61 + DeviceTransportConfiguration deviceTransportConfiguration = (DeviceTransportConfiguration) dataDecodingEncodingService.decode(
  62 + deviceProto.getDeviceTransportConfiguration().toByteArray()
  63 + ).orElseThrow(() -> new IllegalStateException("Can't find device transport configuration"));
  64 +
  65 + DeviceData deviceData = new DeviceData();
  66 + deviceData.setTransportConfiguration(deviceTransportConfiguration);
  67 + device.setDeviceData(deviceData);
  68 +
  69 + return device;
  70 + }
  71 +
  72 + public DeviceCredentials getDeviceCredentialsByDeviceId(DeviceId deviceId) {
  73 + TransportProtos.GetDeviceCredentialsResponseMsg deviceCredentialsResponse = transportService.getDeviceCredentials(
  74 + TransportProtos.GetDeviceCredentialsRequestMsg.newBuilder()
  75 + .setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
  76 + .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
  77 + .build()
  78 + );
  79 +
  80 + return (DeviceCredentials) dataDecodingEncodingService.decode(deviceCredentialsResponse.getDeviceCredentialsData().toByteArray())
  81 + .orElseThrow(() -> new IllegalArgumentException("Device credentials not found"));
  82 + }
  83 +
  84 + public TransportProtos.GetSnmpDevicesResponseMsg getSnmpDevicesIds(int page, int pageSize) {
  85 + TransportProtos.GetSnmpDevicesRequestMsg requestMsg = TransportProtos.GetSnmpDevicesRequestMsg.newBuilder()
  86 + .setPage(page)
  87 + .setPageSize(pageSize)
  88 + .build();
  89 + return transportService.getSnmpDevicesIds(requestMsg);
  90 + }
  91 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import org.snmp4j.AbstractTarget;
  20 +import org.snmp4j.CommunityTarget;
  21 +import org.snmp4j.Target;
  22 +import org.snmp4j.UserTarget;
  23 +import org.snmp4j.security.SecurityLevel;
  24 +import org.snmp4j.security.SecurityModel;
  25 +import org.snmp4j.security.SecurityProtocols;
  26 +import org.snmp4j.security.USM;
  27 +import org.snmp4j.smi.Address;
  28 +import org.snmp4j.smi.GenericAddress;
  29 +import org.snmp4j.smi.OID;
  30 +import org.snmp4j.smi.OctetString;
  31 +import org.springframework.beans.factory.annotation.Value;
  32 +import org.springframework.stereotype.Service;
  33 +import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
  34 +import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
  35 +import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
  36 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  37 +import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
  38 +import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
  39 +
  40 +import java.util.Optional;
  41 +
  42 +@Service
  43 +@TbSnmpTransportComponent
  44 +@RequiredArgsConstructor
  45 +public class SnmpAuthService {
  46 + private final SnmpTransportService snmpTransportService;
  47 +
  48 + @Value("${transport.snmp.underlying_protocol}")
  49 + private String snmpUnderlyingProtocol;
  50 +
  51 + public Target setUpSnmpTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
  52 + AbstractTarget target;
  53 +
  54 + SnmpProtocolVersion protocolVersion = deviceTransportConfig.getProtocolVersion();
  55 + switch (protocolVersion) {
  56 + case V1:
  57 + CommunityTarget communityTargetV1 = new CommunityTarget();
  58 + communityTargetV1.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv1);
  59 + communityTargetV1.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
  60 + communityTargetV1.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
  61 + target = communityTargetV1;
  62 + break;
  63 + case V2C:
  64 + CommunityTarget communityTargetV2 = new CommunityTarget();
  65 + communityTargetV2.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
  66 + communityTargetV2.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
  67 + communityTargetV2.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
  68 + target = communityTargetV2;
  69 + break;
  70 + case V3:
  71 + OctetString username = new OctetString(deviceTransportConfig.getUsername());
  72 + OctetString securityName = new OctetString(deviceTransportConfig.getSecurityName());
  73 + OctetString engineId = new OctetString(deviceTransportConfig.getEngineId());
  74 +
  75 + OID authenticationProtocol = new OID(deviceTransportConfig.getAuthenticationProtocol().getOid());
  76 + OID privacyProtocol = new OID(deviceTransportConfig.getPrivacyProtocol().getOid());
  77 + OctetString authenticationPassphrase = new OctetString(deviceTransportConfig.getAuthenticationPassphrase());
  78 + authenticationPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(authenticationProtocol, authenticationPassphrase, engineId.getValue()));
  79 + OctetString privacyPassphrase = new OctetString(deviceTransportConfig.getPrivacyPassphrase());
  80 + privacyPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(privacyProtocol, authenticationProtocol, privacyPassphrase, engineId.getValue()));
  81 +
  82 + USM usm = snmpTransportService.getSnmp().getUSM();
  83 + if (usm.hasUser(engineId, securityName)) {
  84 + usm.removeAllUsers(username, engineId);
  85 + }
  86 + usm.addLocalizedUser(
  87 + engineId.getValue(), username,
  88 + authenticationProtocol, authenticationPassphrase.getValue(),
  89 + privacyProtocol, privacyPassphrase.getValue()
  90 + );
  91 +
  92 + UserTarget userTarget = new UserTarget();
  93 + userTarget.setSecurityName(securityName);
  94 + userTarget.setAuthoritativeEngineID(engineId.getValue());
  95 + userTarget.setSecurityModel(SecurityModel.SECURITY_MODEL_USM);
  96 + userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
  97 + target = userTarget;
  98 + break;
  99 + default:
  100 + throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");
  101 + }
  102 +
  103 + Address address = GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getHost() + "/" + deviceTransportConfig.getPort());
  104 + target.setAddress(Optional.ofNullable(address).orElseThrow(() -> new IllegalArgumentException("Address of the SNMP device is invalid")));
  105 + target.setTimeout(profileTransportConfig.getTimeoutMs());
  106 + target.setRetries(profileTransportConfig.getRetries());
  107 + target.setVersion(protocolVersion.getCode());
  108 +
  109 + return target;
  110 + }
  111 +
  112 + public void cleanUpSnmpAuthInfo(DeviceSessionContext sessionContext) {
  113 + SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
  114 + if (deviceTransportConfiguration.getProtocolVersion() == SnmpProtocolVersion.V3) {
  115 + OctetString username = new OctetString(deviceTransportConfiguration.getUsername());
  116 + OctetString engineId = new OctetString(deviceTransportConfiguration.getEngineId());
  117 + snmpTransportService.getSnmp().getUSM().removeAllUsers(username, engineId);
  118 + }
  119 + }
  120 +
  121 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.context.ApplicationEventPublisher;
  21 +import org.springframework.stereotype.Service;
  22 +import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
  23 +import org.thingsboard.server.queue.discovery.PartitionService;
  24 +import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
  25 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  26 +import org.thingsboard.server.transport.snmp.event.SnmpTransportListChangedEvent;
  27 +
  28 +import java.util.Comparator;
  29 +import java.util.List;
  30 +import java.util.UUID;
  31 +import java.util.stream.Collectors;
  32 +import java.util.stream.Stream;
  33 +
  34 +@TbSnmpTransportComponent
  35 +@Service
  36 +@RequiredArgsConstructor
  37 +@Slf4j
  38 +public class SnmpTransportBalancingService {
  39 + private final PartitionService partitionService;
  40 + private final ApplicationEventPublisher eventPublisher;
  41 + private final SnmpTransportService snmpTransportService;
  42 +
  43 + private int snmpTransportsCount = 1;
  44 + private Integer currentTransportPartitionIndex = 0;
  45 +
  46 + public void onServiceListChanged(ServiceListChangedEvent event) {
  47 + log.trace("Got service list changed event: {}", event);
  48 + recalculatePartitions(event.getOtherServices(), event.getCurrentService());
  49 + }
  50 +
  51 + public boolean isManagedByCurrentTransport(UUID entityId) {
  52 + boolean isManaged = resolvePartitionIndexForEntity(entityId) == currentTransportPartitionIndex;
  53 + if (!isManaged) {
  54 + log.trace("Entity {} is not managed by current SNMP transport node", entityId);
  55 + }
  56 + return isManaged;
  57 + }
  58 +
  59 + private int resolvePartitionIndexForEntity(UUID entityId) {
  60 + return partitionService.resolvePartitionIndex(entityId, snmpTransportsCount);
  61 + }
  62 +
  63 + private void recalculatePartitions(List<ServiceInfo> otherServices, ServiceInfo currentService) {
  64 + log.info("Recalculating partitions for SNMP transports");
  65 + List<ServiceInfo> snmpTransports = Stream.concat(otherServices.stream(), Stream.of(currentService))
  66 + .filter(service -> service.getTransportsList().contains(snmpTransportService.getName()))
  67 + .sorted(Comparator.comparing(ServiceInfo::getServiceId))
  68 + .collect(Collectors.toList());
  69 + log.trace("Found SNMP transports: {}", snmpTransports);
  70 +
  71 + int previousCurrentTransportPartitionIndex = currentTransportPartitionIndex;
  72 + int previousSnmpTransportsCount = snmpTransportsCount;
  73 +
  74 + if (!snmpTransports.isEmpty()) {
  75 + for (int i = 0; i < snmpTransports.size(); i++) {
  76 + if (snmpTransports.get(i).equals(currentService)) {
  77 + currentTransportPartitionIndex = i;
  78 + break;
  79 + }
  80 + }
  81 + snmpTransportsCount = snmpTransports.size();
  82 + }
  83 +
  84 + if (snmpTransportsCount != previousSnmpTransportsCount || currentTransportPartitionIndex != previousCurrentTransportPartitionIndex) {
  85 + log.info("SNMP transports partitions have changed: transports count = {}, current transport partition index = {}", snmpTransportsCount, currentTransportPartitionIndex);
  86 + eventPublisher.publishEvent(new SnmpTransportListChangedEvent());
  87 + } else {
  88 + log.info("SNMP transports partitions have not changed");
  89 + }
  90 + }
  91 +
  92 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.snmp.service;
  17 +
  18 +import com.google.gson.JsonElement;
  19 +import com.google.gson.JsonObject;
  20 +import lombok.Data;
  21 +import lombok.Getter;
  22 +import lombok.RequiredArgsConstructor;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.snmp4j.PDU;
  25 +import org.snmp4j.Snmp;
  26 +import org.snmp4j.TransportMapping;
  27 +import org.snmp4j.event.ResponseEvent;
  28 +import org.snmp4j.mp.MPv3;
  29 +import org.snmp4j.security.SecurityModels;
  30 +import org.snmp4j.security.SecurityProtocols;
  31 +import org.snmp4j.security.USM;
  32 +import org.snmp4j.smi.OctetString;
  33 +import org.snmp4j.transport.DefaultTcpTransportMapping;
  34 +import org.snmp4j.transport.DefaultUdpTransportMapping;
  35 +import org.springframework.beans.factory.annotation.Value;
  36 +import org.springframework.stereotype.Service;
  37 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
  38 +import org.thingsboard.server.common.data.TbTransportService;
  39 +import org.thingsboard.server.common.data.kv.DataType;
  40 +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
  41 +import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
  42 +import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
  43 +import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
  44 +import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
  45 +import org.thingsboard.server.common.transport.TransportService;
  46 +import org.thingsboard.server.common.transport.TransportServiceCallback;
  47 +import org.thingsboard.server.common.transport.adaptor.JsonConverter;
  48 +import org.thingsboard.server.gen.transport.TransportProtos;
  49 +import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
  50 +import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
  51 +
  52 +import javax.annotation.PostConstruct;
  53 +import javax.annotation.PreDestroy;
  54 +import java.io.IOException;
  55 +import java.util.Arrays;
  56 +import java.util.Collections;
  57 +import java.util.EnumMap;
  58 +import java.util.List;
  59 +import java.util.Map;
  60 +import java.util.Optional;
  61 +import java.util.concurrent.ExecutorService;
  62 +import java.util.concurrent.Executors;
  63 +import java.util.concurrent.ScheduledExecutorService;
  64 +import java.util.concurrent.ScheduledFuture;
  65 +import java.util.concurrent.TimeUnit;
  66 +import java.util.stream.Collectors;
  67 +
  68 +@TbSnmpTransportComponent
  69 +@Service
  70 +@Slf4j
  71 +@RequiredArgsConstructor
  72 +public class SnmpTransportService implements TbTransportService {
  73 + private final TransportService transportService;
  74 + private final PduService pduService;
  75 +
  76 + @Getter
  77 + private Snmp snmp;
  78 + private ScheduledExecutorService queryingExecutor;
  79 + private ExecutorService responseProcessingExecutor;
  80 +
  81 + private final Map<SnmpCommunicationSpec, ResponseDataMapper> responseDataMappers = new EnumMap<>(SnmpCommunicationSpec.class);
  82 + private final Map<SnmpCommunicationSpec, ResponseProcessor> responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class);
  83 +
  84 + @Value("${transport.snmp.response_processing.parallelism_level}")
  85 + private Integer responseProcessingParallelismLevel;
  86 + @Value("${transport.snmp.underlying_protocol}")
  87 + private String snmpUnderlyingProtocol;
  88 +
  89 + @PostConstruct
  90 + private void init() throws IOException {
  91 + queryingExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("snmp-querying"));
  92 + responseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel);
  93 +
  94 + initializeSnmp();
  95 + configureResponseDataMappers();
  96 + configureResponseProcessors();
  97 +
  98 + log.info("SNMP transport service initialized");
  99 + }
  100 +
  101 + private void initializeSnmp() throws IOException {
  102 + TransportMapping<?> transportMapping;
  103 + switch (snmpUnderlyingProtocol) {
  104 + case "udp":
  105 + transportMapping = new DefaultUdpTransportMapping();
  106 + break;
  107 + case "tcp":
  108 + transportMapping = new DefaultTcpTransportMapping();
  109 + break;
  110 + default:
  111 + throw new IllegalArgumentException("Underlying protocol " + snmpUnderlyingProtocol + " for SNMP is not supported");
  112 + }
  113 + snmp = new Snmp(transportMapping);
  114 + snmp.listen();
  115 +
  116 + USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
  117 + SecurityModels.getInstance().addSecurityModel(usm);
  118 + }
  119 +
  120 + public void createQueryingTasks(DeviceSessionContext sessionContext) {
  121 + List<ScheduledFuture<?>> queryingTasks = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
  122 + .filter(communicationConfig -> communicationConfig instanceof RepeatingQueryingSnmpCommunicationConfig)
  123 + .map(config -> {
  124 + RepeatingQueryingSnmpCommunicationConfig repeatingCommunicationConfig = (RepeatingQueryingSnmpCommunicationConfig) config;
  125 + Long queryingFrequency = repeatingCommunicationConfig.getQueryingFrequencyMs();
  126 +
  127 + return queryingExecutor.scheduleWithFixedDelay(() -> {
  128 + try {
  129 + if (sessionContext.isActive()) {
  130 + sendRequest(sessionContext, repeatingCommunicationConfig);
  131 + }
  132 + } catch (Exception e) {
  133 + log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.toString());
  134 + }
  135 + }, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS);
  136 + })
  137 + .collect(Collectors.toList());
  138 + sessionContext.getQueryingTasks().addAll(queryingTasks);
  139 + }
  140 +
  141 + public void cancelQueryingTasks(DeviceSessionContext sessionContext) {
  142 + sessionContext.getQueryingTasks().forEach(task -> task.cancel(true));
  143 + sessionContext.getQueryingTasks().clear();
  144 + }
  145 +
  146 +
  147 + private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) {
  148 + sendRequest(sessionContext, communicationConfig, Collections.emptyMap());
  149 + }
  150 +
  151 + private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
  152 + PDU request = pduService.createPdu(sessionContext, communicationConfig, values);
  153 + RequestInfo requestInfo = new RequestInfo(communicationConfig.getSpec(), communicationConfig.getAllMappings());
  154 + sendRequest(sessionContext, request, requestInfo);
  155 + }
  156 +
  157 + private void sendRequest(DeviceSessionContext sessionContext, PDU request, RequestInfo requestInfo) {
  158 + if (request.size() > 0) {
  159 + log.trace("Executing SNMP request for device {}. Variables bindings: {}", sessionContext.getDeviceId(), request.getVariableBindings());
  160 + try {
  161 + snmp.send(request, sessionContext.getTarget(), requestInfo, sessionContext);
  162 + } catch (IOException e) {
  163 + log.error("Failed to send SNMP request to device {}: {}", sessionContext.getDeviceId(), e.toString());
  164 + }
  165 + }
  166 + }
  167 +
  168 + public void onAttributeUpdate(DeviceSessionContext sessionContext, TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification) {
  169 + sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
  170 + .filter(config -> config.getSpec() == SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING)
  171 + .findFirst()
  172 + .ifPresent(communicationConfig -> {
  173 + Map<String, String> sharedAttributes = JsonConverter.toJson(attributeUpdateNotification).entrySet().stream()
  174 + .collect(Collectors.toMap(
  175 + Map.Entry::getKey,
  176 + entry -> entry.getValue().isJsonPrimitive() ? entry.getValue().getAsString() : entry.getValue().toString()
  177 + ));
  178 + sendRequest(sessionContext, communicationConfig, sharedAttributes);
  179 + });
  180 + }
  181 +
  182 + public void onToDeviceRpcRequest(DeviceSessionContext sessionContext, TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg) {
  183 + SnmpMethod snmpMethod = SnmpMethod.valueOf(toDeviceRpcRequestMsg.getMethodName());
  184 + JsonObject params = JsonConverter.parse(toDeviceRpcRequestMsg.getParams()).getAsJsonObject();
  185 +
  186 + String key = Optional.ofNullable(params.get("key")).map(JsonElement::getAsString).orElse(null);
  187 + String value = Optional.ofNullable(params.get("value")).map(JsonElement::getAsString).orElse(null);
  188 +
  189 + if (value == null && snmpMethod == SnmpMethod.SET) {
  190 + throw new IllegalArgumentException("Value must be specified for SNMP method 'SET'");
  191 + }
  192 +
  193 + SnmpCommunicationConfig communicationConfig = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
  194 + .filter(config -> config.getSpec() == SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST)
  195 + .findFirst()
  196 + .orElseThrow(() -> new IllegalArgumentException("No communication config found with RPC spec"));
  197 + SnmpMapping snmpMapping = communicationConfig.getAllMappings().stream()
  198 + .filter(mapping -> mapping.getKey().equals(key))
  199 + .findFirst()
  200 + .orElseThrow(() -> new IllegalArgumentException("No SNMP mapping found in the config for specified key"));
  201 +
  202 + String oid = snmpMapping.getOid();
  203 + DataType dataType = snmpMapping.getDataType();
  204 +
  205 + PDU request = pduService.createSingleVariablePdu(sessionContext, snmpMethod, oid, value, dataType);
  206 + RequestInfo requestInfo = new RequestInfo(toDeviceRpcRequestMsg.getRequestId(), communicationConfig.getSpec(), communicationConfig.getAllMappings());
  207 + sendRequest(sessionContext, request, requestInfo);
  208 + }
  209 +
  210 +
  211 + public void processResponseEvent(DeviceSessionContext sessionContext, ResponseEvent event) {
  212 + ((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext);
  213 +
  214 + if (event.getError() != null) {
  215 + log.warn("SNMP response error: {}", event.getError().toString());
  216 + return;
  217 + }
  218 +
  219 + PDU response = event.getResponse();
  220 + if (response == null) {
  221 + log.debug("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID());
  222 + return;
  223 + }
  224 +
  225 + RequestInfo requestInfo = (RequestInfo) event.getUserObject();
  226 + responseProcessingExecutor.execute(() -> {
  227 + processResponse(sessionContext, response, requestInfo);
  228 + });
  229 + }
  230 +
  231 + private void processResponse(DeviceSessionContext sessionContext, PDU response, RequestInfo requestInfo) {
  232 + ResponseProcessor responseProcessor = responseProcessors.get(requestInfo.getCommunicationSpec());
  233 + if (responseProcessor == null) return;
  234 +
  235 + JsonObject responseData = responseDataMappers.get(requestInfo.getCommunicationSpec()).map(response, requestInfo);
  236 +
  237 + if (responseData.entrySet().isEmpty()) {
  238 + log.debug("No values is the SNMP response for device {}. Request id: {}", sessionContext.getDeviceId(), response.getRequestID());
  239 + return;
  240 + }
  241 +
  242 + responseProcessor.process(responseData, requestInfo, sessionContext);
  243 + reportActivity(sessionContext.getSessionInfo());
  244 + }
  245 +
  246 + private void configureResponseDataMappers() {
  247 + responseDataMappers.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (pdu, requestInfo) -> {
  248 + JsonObject responseData = new JsonObject();
  249 + pduService.processPdu(pdu).forEach((oid, value) -> {
  250 + requestInfo.getResponseMappings().stream()
  251 + .filter(snmpMapping -> snmpMapping.getOid().equals(oid.toDottedString()))
  252 + .findFirst()
  253 + .ifPresent(snmpMapping -> {
  254 + pduService.processValue(snmpMapping.getKey(), snmpMapping.getDataType(), value, responseData);
  255 + });
  256 + });
  257 + return responseData;
  258 + });
  259 +
  260 + ResponseDataMapper defaultResponseDataMapper = (pdu, requestInfo) -> {
  261 + return pduService.processPdu(pdu, requestInfo.getResponseMappings());
  262 + };
  263 + Arrays.stream(SnmpCommunicationSpec.values())
  264 + .forEach(communicationSpec -> {
  265 + responseDataMappers.putIfAbsent(communicationSpec, defaultResponseDataMapper);
  266 + });
  267 + }
  268 +
  269 + private void configureResponseProcessors() {
  270 + responseProcessors.put(SnmpCommunicationSpec.TELEMETRY_QUERYING, (responseData, requestInfo, sessionContext) -> {
  271 + TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(responseData);
  272 + transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, null);
  273 + log.debug("Posted telemetry for SNMP device {}: {}", sessionContext.getDeviceId(), responseData);
  274 + });
  275 +
  276 + responseProcessors.put(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, (responseData, requestInfo, sessionContext) -> {
  277 + TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(responseData);
  278 + transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, null);
  279 + log.debug("Posted attributes for SNMP device {}: {}", sessionContext.getDeviceId(), responseData);
  280 + });
  281 +
  282 + responseProcessors.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (responseData, requestInfo, sessionContext) -> {
  283 + TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder()
  284 + .setRequestId(requestInfo.getRequestId())
  285 + .setPayload(JsonConverter.toJson(responseData))
  286 + .build();
  287 + transportService.process(sessionContext.getSessionInfo(), rpcResponseMsg, null);
  288 + log.debug("Posted RPC response {} for device {}", responseData, sessionContext.getDeviceId());
  289 + });
  290 + }
  291 +
  292 + private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
  293 + transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
  294 + .setAttributeSubscription(true)
  295 + .setRpcSubscription(true)
  296 + .setLastActivityTime(System.currentTimeMillis())
  297 + .build(), TransportServiceCallback.EMPTY);
  298 + }
  299 +
  300 +
  301 + @Override
  302 + public String getName() {
  303 + return "SNMP";
  304 + }
  305 +
  306 + @PreDestroy
  307 + public void shutdown() {
  308 + log.info("Stopping SNMP transport!");
  309 + if (queryingExecutor != null) {
  310 + queryingExecutor.shutdownNow();
  311 + }
  312 + if (responseProcessingExecutor != null) {
  313 + responseProcessingExecutor.shutdownNow();
  314 + }
  315 + if (snmp != null) {
  316 + try {
  317 + snmp.close();
  318 + } catch (IOException e) {
  319 + log.error(e.getMessage(), e);
  320 + }
  321 + }
  322 + log.info("SNMP transport stopped!");
  323 + }
  324 +
  325 + @Data
  326 + private static class RequestInfo {
  327 + private Integer requestId;
  328 + private SnmpCommunicationSpec communicationSpec;
  329 + private List<SnmpMapping> responseMappings;
  330 +
  331 + public RequestInfo(Integer requestId, SnmpCommunicationSpec communicationSpec, List<SnmpMapping> responseMappings) {
  332 + this.requestId = requestId;
  333 + this.communicationSpec = communicationSpec;
  334 + this.responseMappings = responseMappings;
  335 + }
  336 +
  337 + public RequestInfo(SnmpCommunicationSpec communicationSpec, List<SnmpMapping> responseMappings) {
  338 + this.communicationSpec = communicationSpec;
  339 + this.responseMappings = responseMappings;
  340 + }
  341 + }
  342 +
  343 + private interface ResponseDataMapper {
  344 + JsonObject map(PDU pdu, RequestInfo requestInfo);
  345 + }
  346 +
  347 + private interface ResponseProcessor {
  348 + void process(JsonObject responseData, RequestInfo requestInfo, DeviceSessionContext sessionContext);
  349 + }
  350 +
  351 +}