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 90 <artifactId>lwm2m</artifactId>
91 91 </dependency>
92 92 <dependency>
  93 + <groupId>org.thingsboard.common.transport</groupId>
  94 + <artifactId>snmp</artifactId>
  95 + </dependency>
  96 + <dependency>
93 97 <groupId>org.thingsboard</groupId>
94 98 <artifactId>dao</artifactId>
95 99 </dependency>
... ...
... ... @@ -22,10 +22,10 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
22 22 import org.springframework.context.event.EventListener;
23 23 import org.springframework.core.annotation.Order;
24 24 import org.springframework.stereotype.Service;
  25 +import org.thingsboard.common.util.ThingsBoardExecutors;
25 26 import org.thingsboard.common.util.ThingsBoardThreadFactory;
26 27 import org.thingsboard.server.actors.ActorSystemContext;
27 28 import org.thingsboard.server.actors.DefaultTbActorSystem;
28   -import org.thingsboard.server.actors.TbActorId;
29 29 import org.thingsboard.server.actors.TbActorRef;
30 30 import org.thingsboard.server.actors.TbActorSystem;
31 31 import org.thingsboard.server.actors.TbActorSystemSettings;
... ... @@ -33,14 +33,13 @@ import org.thingsboard.server.actors.app.AppActor;
33 33 import org.thingsboard.server.actors.app.AppInitMsg;
34 34 import org.thingsboard.server.actors.stats.StatsActor;
35 35 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
36   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
37 36 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
  37 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
38 38
39 39 import javax.annotation.PostConstruct;
40 40 import javax.annotation.PreDestroy;
41 41 import java.util.concurrent.ExecutorService;
42 42 import java.util.concurrent.Executors;
43   -import java.util.concurrent.ScheduledExecutorService;
44 43
45 44 @Service
46 45 @Slf4j
... ... @@ -110,7 +109,7 @@ public class DefaultActorService extends TbApplicationEventListener<PartitionCha
110 109 if (poolSize == 1) {
111 110 return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(dispatcherName));
112 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 32 import org.springframework.web.bind.annotation.ResponseBody;
33 33 import org.springframework.web.bind.annotation.ResponseStatus;
34 34 import org.springframework.web.bind.annotation.RestController;
  35 +import org.thingsboard.common.util.JacksonUtil;
35 36 import org.thingsboard.rule.engine.api.MailService;
36 37 import org.thingsboard.server.common.data.EntityType;
37 38 import org.thingsboard.server.common.data.User;
... ... @@ -89,13 +90,14 @@ public class UserController extends BaseController {
89 90 try {
90 91 UserId userId = new UserId(toUUID(strUserId));
91 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 102 return user;
101 103 } catch (Exception e) {
... ... @@ -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 108 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
115 109 @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET)
116 110 @ResponseBody
... ...
... ... @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
23 23 import org.springframework.beans.factory.annotation.Value;
24 24 import org.springframework.context.annotation.Lazy;
25 25 import org.springframework.stereotype.Service;
  26 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
26 27 import org.thingsboard.rule.engine.api.MailService;
27 28 import org.thingsboard.server.common.data.ApiFeature;
28 29 import org.thingsboard.server.common.data.ApiUsageRecordKey;
... ... @@ -56,7 +57,7 @@ import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
56 57 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
57 58 import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto;
58 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 61 import org.thingsboard.server.queue.discovery.PartitionService;
61 62 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
62 63 import org.thingsboard.server.queue.scheduler.SchedulerComponent;
... ... @@ -146,7 +147,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa
146 147 this.scheduler = scheduler;
147 148 this.tenantProfileCache = tenantProfileCache;
148 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 153 @PostConstruct
... ...
... ... @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantProfileId;
23 23 import org.thingsboard.server.common.msg.queue.TbCallback;
24 24 import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
25 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 28 public interface TbApiUsageStateService extends ApplicationListener<PartitionChangeEvent> {
29 29
... ...
... ... @@ -55,7 +55,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceM
55 55 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
56 56 import org.thingsboard.server.queue.TbQueueConsumer;
57 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 59 import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
60 60 import org.thingsboard.server.queue.util.TbCoreComponent;
61 61 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
... ...
... ... @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
20 20 import org.springframework.beans.factory.annotation.Value;
21 21 import org.springframework.scheduling.annotation.Scheduled;
22 22 import org.springframework.stereotype.Service;
  23 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
23 24 import org.thingsboard.rule.engine.api.RpcError;
24 25 import org.thingsboard.server.actors.ActorSystemContext;
25 26 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -38,7 +39,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
38 39 import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
39 40 import org.thingsboard.server.queue.TbQueueConsumer;
40 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 43 import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory;
43 44 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
44 45 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
... ... @@ -127,7 +128,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
127 128 consumers.computeIfAbsent(configuration.getName(), queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration));
128 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 134 @PreDestroy
... ... @@ -160,6 +161,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
160 161
161 162 private void launchConsumer(TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer, TbRuleEngineQueueConfiguration configuration, TbRuleEngineConsumerStats stats) {
162 163 consumersExecutor.execute(() -> {
  164 + Thread.currentThread().setName("" + Thread.currentThread().getName() + "-" + configuration.getName());
163 165 while (!stopped) {
164 166 try {
165 167 List<TbProtoQueueMsg<ToRuleEngineMsg>> msgs = consumer.poll(pollDuration);
... ...
... ... @@ -16,7 +16,7 @@
16 16 package org.thingsboard.server.service.queue;
17 17
18 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 21 public interface TbCoreConsumerService extends ApplicationListener<PartitionChangeEvent> {
22 22
... ...
... ... @@ -16,7 +16,7 @@
16 16 package org.thingsboard.server.service.queue;
17 17
18 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 21 public interface TbRuleEngineConsumerService extends ApplicationListener<PartitionChangeEvent> {
22 22
... ...
... ... @@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
35 35 import org.thingsboard.server.common.msg.queue.TbCallback;
36 36 import org.thingsboard.server.queue.TbQueueConsumer;
37 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 39 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
40 40 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
41 41 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
... ...
... ... @@ -185,7 +185,7 @@ public class DefaultTbResourceService implements TbResourceService {
185 185 instance.setId(0);
186 186 List<LwM2mResourceObserve> resources = new ArrayList<>();
187 187 obj.resources.forEach((k, v) -> {
188   - if (!v.operations.isExecutable()) {
  188 + if (v.operations.isReadable()) {
189 189 LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
190 190 resources.add(lwM2MResourceObserve);
191 191 }
... ...
... ... @@ -25,6 +25,7 @@ import lombok.Getter;
25 25 import lombok.extern.slf4j.Slf4j;
26 26 import org.springframework.beans.factory.annotation.Value;
27 27 import org.springframework.scheduling.annotation.Scheduled;
  28 +import org.thingsboard.common.util.ThingsBoardExecutors;
28 29 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
29 30 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
30 31
... ... @@ -93,7 +94,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
93 94 super.init(maxRequestsTimeout);
94 95 if (useJsSandbox()) {
95 96 sandbox = NashornSandboxes.create();
96   - monitorExecutorService = Executors.newWorkStealingPool(getMonitorThreadPoolSize());
  97 + monitorExecutorService = ThingsBoardExecutors.newWorkStealingPool(getMonitorThreadPoolSize(), "nashorn-js-monitor");
97 98 sandbox.setExecutor(monitorExecutorService);
98 99 sandbox.setMaxCPUTime(getMaxCpuTime());
99 100 sandbox.allowNoBraces(false);
... ...
... ... @@ -54,7 +54,7 @@ import org.thingsboard.server.dao.tenant.TenantService;
54 54 import org.thingsboard.server.dao.timeseries.TimeseriesService;
55 55 import org.thingsboard.common.util.JacksonUtil;
56 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 58 import org.thingsboard.server.queue.discovery.PartitionService;
59 59 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
60 60 import org.thingsboard.server.queue.util.TbCoreComponent;
... ...
... ... @@ -18,7 +18,7 @@ package org.thingsboard.server.service.state;
18 18 import org.springframework.context.ApplicationListener;
19 19 import org.thingsboard.server.common.data.Device;
20 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 22 import org.thingsboard.server.gen.transport.TransportProtos;
23 23 import org.thingsboard.server.common.msg.queue.TbCallback;
24 24
... ...
... ... @@ -46,7 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdate
46 46 import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateValueListProto;
47 47 import org.thingsboard.server.queue.TbQueueProducer;
48 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 50 import org.thingsboard.server.queue.discovery.PartitionService;
51 51 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
52 52 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
... ...
... ... @@ -20,10 +20,10 @@ import org.springframework.beans.factory.annotation.Autowired;
20 20 import org.springframework.context.annotation.Lazy;
21 21 import org.springframework.context.event.EventListener;
22 22 import org.springframework.stereotype.Service;
23   -import org.thingsboard.common.util.ThingsBoardThreadFactory;
  23 +import org.thingsboard.common.util.ThingsBoardExecutors;
24 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 27 import org.thingsboard.server.queue.discovery.PartitionService;
28 28 import org.thingsboard.server.common.msg.queue.ServiceType;
29 29 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
... ... @@ -63,7 +63,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
63 63 private SubscriptionManagerService subscriptionManagerService;
64 64
65 65 private ExecutorService subscriptionUpdateExecutor;
66   -
  66 +
67 67 private TbApplicationEventListener<PartitionChangeEvent> partitionChangeListener = new TbApplicationEventListener<>() {
68 68 @Override
69 69 protected void onTbApplicationEvent(PartitionChangeEvent event) {
... ... @@ -94,7 +94,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
94 94
95 95 @PostConstruct
96 96 public void initExecutor() {
97   - subscriptionUpdateExecutor = Executors.newWorkStealingPool(20);
  97 + subscriptionUpdateExecutor = ThingsBoardExecutors.newWorkStealingPool(20, getClass());
98 98 }
99 99
100 100 @PreDestroy
... ...
... ... @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.TenantId;
22 22 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
23 23 import org.thingsboard.server.common.data.kv.TsKvEntry;
24 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 27 import java.util.List;
28 28
... ...
... ... @@ -15,8 +15,8 @@
15 15 */
16 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 20 import org.thingsboard.server.common.msg.queue.TbCallback;
21 21 import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
22 22 import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;
... ...
... ... @@ -22,35 +22,18 @@ import lombok.extern.slf4j.Slf4j;
22 22 import org.springframework.beans.factory.annotation.Autowired;
23 23 import org.springframework.context.ApplicationListener;
24 24 import org.springframework.context.event.EventListener;
25   -import org.springframework.stereotype.Service;
26 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 26 import org.thingsboard.server.common.msg.queue.ServiceType;
37   -import org.thingsboard.server.common.msg.queue.TbCallback;
38 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 29 import org.thingsboard.server.queue.discovery.PartitionService;
44 30 import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
45 31 import org.thingsboard.server.service.queue.TbClusterService;
46 32 import org.thingsboard.server.service.subscription.SubscriptionManagerService;
47   -import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
48 33
49 34 import javax.annotation.Nullable;
50 35 import javax.annotation.PostConstruct;
51 36 import javax.annotation.PreDestroy;
52   -import java.util.Collections;
53   -import java.util.List;
54 37 import java.util.Optional;
55 38 import java.util.Set;
56 39 import java.util.concurrent.ConcurrentHashMap;
... ...
... ... @@ -17,8 +17,7 @@ package org.thingsboard.server.service.telemetry;
17 17
18 18 import org.springframework.context.ApplicationListener;
19 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 23 * Created by ashvayka on 27.03.18.
... ...
... ... @@ -28,6 +28,8 @@ import org.springframework.beans.factory.annotation.Value;
28 28 import org.springframework.stereotype.Service;
29 29 import org.springframework.util.StringUtils;
30 30 import org.springframework.web.socket.CloseStatus;
  31 +import org.thingsboard.common.util.ThingsBoardExecutors;
  32 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
31 33 import org.thingsboard.server.common.data.DataConstants;
32 34 import org.thingsboard.server.common.data.id.CustomerId;
33 35 import org.thingsboard.server.common.data.id.EntityId;
... ... @@ -157,9 +159,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
157 159 @PostConstruct
158 160 public void initExecutor() {
159 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 165 pingExecutor.scheduleWithFixedDelay(this::sendPing, 10000, 10000, TimeUnit.MILLISECONDS);
164 166 }
165 167
... ...
... ... @@ -16,8 +16,7 @@
16 16 package org.thingsboard.server.service.telemetry;
17 17
18 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 22 * Created by ashvayka on 27.03.18.
... ...
... ... @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.ApiUsageState;
31 31 import org.thingsboard.server.common.data.DataConstants;
32 32 import org.thingsboard.server.common.data.Device;
33 33 import org.thingsboard.server.common.data.DeviceProfile;
  34 +import org.thingsboard.server.common.data.DeviceTransportType;
34 35 import org.thingsboard.server.common.data.EntityType;
35 36 import org.thingsboard.server.common.data.Firmware;
36 37 import org.thingsboard.server.common.data.FirmwareInfo;
... ... @@ -47,6 +48,8 @@ import org.thingsboard.server.common.data.id.DeviceId;
47 48 import org.thingsboard.server.common.data.id.DeviceProfileId;
48 49 import org.thingsboard.server.common.data.id.FirmwareId;
49 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 53 import org.thingsboard.server.common.data.relation.EntityRelation;
51 54 import org.thingsboard.server.common.data.security.DeviceCredentials;
52 55 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
... ... @@ -66,11 +69,15 @@ import org.thingsboard.server.dao.relation.RelationService;
66 69 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
67 70 import org.thingsboard.server.gen.transport.TransportProtos;
68 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 74 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
70 75 import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
71 76 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
72 77 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
73 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 81 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
75 82 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
76 83 import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
... ... @@ -93,6 +100,7 @@ import java.util.concurrent.ConcurrentHashMap;
93 100 import java.util.concurrent.ConcurrentMap;
94 101 import java.util.concurrent.locks.Lock;
95 102 import java.util.concurrent.locks.ReentrantLock;
  103 +import java.util.stream.Collectors;
96 104
97 105 /**
98 106 * Created by ashvayka on 05.10.18.
... ... @@ -146,43 +154,43 @@ public class DefaultTransportApiService implements TransportApiService {
146 154 @Override
147 155 public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) {
148 156 TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue();
  157 + ListenableFuture<TransportApiResponseMsg> result = null;
  158 +
149 159 if (transportApiRequestMsg.hasValidateTokenRequestMsg()) {
150 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 162 } else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) {
154 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 165 } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) {
158 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 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 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 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 174 } else if (transportApiRequestMsg.hasValidateDeviceLwM2MCredentialsRequestMsg()) {
171 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 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 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 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 196 private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) {
... ... @@ -376,6 +384,39 @@ public class DefaultTransportApiService implements TransportApiService {
376 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 420 private ListenableFuture<TransportApiResponseMsg> handle(GetResourceRequestMsg requestMsg) {
380 421 TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
381 422 ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType());
... ... @@ -394,6 +435,22 @@ public class DefaultTransportApiService implements TransportApiService {
394 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 454 private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) {
398 455 return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, deviceId), device -> {
399 456 if (device == null) {
... ...
... ... @@ -21,6 +21,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
21 21 import org.springframework.context.event.EventListener;
22 22 import org.springframework.core.annotation.Order;
23 23 import org.springframework.stereotype.Service;
  24 +import org.thingsboard.common.util.ThingsBoardExecutors;
24 25 import org.thingsboard.server.common.stats.MessagesStats;
25 26 import org.thingsboard.server.common.stats.StatsFactory;
26 27 import org.thingsboard.server.common.stats.StatsType;
... ... @@ -70,7 +71,7 @@ public class TbCoreTransportApiService {
70 71
71 72 @PostConstruct
72 73 public void init() {
73   - this.transportCallbackExecutor = Executors.newWorkStealingPool(maxCallbackThreads);
  74 + this.transportCallbackExecutor = ThingsBoardExecutors.newWorkStealingPool(maxCallbackThreads, getClass());
74 75 TbQueueProducer<TbProtoQueueMsg<TransportApiResponseMsg>> producer = tbCoreQueueFactory.createTransportApiResponseProducer();
75 76 TbQueueConsumer<TbProtoQueueMsg<TransportApiRequestMsg>> consumer = tbCoreQueueFactory.createTransportApiRequestConsumer();
76 77
... ...
... ... @@ -26,6 +26,7 @@
26 26 </appender>
27 27
28 28 <logger name="org.thingsboard.server" level="INFO" />
  29 + <logger name="org.thingsboard.server.transport.snmp" level="TRACE" />
29 30
30 31 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />-->
31 32 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />-->
... ...
... ... @@ -621,9 +621,9 @@ transport:
621 621 key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}"
622 622 # Key alias
623 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 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 627 dtls_session_inactivity_timeout: "${TB_COAP_X509_DTLS_SESSION_INACTIVITY_TIMEOUT:86400000}"
628 628 dtls_session_report_timeout: "${TB_COAP_X509_DTLS_SESSION_REPORT_TIMEOUT:1800000}"
629 629 # Local LwM2M transport parameters
... ... @@ -686,6 +686,13 @@ transport:
686 686 alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}"
687 687 # Use redis for Security and Registration stores
688 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 697 # Edges parameters
691 698 edges:
... ...
... ... @@ -16,10 +16,10 @@
16 16 package org.thingsboard.server.service.queue;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
19 20 import org.junit.Assert;
20 21 import org.junit.Test;
21 22 import org.junit.runner.RunWith;
22   -import org.mockito.Mockito;
23 23 import org.mockito.junit.MockitoJUnitRunner;
24 24 import org.thingsboard.server.gen.transport.TransportProtos;
25 25 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
... ... @@ -28,39 +28,74 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrateg
28 28 import java.util.UUID;
29 29 import java.util.concurrent.ConcurrentHashMap;
30 30 import java.util.concurrent.ConcurrentMap;
  31 +import java.util.concurrent.CountDownLatch;
31 32 import java.util.concurrent.ExecutorService;
32 33 import java.util.concurrent.Executors;
33 34 import java.util.concurrent.TimeUnit;
34 35
  36 +import static org.junit.Assert.assertTrue;
  37 +import static org.mockito.ArgumentMatchers.any;
35 38 import static org.mockito.Mockito.mock;
  39 +import static org.mockito.Mockito.times;
  40 +import static org.mockito.Mockito.verify;
36 41 import static org.mockito.Mockito.when;
37 42
38 43 @Slf4j
39 44 @RunWith(MockitoJUnitRunner.class)
40 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 57 @Test
43 58 public void testHighConcurrencyCase() throws InterruptedException {
44   - TbRuleEngineSubmitStrategy strategyMock = mock(TbRuleEngineSubmitStrategy.class);
  59 + //log.warn("preparing the test...");
45 60 int msgCount = 1000;
46 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 19 import lombok.extern.slf4j.Slf4j;
20 20 import org.springframework.beans.factory.annotation.Autowired;
21 21 import org.springframework.beans.factory.annotation.Value;
22   -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
23 22 import org.springframework.stereotype.Component;
24 23
25 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 26 @Component
28 27 public class CoapServerContext {
29 28
... ...
... ... @@ -23,7 +23,6 @@ import org.eclipse.californium.core.server.resources.Resource;
23 23 import org.eclipse.californium.scandium.DTLSConnector;
24 24 import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
25 25 import org.springframework.beans.factory.annotation.Autowired;
26   -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
27 26 import org.springframework.stereotype.Component;
28 27
29 28 import javax.annotation.PostConstruct;
... ... @@ -39,7 +38,7 @@ import java.util.concurrent.TimeUnit;
39 38
40 39 @Slf4j
41 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 42 public class DefaultCoapServerService implements CoapServerService {
44 43
45 44 @Autowired
... ...
... ... @@ -39,7 +39,6 @@ import java.util.Collections;
39 39 import java.util.Optional;
40 40
41 41 @Slf4j
42   -@ConditionalOnExpression("'${transport.coap.enabled}'=='true'")
43 42 @ConditionalOnProperty(prefix = "transport.coap.dtls", value = "enabled", havingValue = "true", matchIfMissing = false)
44 43 @Component
45 44 public class TbCoapDtlsSettings {
... ... @@ -50,7 +49,7 @@ public class TbCoapDtlsSettings {
50 49 @Value("${transport.coap.dtls.bind_port}")
51 50 private Integer port;
52 51
53   - @Value("${transport.coap.dtls.mode}")
  52 + @Value("${transport.coap.dtls.mode:NO_AUTH}")
54 53 private String mode;
55 54
56 55 @Value("${transport.coap.dtls.key_store}")
... ... @@ -65,13 +64,13 @@ public class TbCoapDtlsSettings {
65 64 @Value("${transport.coap.dtls.key_alias}")
66 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 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 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 74 private long dtlsSessionReportTimeout;
76 75
77 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 19 import org.thingsboard.server.common.data.Device;
20 20 import org.thingsboard.server.common.data.DeviceInfo;
21 21 import org.thingsboard.server.common.data.DeviceProfile;
  22 +import org.thingsboard.server.common.data.DeviceTransportType;
22 23 import org.thingsboard.server.common.data.EntitySubtype;
23 24 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
24 25 import org.thingsboard.server.common.data.id.CustomerId;
... ... @@ -32,6 +33,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
32 33 import org.thingsboard.server.dao.device.provision.ProvisionRequest;
33 34
34 35 import java.util.List;
  36 +import java.util.UUID;
35 37
36 38 public interface DeviceService {
37 39
... ... @@ -95,6 +97,8 @@ public interface DeviceService {
95 97
96 98 Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile);
97 99
  100 + PageData<UUID> findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType, PageLink pageLink);
  101 +
98 102 Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
99 103
100 104 Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
... ...
... ... @@ -87,6 +87,10 @@
87 87 <groupId>org.thingsboard</groupId>
88 88 <artifactId>protobuf-dynamic</artifactId>
89 89 </dependency>
  90 + <dependency>
  91 + <groupId>org.apache.commons</groupId>
  92 + <artifactId>commons-lang3</artifactId>
  93 + </dependency>
90 94 </dependencies>
91 95
92 96 <build>
... ...
... ... @@ -93,6 +93,25 @@ public class DataConstants {
93 93 public static final String USERNAME = "username";
94 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 115 public static final String EDGE_MSG_SOURCE = "edge";
97 116 public static final String MSG_SOURCE_KEY = "source";
98 117
... ...
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data;
18 18 public enum DeviceTransportType {
19 19 DEFAULT,
20 20 MQTT,
  21 + COAP,
21 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 21 import com.fasterxml.jackson.annotation.JsonTypeInfo;
22 22 import org.thingsboard.server.common.data.DeviceTransportType;
23 23
  24 +import java.io.Serializable;
  25 +
24 26 @JsonIgnoreProperties(ignoreUnknown = true)
25 27 @JsonTypeInfo(
26 28 use = JsonTypeInfo.Id.NAME,
... ... @@ -29,11 +31,14 @@ import org.thingsboard.server.common.data.DeviceTransportType;
29 31 @JsonSubTypes({
30 32 @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"),
31 33 @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"),
  34 + @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP"),
32 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 38 @JsonIgnore
37 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 29 include = JsonTypeInfo.As.PROPERTY,
30 30 property = "type")
31 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 38 public interface DeviceProfileTransportConfiguration extends Serializable {
37 39
38 40 @JsonIgnore
39 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 38 @Slf4j
39 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 42 private volatile boolean subscribed;
42 43 protected volatile boolean stopped = false;
43 44 protected volatile Set<TopicPartitionInfo> partitions;
... ... @@ -83,7 +84,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
83 84 }
84 85
85 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 90 consumerLock.lock();
... ... @@ -131,9 +132,12 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
131 132 List<T> sleepAndReturnEmpty(final long startNanos, final long durationInMillis) {
132 133 long durationNanos = TimeUnit.MILLISECONDS.toNanos(durationInMillis);
133 134 long spentNanos = System.nanoTime() - startNanos;
134   - if (spentNanos < durationNanos) {
  135 + long nanosLeft = durationNanos - spentNanos;
  136 + if (nanosLeft >= ONE_MILLISECOND_IN_NANOS) {
135 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 141 } catch (InterruptedException e) {
138 142 if (!stopped) {
139 143 log.error("Failed to wait", e);
... ... @@ -146,7 +150,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i
146 150 @Override
147 151 public void commit() {
148 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 155 consumerLock.lock();
152 156 try {
... ...
... ... @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture;
20 20 import com.google.common.util.concurrent.SettableFuture;
21 21 import lombok.Builder;
22 22 import lombok.extern.slf4j.Slf4j;
  23 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
23 24 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
24 25 import org.thingsboard.server.queue.TbQueueAdmin;
25 26 import org.thingsboard.server.queue.TbQueueCallback;
... ... @@ -77,7 +78,7 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
77 78 this.executor = executor;
78 79 } else {
79 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 17
18 18 import lombok.Builder;
19 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
20 21 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
21 22 import org.thingsboard.server.queue.TbQueueConsumer;
22 23 import org.thingsboard.server.queue.TbQueueHandler;
... ... @@ -70,8 +71,8 @@ public class DefaultTbQueueResponseTemplate<Request extends TbQueueMsg, Response
70 71 this.requestTimeout = requestTimeout;
71 72 this.callbackExecutor = executor;
72 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 78 @Override
... ...
... ... @@ -19,19 +19,23 @@ import lombok.Getter;
19 19 import lombok.extern.slf4j.Slf4j;
20 20 import org.springframework.beans.factory.annotation.Autowired;
21 21 import org.springframework.beans.factory.annotation.Value;
  22 +import org.springframework.context.ApplicationContext;
22 23 import org.springframework.stereotype.Component;
23 24 import org.springframework.util.StringUtils;
  25 +import org.thingsboard.server.common.data.TbTransportService;
24 26 import org.thingsboard.server.common.data.id.TenantId;
25 27 import org.thingsboard.server.common.msg.queue.ServiceType;
26 28 import org.thingsboard.server.gen.transport.TransportProtos;
27 29 import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
28 30 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
29 31 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
  32 +import org.thingsboard.server.queue.util.AfterContextReady;
30 33
31 34 import javax.annotation.PostConstruct;
32 35 import java.net.InetAddress;
33 36 import java.net.UnknownHostException;
34 37 import java.util.Arrays;
  38 +import java.util.Collection;
35 39 import java.util.Collections;
36 40 import java.util.List;
37 41 import java.util.Optional;
... ... @@ -56,6 +60,8 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
56 60
57 61 @Autowired(required = false)
58 62 private TbQueueRuleEngineSettings ruleEngineSettings;
  63 + @Autowired
  64 + private ApplicationContext applicationContext;
59 65
60 66 private List<ServiceType> serviceTypes;
61 67 private ServiceInfo serviceInfo;
... ... @@ -102,6 +108,19 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
102 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 124 @Override
106 125 public ServiceInfo getServiceInfo() {
107 126 return serviceInfo;
... ...
... ... @@ -15,26 +15,27 @@
15 15 */
16 16 package org.thingsboard.server.queue.discovery;
17 17
18   -import com.google.common.hash.HashCode;
19 18 import com.google.common.hash.HashFunction;
  19 +import com.google.common.hash.Hasher;
20 20 import com.google.common.hash.Hashing;
21   -import lombok.Getter;
22 21 import lombok.extern.slf4j.Slf4j;
23 22 import org.springframework.beans.factory.annotation.Value;
24 23 import org.springframework.context.ApplicationEventPublisher;
25 24 import org.springframework.stereotype.Service;
26 25 import org.thingsboard.server.common.data.id.EntityId;
27 26 import org.thingsboard.server.common.data.id.TenantId;
28   -import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
29 27 import org.thingsboard.server.common.msg.queue.ServiceQueue;
  28 +import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
30 29 import org.thingsboard.server.common.msg.queue.ServiceType;
31 30 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
32 31 import org.thingsboard.server.gen.transport.TransportProtos;
33 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 36 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
35 37
36 38 import javax.annotation.PostConstruct;
37   -import java.nio.charset.StandardCharsets;
38 39 import java.util.ArrayList;
39 40 import java.util.Collections;
40 41 import java.util.Comparator;
... ... @@ -46,7 +47,6 @@ import java.util.Set;
46 47 import java.util.UUID;
47 48 import java.util.concurrent.ConcurrentHashMap;
48 49 import java.util.concurrent.ConcurrentMap;
49   -import java.util.concurrent.ConcurrentNavigableMap;
50 50 import java.util.stream.Collectors;
51 51
52 52 @Service
... ... @@ -186,6 +186,8 @@ public class HashPartitionService implements PartitionService {
186 186 applicationEventPublisher.publishEvent(new ClusterTopologyChangeEvent(this, changes));
187 187 }
188 188 }
  189 +
  190 + applicationEventPublisher.publishEvent(new ServiceListChangedEvent(otherServices, currentService));
189 191 }
190 192
191 193 @Override
... ... @@ -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 232 private Map<ServiceQueueKey, List<ServiceInfo>> getServiceKeyListMap(List<ServiceInfo> services) {
223 233 final Map<ServiceQueueKey, List<ServiceInfo>> currentMap = new HashMap<>();
224 234 services.forEach(serviceInfo -> {
... ...
... ... @@ -20,9 +20,11 @@ import org.thingsboard.server.common.data.id.TenantId;
20 20 import org.thingsboard.server.common.msg.queue.ServiceType;
21 21 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
22 22 import org.thingsboard.server.gen.transport.TransportProtos;
  23 +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
23 24
24 25 import java.util.List;
25 26 import java.util.Set;
  27 +import java.util.UUID;
26 28
27 29 /**
28 30 * Once application is ready or cluster topology changes, this Service will produce {@link PartitionChangeEvent}
... ... @@ -55,4 +57,6 @@ public interface PartitionService {
55 57 * @return
56 58 */
57 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 17
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.context.ApplicationListener;
  20 +import org.thingsboard.server.queue.discovery.event.TbApplicationEvent;
20 21
21 22 import java.util.concurrent.locks.Lock;
22 23 import java.util.concurrent.locks.ReentrantLock;
... ...
... ... @@ -33,12 +33,14 @@ import org.apache.zookeeper.KeeperException;
33 33 import org.springframework.beans.factory.annotation.Value;
34 34 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
35 35 import org.springframework.boot.context.event.ApplicationReadyEvent;
  36 +import org.springframework.context.ApplicationEventPublisher;
36 37 import org.springframework.context.event.EventListener;
37 38 import org.springframework.core.annotation.Order;
38 39 import org.springframework.stereotype.Service;
39 40 import org.springframework.util.Assert;
40 41 import org.thingsboard.common.util.ThingsBoardThreadFactory;
41 42 import org.thingsboard.server.gen.transport.TransportProtos;
  43 +import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
42 44
43 45 import javax.annotation.PostConstruct;
44 46 import javax.annotation.PreDestroy;
... ... @@ -77,7 +79,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
77 79
78 80 private volatile boolean stopped = true;
79 81
80   - public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider, PartitionService partitionService) {
  82 + public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider,
  83 + PartitionService partitionService) {
81 84 this.serviceInfoProvider = serviceInfoProvider;
82 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 13 * See the License for the specific language governing permissions and
14 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 18 import lombok.Getter;
19   -import org.springframework.context.ApplicationEvent;
20 19 import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
21 20
22 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 13 * See the License for the specific language governing permissions and
14 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 18 import lombok.Getter;
19   -import org.springframework.context.ApplicationEvent;
20 19 import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
21 20 import org.thingsboard.server.common.msg.queue.ServiceType;
22 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 13 * See the License for the specific language governing permissions and
14 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 18 import lombok.Getter;
19 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 34 int64 tenantIdMSB = 3;
35 35 int64 tenantIdLSB = 4;
36 36 repeated QueueInfo ruleEngineQueues = 5;
  37 + repeated string transports = 6;
37 38 }
38 39
39 40 /**
... ... @@ -246,6 +247,36 @@ message GetEntityProfileResponseMsg {
246 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 280 message EntityUpdateMsg {
250 281 string entityType = 1;
251 282 bytes data = 2;
... ... @@ -592,6 +623,9 @@ message TransportApiRequestMsg {
592 623 ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8;
593 624 GetResourceRequestMsg resourceRequestMsg = 9;
594 625 GetFirmwareRequestMsg firmwareRequestMsg = 10;
  626 + GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11;
  627 + GetDeviceRequestMsg deviceRequestMsg = 12;
  628 + GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13;
595 629 }
596 630
597 631 /* Response from ThingsBoard Core Service to Transport Service */
... ... @@ -600,9 +634,12 @@ message TransportApiResponseMsg {
600 634 GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2;
601 635 GetEntityProfileResponseMsg entityProfileResponseMsg = 3;
602 636 ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4;
  637 + GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5;
603 638 LwM2MResponseMsg lwM2MResponseMsg = 6;
604 639 GetResourceResponseMsg resourceResponseMsg = 7;
605 640 GetFirmwareResponseMsg firmwareResponseMsg = 8;
  641 + GetDeviceResponseMsg deviceResponseMsg = 9;
  642 + GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10;
606 643 }
607 644
608 645 /* Messages that are handled by ThingsBoard Core Service */
... ...
... ... @@ -21,7 +21,9 @@ import org.eclipse.californium.core.CoapServer;
21 21 import org.springframework.beans.factory.annotation.Autowired;
22 22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
23 23 import org.springframework.stereotype.Service;
  24 +import org.thingsboard.server.common.data.TbTransportService;
24 25 import org.thingsboard.server.coapserver.CoapServerService;
  26 +import org.thingsboard.server.coapserver.TbCoapServerComponent;
25 27 import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource;
26 28
27 29 import javax.annotation.PostConstruct;
... ... @@ -29,9 +31,9 @@ import javax.annotation.PreDestroy;
29 31 import java.net.UnknownHostException;
30 32
31 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 35 @Slf4j
34   -public class CoapTransportService {
  36 +public class CoapTransportService implements TbTransportService {
35 37
36 38 private static final String V1 = "v1";
37 39 private static final String API = "api";
... ... @@ -65,4 +67,9 @@ public class CoapTransportService {
65 67 public void shutdown() {
66 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 35 import org.springframework.web.context.request.async.DeferredResult;
36 36 import org.thingsboard.server.common.data.DeviceTransportType;
37 37 import org.thingsboard.server.common.data.firmware.FirmwareType;
  38 +import org.thingsboard.server.common.data.TbTransportService;
38 39 import org.thingsboard.server.common.data.id.DeviceId;
39 40 import org.thingsboard.server.common.transport.SessionMsgListener;
40 41 import org.thingsboard.server.common.transport.TransportContext;
... ... @@ -71,7 +72,7 @@ import java.util.function.Consumer;
71 72 @ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.http.enabled}'=='true')")
72 73 @RequestMapping("/api/v1")
73 74 @Slf4j
74   -public class DeviceApiController {
  75 +public class DeviceApiController implements TbTransportService {
75 76
76 77 @Autowired
77 78 private HttpTransportContext transportContext;
... ... @@ -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 55 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
56 56 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
57 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 60 @Slf4j
61 61 @Component
... ... @@ -93,7 +93,6 @@ public class LwM2MTransportBootstrapServerConfiguration {
93 93 builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort));
94 94
95 95 /** Define model provider (Create Models )*/
96   -// builder.setModel(new StaticModel(contextS.getLwM2MTransportConfigServer().getModelsValueCommon()));
97 96
98 97 /** Create credentials */
99 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 26
27 27 import java.util.Collection;
28 28
  29 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
29 30 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer;
30 31
31 32 @Slf4j
... ... @@ -85,17 +86,19 @@ public class LwM2mServerListener {
85 86
86 87 @Override
87 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 94 @Override
92 95 public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
93 96 if (registration != null) {
94 97 try {
95   - service.onObservationResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
  98 + service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
96 99 registration), response, null);
97 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 111
109 112 @Override
110 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 25 import com.google.gson.reflect.TypeToken;
26 26 import lombok.extern.slf4j.Slf4j;
27 27 import org.apache.commons.lang3.StringUtils;
28   -import org.eclipse.californium.core.network.config.NetworkConfig;
29 28 import org.eclipse.leshan.core.attributes.Attribute;
30 29 import org.eclipse.leshan.core.attributes.AttributeSet;
31 30 import org.eclipse.leshan.core.model.ObjectModel;
... ... @@ -40,7 +39,6 @@ import org.eclipse.leshan.core.node.codec.CodecException;
40 39 import org.eclipse.leshan.core.request.DownlinkRequest;
41 40 import org.eclipse.leshan.core.request.WriteAttributesRequest;
42 41 import org.eclipse.leshan.core.util.Hex;
43   -import org.eclipse.leshan.server.californium.LeshanServerBuilder;
44 42 import org.eclipse.leshan.server.registration.Registration;
45 43 import org.nustaq.serialization.FSTConfiguration;
46 44 import org.thingsboard.server.common.data.DeviceProfile;
... ... @@ -50,7 +48,6 @@ import org.thingsboard.server.common.transport.TransportServiceCallback;
50 48 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
51 49 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
52 50
53   -import java.io.File;
54 51 import java.io.IOException;
55 52 import java.util.ArrayList;
56 53 import java.util.Arrays;
... ... @@ -72,7 +69,6 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA
72 69 @Slf4j
73 70 public class LwM2mTransportHandler {
74 71
75   - // public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me";
76 72 public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0";
77 73 public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";
78 74 public static final String BOOTSTRAP = "bootstrap";
... ... @@ -85,19 +81,12 @@ public class LwM2mTransportHandler {
85 81 public static final String KEY_NAME = "keyName";
86 82 public static final String OBSERVE_LWM2M = "observe";
87 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 85 private static final String REQUEST = "/request";
92   - // private static final String RESPONSE = "/response";
93 86 private static final String ATTRIBUTES = "/" + ATTRIBUTE;
94 87 public static final String TELEMETRIES = "/" + TELEMETRY;
95   - // public static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE;
96 88 public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST;
97   - // public static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/";
98 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 91 public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
103 92
... ... @@ -112,6 +101,11 @@ public class LwM2mTransportHandler {
112 101
113 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 109 public enum LwM2mTypeServer {
116 110 BOOTSTRAP(0, "bootstrap"),
117 111 CLIENT(1, "client");
... ... @@ -168,6 +162,8 @@ public class LwM2mTransportHandler {
168 162 WRITE_ATTRIBUTES(8, "WriteAttributes"),
169 163 DELETE(9, "Delete");
170 164
  165 +// READ_INFO_FW(10, "ReadInfoFirmware");
  166 +
171 167 public int code;
172 168 public String type;
173 169
... ... @@ -190,21 +186,6 @@ public class LwM2mTransportHandler {
190 186 public static final String SERVICE_CHANNEL = "SERVICE";
191 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 189 public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException {
209 190 switch (type) {
210 191 case BOOLEAN:
... ...
... ... @@ -67,6 +67,7 @@ import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
67 67 import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST;
68 68 import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND;
69 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 71 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
71 72 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
72 73 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE;
... ... @@ -125,7 +126,6 @@ public class LwM2mTransportRequest {
125 126 public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
126 127 String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
127 128 try {
128   -
129 129 String target = convertPathFromIdVerToObjectId(targetIdVer);
130 130 DownlinkRequest request = null;
131 131 ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT;
... ... @@ -145,11 +145,11 @@ public class LwM2mTransportRequest {
145 145 break;
146 146 case OBSERVE:
147 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 149 } else if (resultIds.isObjectInstance()) {
150   - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId());
  150 + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId());
151 151 } else if (resultIds.getObjectId() >= 0) {
152   - request = new ObserveRequest(resultIds.getObjectId());
  152 + request = new ObserveRequest(contentFormat, resultIds.getObjectId());
153 153 }
154 154 break;
155 155 case OBSERVE_CANCEL:
... ... @@ -171,8 +171,6 @@ public class LwM2mTransportRequest {
171 171 break;
172 172 case WRITE_REPLACE:
173 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 174 resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
177 175 .getModelProvider());
178 176 if (contentFormat.equals(ContentFormat.TLV)) {
... ... @@ -181,7 +179,6 @@ public class LwM2mTransportRequest {
181 179 registration, rpcRequest);
182 180 }
183 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 182 else if (!contentFormat.equals(ContentFormat.TLV)) {
186 183 request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
187 184 resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type,
... ... @@ -215,13 +212,16 @@ public class LwM2mTransportRequest {
215 212 long finalTimeoutInMs = timeoutInMs;
216 213 lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest));
217 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 223 } else {
224   - log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper, targetIdVer);
  224 + log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
225 225 if (rpcRequest != null) {
226 226 String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
227 227 serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
... ... @@ -236,8 +236,8 @@ public class LwM2mTransportRequest {
236 236 Set<String> observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
237 237 String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO,
238 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 241 if (rpcRequest != null) {
242 242 String valueMsg = String.format("Observation paths - %s", observationPaths);
243 243 serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
... ... @@ -246,7 +246,7 @@ public class LwM2mTransportRequest {
246 246 } catch (Exception e) {
247 247 String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
248 248 typeOper.name(), e.getMessage());
249   - serviceImpl.sendLogsToThingsboard(msg, registration);
  249 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
250 250 throw new Exception(e);
251 251 }
252 252 }
... ... @@ -261,45 +261,53 @@ public class LwM2mTransportRequest {
261 261 private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
262 262 leshanServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
263 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 266 if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
267 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 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 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 277 if (rpcRequest != null) {
287 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 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 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 307 if (rpcRequest != null) {
299 308 serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
300 309 }
301 310 });
302   -
303 311 }
304 312
305 313 private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId,
... ... @@ -323,7 +331,9 @@ public class LwM2mTransportRequest {
323 331 Date date = new Date(Long.decode(value.toString()));
324 332 return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date);
325 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 337 default:
328 338 }
329 339 }
... ... @@ -337,7 +347,7 @@ public class LwM2mTransportRequest {
337 347 String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
338 348 String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
339 349 patn, type, value, e.toString());
340   - serviceImpl.sendLogsToThingsboard(msg, registration);
  350 + serviceImpl.sendLogsToThingsboard(msg, registration.getId());
341 351 log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
342 352 if (rpcRequest != null) {
343 353 String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
... ... @@ -369,7 +379,7 @@ public class LwM2mTransportRequest {
369 379 DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
370 380 String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
371 381 if (response instanceof ReadResponse) {
372   - serviceImpl.onObservationResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
  382 + serviceImpl.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
373 383 } else if (response instanceof CancelObservationResponse) {
374 384 log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response);
375 385
... ... @@ -389,14 +399,33 @@ public class LwM2mTransportRequest {
389 399 } else if (response instanceof WriteAttributesResponse) {
390 400 log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response);
391 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 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 16 package org.thingsboard.server.transport.lwm2m.server;
17 17
18 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 21 import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
20 22 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
21 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 59 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
58 60 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
59 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 64 @Slf4j
63 65 @Component
... ... @@ -92,7 +94,10 @@ public class LwM2mTransportServerConfiguration {
92 94 /** Use a magic converter to support bad type send by the UI. */
93 95 builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
94 96
  97 +
95 98 /** Create CoAP Config */
  99 + NetworkConfig networkConfig = getCoapConfig(serverPortNoSec, serverSecurePort);
  100 + BlockwiseLayer blockwiseLayer = new BlockwiseLayer(networkConfig);
96 101 builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort));
97 102
98 103 /** Define model provider (Create Models )*/
... ... @@ -110,6 +115,7 @@ public class LwM2mTransportServerConfiguration {
110 115
111 116 /** Create DTLS Config */
112 117 DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
  118 + dtlsConfig.setServerOnly(true);
113 119 dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups());
114 120 dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers());
115 121 if (this.pskMode) {
... ...
... ... @@ -20,13 +20,14 @@ import org.eclipse.leshan.core.response.ReadResponse;
20 20 import org.eclipse.leshan.server.registration.Registration;
21 21 import org.thingsboard.server.common.data.Device;
22 22 import org.thingsboard.server.common.data.DeviceProfile;
  23 +import org.thingsboard.server.common.data.TbTransportService;
23 24 import org.thingsboard.server.gen.transport.TransportProtos;
24 25 import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
25 26
26 27 import java.util.Collection;
27 28 import java.util.Optional;
28 29
29   -public interface LwM2mTransportService {
  30 +public interface LwM2mTransportService extends TbTransportService {
30 31
31 32 void onRegistered(Registration registration, Collection<Observation> previousObsersations);
32 33
... ... @@ -38,7 +39,7 @@ public interface LwM2mTransportService {
38 39
39 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 44 void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo);
44 45
... ... @@ -59,6 +60,4 @@ public interface LwM2mTransportService {
59 60 void doTrigger(Registration registration, String path);
60 61
61 62 void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
62   -
63   -
64 63 }
... ...
... ... @@ -38,9 +38,15 @@ import org.eclipse.leshan.server.registration.Registration;
38 38 import org.springframework.context.annotation.Lazy;
39 39 import org.springframework.stereotype.Service;
40 40 import org.thingsboard.common.util.JacksonUtil;
  41 +import org.thingsboard.server.cache.firmware.FirmwareDataCache;
41 42 import org.thingsboard.server.common.data.Device;
42 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 48 import org.thingsboard.server.common.transport.TransportService;
  49 +import org.thingsboard.server.common.transport.TransportServiceCallback;
44 50 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
45 51 import org.thingsboard.server.common.transport.service.DefaultTransportService;
46 52 import org.thingsboard.server.gen.transport.TransportProtos;
... ... @@ -76,9 +82,11 @@ import java.util.stream.Collectors;
76 82
77 83 import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST;
78 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 86 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
80 87 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED;
81 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 90 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
83 91 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
84 92 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE;
... ... @@ -109,6 +117,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
109 117 private ExecutorService executorUpdateRegistered;
110 118 private ExecutorService executorUnRegistered;
111 119 private LwM2mValueConverterImpl converter;
  120 + private FirmwareDataCache firmwareDataCache;
  121 +
112 122
113 123 private final TransportService transportService;
114 124
... ... @@ -120,12 +130,15 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
120 130
121 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 136 this.transportService = transportService;
125 137 this.lwM2mTransportContextServer = lwM2mTransportContextServer;
126 138 this.lwM2mClientContext = lwM2mClientContext;
127 139 this.leshanServer = leshanServer;
128 140 this.lwM2mTransportRequest = lwM2mTransportRequest;
  141 + this.firmwareDataCache = firmwareDataCache;
129 142 }
130 143
131 144 @PostConstruct
... ... @@ -167,8 +180,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
167 180 transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
168 181 transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
169 182 transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null);
  183 + this.getInfoFirmwareUpdate(lwM2MClient);
170 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 186 } else {
173 187 log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
174 188 }
... ... @@ -223,7 +237,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
223 237 executorUnRegistered.submit(() -> {
224 238 try {
225 239 this.setCancelObservations(registration);
226   - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration);
  240 + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
227 241 this.closeClientSession(registration);
228 242 } catch (Throwable t) {
229 243 log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
... ... @@ -256,7 +270,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
256 270 @Override
257 271 public void onSleepingDev(Registration registration) {
258 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 275 //TODO: associate endpointId with device information.
262 276 }
... ... @@ -279,7 +293,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
279 293 * @param response - observe
280 294 */
281 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 297 if (response.getContent() != null) {
284 298 if (response.getContent() instanceof LwM2mObject) {
285 299 LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
... ... @@ -303,20 +317,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
303 317
304 318 /**
305 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 327 * @param msg -
311 328 */
312 329 @Override
313 330 public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
  331 + LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
314 332 if (msg.getSharedUpdatedCount() > 0) {
315 333 msg.getSharedUpdatedList().forEach(tsKvProto -> {
316 334 String pathName = tsKvProto.getKv().getKey();
317 335 String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
318 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 340 if (pathIdVer != null) {
321 341 ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
322 342 .getModelProvider());
... ... @@ -326,19 +346,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
326 346 log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew);
327 347 String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
328 348 LOG_LW2M_ERROR, pathIdVer, valueNew);
329   - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  349 + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
330 350 }
331 351 } else {
332 352 log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew);
333 353 String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
334 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 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 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 481 }
455 482 if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) {
456 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 487 lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
461 488 if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) {
462 489 lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " +
463 490 lwm2mClientRpcRequest.keyNameKey + " is null or bad format");
464   - }
465   - else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
  491 + } else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
466 492 || WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper())
467   - && lwm2mClientRpcRequest.getTargetIdVer() !=null
  493 + && lwm2mClientRpcRequest.getTargetIdVer() != null
468 494 && !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource()
469 495 || new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) {
470   - lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey
  496 + lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey
471 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 499 lwm2mClientRpcRequest.setErrorMsg("Procedures In Development...");
475 500 }
476 501 } else {
... ... @@ -482,23 +507,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
482 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 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 528 this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo());
504 529 }
... ... @@ -555,7 +580,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
555 580 */
556 581 protected void onAwakeDev(Registration registration) {
557 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 584 //TODO: associate endpointId with device information.
560 585 }
561 586
... ... @@ -582,10 +607,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
582 607
583 608 /**
584 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 614 if (logMsg != null && sessionInfo != null) {
590 615 this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo);
591 616 }
... ... @@ -609,7 +634,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
609 634 if (clientObjects != null && clientObjects.size() > 0) {
610 635 if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
611 636 // #2
612   - lwM2MClient.getPendingRequests().addAll(clientObjects);
  637 + lwM2MClient.getPendingReadRequests().addAll(clientObjects);
613 638 clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(),
614 639 null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null));
615 640 }
... ... @@ -651,6 +676,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
651 676 * Sending observe value of resources to thingsboard
652 677 * #1 Return old Value Resource from LwM2MClient
653 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 682 * @param registration - Registration LwM2M Client
656 683 * @param lwM2mResource - LwM2mSingleResource response.getContent()
... ... @@ -660,6 +687,19 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
660 687 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
661 688 if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
662 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 703 Set<String> paths = new HashSet<>();
664 704 paths.add(path);
665 705 this.updateAttrTelemetry(registration, paths);
... ... @@ -668,6 +708,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
668 708 }
669 709 }
670 710
  711 +
671 712 /**
672 713 * send Attribute and Telemetry to Thingsboard
673 714 * #1 - get AttrName/TelemetryName with value from LwM2MClient:
... ... @@ -722,22 +763,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
722 763 params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
723 764 result = params.keySet();
724 765 }
725   - if (!result.isEmpty()) {
  766 + if (result != null && !result.isEmpty()) {
726 767 // #1
727 768 Set<String> pathSend = result.stream().filter(target -> {
728 769 return target.split(LWM2M_SEPARATOR_PATH).length < 3 ?
729 770 clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) :
730 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 774 if (!pathSend.isEmpty()) {
735   - lwM2MClient.getPendingRequests().addAll(pathSend);
  775 + lwM2MClient.getPendingReadRequests().addAll(pathSend);
736 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 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 1010 // update value in Resources
969 1011 registrationIds.forEach(registrationId -> {
970 1012 Registration registration = lwM2mClientContext.getRegistration(registrationId);
971   - this.readResourceValueObserve(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
  1013 + this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
972 1014 // send attr/telemetry to tingsboard for new path
973 1015 this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd());
974 1016 });
... ... @@ -997,12 +1039,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
997 1039 registrationIds.forEach(registrationId -> {
998 1040 Registration registration = lwM2mClientContext.getRegistration(registrationId);
999 1041 if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) {
1000   - this.readResourceValueObserve(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
  1042 + this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
1001 1043 }
1002 1044 // 5.3 del
1003 1045 // send Request cancel observe to Client
1004 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 1084 * @param registration - Registration LwM2M Client
1043 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 1088 targets.forEach(target -> {
1047 1089 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target));
1048 1090 if (pathIds.isResource()) {
... ... @@ -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 1178 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
1137 1179 paramAnallyzer.forEach(pathIdVer -> {
1138 1180 if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) {
... ... @@ -1152,7 +1194,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1152 1194 log.error("Failed update resource [{}] [{}]", path, valueNew);
1153 1195 String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad",
1154 1196 LOG_LW2M_ERROR, path, valueNew);
1155   - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
  1197 + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
1156 1198 log.info("Failed update resource [{}] [{}]", path, valueNew);
1157 1199 }
1158 1200 }
... ... @@ -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 1231 * @param attributesResponse -
1188 1232 * @param sessionInfo -
... ... @@ -1190,6 +1234,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1190 1234 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
1191 1235 try {
1192 1236 List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList();
  1237 +
1193 1238 this.updateAttriuteFromThingsboard(tsKvProtos, sessionInfo);
1194 1239 } catch (Exception e) {
1195 1240 log.error(String.valueOf(e));
... ... @@ -1273,7 +1318,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1273 1318 */
1274 1319 private SessionInfoProto getValidateSessionInfo(String registrationId) {
1275 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 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 1347 * @param lwM2MClient - LwM2M Client
1302 1348 */
1303 1349 public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) {
1304 1350 SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
1305 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 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 1358 transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
1313 1359 } catch (AdaptorException e) {
1314 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 1430 * @param lwM2MClient -
1326 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 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 1437 new TypeToken<ConcurrentHashMap<String, String>>() {
1335 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 1441 private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) {
... ... @@ -1353,4 +1447,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
1353 1447 objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() :
1354 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 24 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
25 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 28 import java.util.Random;
31   -import java.util.Set;
32 29
33 30 @Slf4j
34 31 @Component("LwM2MJsonAdaptor")
... ... @@ -54,11 +51,7 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
54 51 }
55 52
56 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 55 try {
63 56 TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
64 57 Random random = new Random();
... ... @@ -75,14 +68,4 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
75 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 19 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
20 20 import org.thingsboard.server.gen.transport.TransportProtos;
21 21
22   -import java.util.List;
  22 +import java.util.Collection;
23 23
24 24 public interface LwM2MTransportAdaptor {
25 25
... ... @@ -27,5 +27,5 @@ public interface LwM2MTransportAdaptor {
27 27
28 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 57 private UUID deviceId;
58 58 private UUID sessionId;
59 59 private UUID profileId;
  60 + private volatile LwM2mFirmwareUpdate frUpdate;
60 61 private Registration registration;
61 62 private ValidateDeviceCredentialsResponseMsg credentialsResponse;
62 63 private final Map<String, ResourceValue> resources;
63 64 private final Map<String, TransportProtos.TsKvProto> delayedRequests;
64   - private final List<String> pendingRequests;
  65 + private final List<String> pendingReadRequests;
65 66 private final Queue<LwM2mQueuedRequest> queuedRequests;
66 67 private boolean init;
  68 + private volatile boolean updateFw;
67 69
68 70 public Object clone() throws CloneNotSupportedException {
69 71 return super.clone();
... ... @@ -75,12 +77,14 @@ public class LwM2mClient implements Cloneable {
75 77 this.securityInfo = securityInfo;
76 78 this.credentialsResponse = credentialsResponse;
77 79 this.delayedRequests = new ConcurrentHashMap<>();
78   - this.pendingRequests = new CopyOnWriteArrayList<>();
  80 + this.pendingReadRequests = new CopyOnWriteArrayList<>();
79 81 this.resources = new ConcurrentHashMap<>();
80 82 this.profileId = profileId;
81 83 this.sessionId = sessionId;
82 84 this.init = false;
  85 + this.updateFw = false;
83 86 this.queuedRequests = new ConcurrentLinkedQueue<>();
  87 + this.frUpdate = new LwM2mFirmwareUpdate();
84 88 }
85 89
86 90 public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) {
... ... @@ -103,15 +107,13 @@ public class LwM2mClient implements Cloneable {
103 107 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez));
104 108 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
105 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 111 .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null;
108 112 }
109 113
110 114 public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, LwM2mModelProvider modelProvider,
111 115 LwM2mValueConverterImpl converter) {
112 116 LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer));
113   - String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
114   - String verRez = getVerFromPathIdVerOrId(pathRezIdVer);
115 117 Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet();
116 118 Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration)
117 119 .getObjectModel(pathIds.getObjectId()).resources;
... ... @@ -170,11 +172,11 @@ public class LwM2mClient implements Cloneable {
170 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 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 180 this.init = true;
179 181 serviceImpl.putDelayedUpdateResourcesThingsboard(this);
180 182 }
... ...
... ... @@ -82,12 +82,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
82 82
83 83 @Override
84 84 public LwM2mClient getLwM2mClientWithReg(Registration registration, String registrationId) {
85   - LwM2mClient client = registrationId != null ?
  85 + LwM2mClient client = registrationId != null && this.lwM2mClients.containsKey(registrationId) ?
86 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 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 28 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
29 29 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
30 30 import org.springframework.stereotype.Service;
  31 +import org.thingsboard.server.common.data.TbTransportService;
31 32
32 33 import javax.annotation.PostConstruct;
33 34 import javax.annotation.PreDestroy;
... ... @@ -38,7 +39,7 @@ import javax.annotation.PreDestroy;
38 39 @Service("MqttTransportService")
39 40 @ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')")
40 41 @Slf4j
41   -public class MqttTransportService {
  42 +public class MqttTransportService implements TbTransportService {
42 43
43 44 @Value("${transport.mqtt.bind_address}")
44 45 private String host;
... ... @@ -90,4 +91,9 @@ public class MqttTransportService {
90 91 }
91 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 40 <module>http</module>
41 41 <module>coap</module>
42 42 <module>lwm2m</module>
  43 + <module>snmp</module>
43 44 </modules>
44 45
45 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 +}
... ...