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 | ... | ... |
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
... | ... | @@ -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 | ... | ... |
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java
... | ... | @@ -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 | ... | ... |
common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapServerComponent.java
0 → 100644
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 | ... | ... |
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 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java
0 → 100644
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 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
0 → 100644
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 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java
0 → 100644
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 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpProtocolVersion.java
0 → 100644
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; | ... | ... |
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ServiceListChangedEvent.java
0 → 100644
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 | +} | ... | ... |
common/queue/src/main/java/org/thingsboard/server/queue/util/TbSnmpTransportComponent.java
0 → 100644
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 | } | ... | ... |
common/transport/snmp/pom.xml
0 → 100644
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> | ... | ... |
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
0 → 100644
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 | +} | ... | ... |
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduService.java
0 → 100644
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 | +} | ... | ... |