Browse Source

Merge branch 'master' of https://github.com/thingsboard/thingsboard into feature/firmware-checksum-autogenerating

pull/4503/head
zbeacon 6 years ago
parent
commit
5b1cc8ac33
  1. 4
      application/pom.xml
  2. 2385
      application/src/main/data/json/demo/dashboards/firmware.json
  3. 2
      application/src/main/data/upgrade/3.2.2/schema_update.sql
  4. 4
      application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
  5. 10
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  6. 33
      application/src/main/java/org/thingsboard/server/controller/FirmwareController.java
  7. 27
      application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
  8. 2
      application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
  9. 2
      application/src/main/java/org/thingsboard/server/service/apiusage/TbApiUsageStateService.java
  10. 5
      application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java
  11. 2
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
  12. 2
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
  13. 2
      application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerService.java
  14. 2
      application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerService.java
  15. 2
      application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java
  16. 52
      application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
  17. 2
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  18. 2
      application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java
  19. 2
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
  20. 5
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
  21. 2
      application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java
  22. 4
      application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java
  23. 19
      application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java
  24. 3
      application/src/main/java/org/thingsboard/server/service/telemetry/AlarmSubscriptionService.java
  25. 3
      application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java
  26. 129
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  27. 1
      application/src/main/resources/logback.xml
  28. 17
      application/src/main/resources/thingsboard.yml
  29. 22
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java
  30. 38
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheWriter.java
  31. 12
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java
  32. 38
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheWriter.java
  33. 31
      common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java
  34. 3
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/CoapServerContext.java
  35. 3
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java
  36. 9
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java
  37. 26
      common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapServerComponent.java
  38. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  39. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java
  40. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java
  41. 4
      common/data/pom.xml
  42. 1
      common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java
  43. 3
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java
  44. 9
      common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java
  45. 9
      common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java
  46. 6
      common/data/src/main/java/org/thingsboard/server/common/data/TbTransportService.java
  47. 11
      common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
  48. 85
      common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
  49. 13
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java
  50. 52
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java
  51. 45
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java
  52. 43
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java
  53. 25
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java
  54. 41
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java
  55. 32
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java
  56. 32
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpProtocolVersion.java
  57. 23
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/MultipleMappingsSnmpCommunicationConfig.java
  58. 36
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/RepeatingQueryingSnmpCommunicationConfig.java
  59. 56
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java
  60. 28
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ClientAttributesQueryingSnmpCommunicationConfig.java
  61. 34
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/SharedAttributesSettingSnmpCommunicationConfig.java
  62. 32
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/TelemetryQueryingSnmpCommunicationConfig.java
  63. 26
      common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ToDeviceRpcRequestSnmpCommunicationConfig.java
  64. 2
      common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java
  65. 4
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java
  66. 19
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java
  67. 20
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java
  68. 4
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java
  69. 1
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEventListener.java
  70. 5
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java
  71. 3
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ClusterTopologyChangeEvent.java
  72. 3
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java
  73. 35
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ServiceListChangedEvent.java
  74. 2
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/TbApplicationEvent.java
  75. 35
      common/queue/src/main/java/org/thingsboard/server/queue/util/AfterContextReady.java
  76. 35
      common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java
  77. 29
      common/queue/src/main/java/org/thingsboard/server/queue/util/TbSnmpTransportComponent.java
  78. 37
      common/queue/src/main/proto/queue.proto
  79. 48
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
  80. 12
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
  81. 10
      common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
  82. 3
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java
  83. 7
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java
  84. 2
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
  85. 8
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java
  86. 1
      common/transport/pom.xml
  87. 68
      common/transport/snmp/pom.xml
  88. 270
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
  89. 35
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/ServiceListChangedEventListener.java
  90. 24
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEvent.java
  91. 34
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEventListener.java
  92. 171
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduService.java
  93. 110
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/ProtoTransportEntityService.java
  94. 121
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpAuthService.java
  95. 92
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportBalancingService.java
  96. 351
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java
  97. 146
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java
  98. 196
      common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java
  99. 745
      common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java
  100. 49
      common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java

4
application/pom.xml

@ -89,6 +89,10 @@
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>
<artifactId>lwm2m</artifactId> <artifactId>lwm2m</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>snmp</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<artifactId>dao</artifactId> <artifactId>dao</artifactId>

2385
application/src/main/data/json/demo/dashboards/firmware.json

File diff suppressed because it is too large

2
application/src/main/data/upgrade/3.2.2/schema_update.sql

@ -69,7 +69,7 @@ CREATE TABLE IF NOT EXISTS firmware (
content_type varchar(255), content_type varchar(255),
checksum_algorithm varchar(32), checksum_algorithm varchar(32),
checksum varchar(1020), checksum varchar(1020),
data bytea, data oid,
data_size bigint, data_size bigint,
additional_info varchar, additional_info varchar,
search_text varchar(255), search_text varchar(255),

4
application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java

@ -25,7 +25,6 @@ import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.DefaultTbActorSystem; import org.thingsboard.server.actors.DefaultTbActorSystem;
import org.thingsboard.server.actors.TbActorId;
import org.thingsboard.server.actors.TbActorRef; import org.thingsboard.server.actors.TbActorRef;
import org.thingsboard.server.actors.TbActorSystem; import org.thingsboard.server.actors.TbActorSystem;
import org.thingsboard.server.actors.TbActorSystemSettings; import org.thingsboard.server.actors.TbActorSystemSettings;
@ -33,14 +32,13 @@ import org.thingsboard.server.actors.app.AppActor;
import org.thingsboard.server.actors.app.AppInitMsg; import org.thingsboard.server.actors.app.AppInitMsg;
import org.thingsboard.server.actors.stats.StatsActor; import org.thingsboard.server.actors.stats.StatsActor;
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@Service @Service
@Slf4j @Slf4j

10
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -75,7 +75,6 @@ import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.server.controller.EdgeController.EDGE_ID; import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
@ -120,12 +119,12 @@ public class DeviceController extends BaseController {
@ResponseBody @ResponseBody
public Device saveDevice(@RequestBody Device device, public Device saveDevice(@RequestBody Device device,
@RequestParam(name = "accessToken", required = false) String accessToken) throws ThingsboardException { @RequestParam(name = "accessToken", required = false) String accessToken) throws ThingsboardException {
boolean created = device.getId() == null;
try { try {
device.setTenantId(getCurrentUser().getTenantId()); device.setTenantId(getCurrentUser().getTenantId());
checkEntity(device.getId(), device, Resource.DEVICE); checkEntity(device.getId(), device, Resource.DEVICE);
boolean created = device.getId() == null;
Device oldDevice; Device oldDevice;
if (!created) { if (!created) {
oldDevice = deviceService.findDeviceById(getTenantId(), device.getId()); oldDevice = deviceService.findDeviceById(getTenantId(), device.getId());
@ -146,7 +145,7 @@ public class DeviceController extends BaseController {
logEntityAction(savedDevice.getId(), savedDevice, logEntityAction(savedDevice.getId(), savedDevice,
savedDevice.getCustomerId(), savedDevice.getCustomerId(),
device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); created ? ActionType.ADDED : ActionType.UPDATED, null);
if (device.getId() == null) { if (device.getId() == null) {
deviceStateService.onDeviceAdded(savedDevice); deviceStateService.onDeviceAdded(savedDevice);
@ -157,10 +156,9 @@ public class DeviceController extends BaseController {
firmwareStateService.update(savedDevice, oldDevice); firmwareStateService.update(savedDevice, oldDevice);
return savedDevice; return savedDevice;
} catch ( } catch (Exception e) {
Exception e) {
logEntityAction(emptyId(EntityType.DEVICE), device, logEntityAction(emptyId(EntityType.DEVICE), device,
null, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); null, created ? ActionType.ADDED : ActionType.UPDATED, e);
throw handleException(e); throw handleException(e);
} }

33
application/src/main/java/org/thingsboard/server/controller/FirmwareController.java

@ -29,8 +29,10 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.id.FirmwareId;
@ -72,14 +74,14 @@ public class FirmwareController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/firmware/info/{firmwareId}", method = RequestMethod.GET) @RequestMapping(value = "/firmware/info/{firmwareId}", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public FirmwareInfo getFirmwareInfoById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { public FirmwareInfo getFirmwareInfoById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException {
checkParameter(FIRMWARE_ID, strFirmwareId); checkParameter(FIRMWARE_ID, strFirmwareId);
try { try {
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
return checkFirmwareInfoId(firmwareId, Operation.READ); return checkNotNull(firmwareService.findFirmwareInfoById(getTenantId(), firmwareId));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
@ -102,11 +104,17 @@ public class FirmwareController extends BaseController {
@RequestMapping(value = "/firmware", method = RequestMethod.POST) @RequestMapping(value = "/firmware", method = RequestMethod.POST)
@ResponseBody @ResponseBody
public FirmwareInfo saveFirmwareInfo(@RequestBody FirmwareInfo firmwareInfo) throws ThingsboardException { public FirmwareInfo saveFirmwareInfo(@RequestBody FirmwareInfo firmwareInfo) throws ThingsboardException {
firmwareInfo.setTenantId(getTenantId()); boolean created = firmwareInfo.getId() == null;
checkEntity(firmwareInfo.getId(), firmwareInfo, Resource.FIRMWARE);
try { try {
return firmwareService.saveFirmwareInfo(firmwareInfo); firmwareInfo.setTenantId(getTenantId());
checkEntity(firmwareInfo.getId(), firmwareInfo, Resource.FIRMWARE);
FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo);
logEntityAction(savedFirmwareInfo.getId(), savedFirmwareInfo,
null, created ? ActionType.ADDED : ActionType.UPDATED, null);
return savedFirmwareInfo;
} catch (Exception e) { } catch (Exception e) {
logEntityAction(emptyId(EntityType.FIRMWARE), firmwareInfo,
null, created ? ActionType.ADDED : ActionType.UPDATED, e);
throw handleException(e); throw handleException(e);
} }
} }
@ -144,13 +152,16 @@ public class FirmwareController extends BaseController {
firmware.setContentType(file.getContentType()); firmware.setContentType(file.getContentType());
firmware.setData(data); firmware.setData(data);
firmware.setDataSize((long) data.capacity()); firmware.setDataSize((long) data.capacity());
return firmwareService.saveFirmware(firmware); Firmware savedFirmware = firmwareService.saveFirmware(firmware);
logEntityAction(savedFirmware.getId(), savedFirmware, null, ActionType.UPDATED, null);
return savedFirmware;
} catch (Exception e) { } catch (Exception e) {
logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.UPDATED, e, strFirmwareId);
throw handleException(e); throw handleException(e);
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/firmwares", method = RequestMethod.GET) @RequestMapping(value = "/firmwares", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public PageData<FirmwareInfo> getFirmwares(@RequestParam int pageSize, public PageData<FirmwareInfo> getFirmwares(@RequestParam int pageSize,
@ -166,7 +177,7 @@ public class FirmwareController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/firmwares/{hasData}", method = RequestMethod.GET) @RequestMapping(value = "/firmwares/{hasData}", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public PageData<FirmwareInfo> getFirmwares(@PathVariable("hasData") boolean hasData, public PageData<FirmwareInfo> getFirmwares(@PathVariable("hasData") boolean hasData,
@ -186,13 +197,15 @@ public class FirmwareController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.DELETE) @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.DELETE)
@ResponseBody @ResponseBody
public void deleteResource(@PathVariable("firmwareId") String strFirmwareId) throws ThingsboardException { public void deleteFirmware(@PathVariable("firmwareId") String strFirmwareId) throws ThingsboardException {
checkParameter(FIRMWARE_ID, strFirmwareId); checkParameter(FIRMWARE_ID, strFirmwareId);
try { try {
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
checkFirmwareInfoId(firmwareId, Operation.DELETE); FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.DELETE);
firmwareService.deleteFirmware(getTenantId(), firmwareId); firmwareService.deleteFirmware(getTenantId(), firmwareId);
logEntityAction(firmwareId, info, null, ActionType.DELETED, null, strFirmwareId);
} catch (Exception e) { } catch (Exception e) {
logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.DELETED, e, strFirmwareId);
throw handleException(e); throw handleException(e);
} }
} }

27
application/src/main/java/org/thingsboard/server/controller/TbResourceController.java

@ -28,8 +28,10 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
@ -37,7 +39,6 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.resource.TbResourceService;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
@ -103,12 +104,18 @@ public class TbResourceController extends BaseController {
@RequestMapping(value = "/resource", method = RequestMethod.POST) @RequestMapping(value = "/resource", method = RequestMethod.POST)
@ResponseBody @ResponseBody
public TbResource saveResource(@RequestBody TbResource resource) throws ThingsboardException { public TbResource saveResource(@RequestBody TbResource resource) throws ThingsboardException {
boolean created = resource.getId() == null;
try { try {
resource.setTenantId(getTenantId()); resource.setTenantId(getTenantId());
checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); checkEntity(resource.getId(), resource, Resource.TB_RESOURCE);
return addResource(resource); TbResource savedResource = checkNotNull(resourceService.saveResource(resource));
} tbClusterService.onResourceChange(savedResource, null);
catch (Exception e) { logEntityAction(savedResource.getId(), savedResource,
null, created ? ActionType.ADDED : ActionType.UPDATED, null);
return savedResource;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.TB_RESOURCE), resource,
null, created ? ActionType.ADDED : ActionType.UPDATED, e);
throw handleException(e); throw handleException(e);
} }
} }
@ -172,15 +179,11 @@ public class TbResourceController extends BaseController {
TbResource tbResource = checkResourceId(resourceId, Operation.DELETE); TbResource tbResource = checkResourceId(resourceId, Operation.DELETE);
resourceService.deleteResource(getTenantId(), resourceId); resourceService.deleteResource(getTenantId(), resourceId);
tbClusterService.onResourceDeleted(tbResource, null); tbClusterService.onResourceDeleted(tbResource, null);
logEntityAction(resourceId, tbResource, null, ActionType.DELETED, null, strResourceId);
} catch (Exception e) { } catch (Exception e) {
logEntityAction(emptyId(EntityType.TB_RESOURCE), null, null, ActionType.DELETED, e, strResourceId);
throw handleException(e); throw handleException(e);
} }
} }
private TbResource addResource(TbResource resource) throws Exception {
checkEntity(resource.getId(), resource, Resource.TB_RESOURCE);
TbResource savedResource = checkNotNull(resourceService.saveResource(resource));
tbClusterService.onResourceChange(savedResource, null);
return savedResource;
}
} }

2
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java

@ -56,7 +56,7 @@ import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto; import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.scheduler.SchedulerComponent; import org.thingsboard.server.queue.scheduler.SchedulerComponent;

2
application/src/main/java/org/thingsboard/server/service/apiusage/TbApiUsageStateService.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
public interface TbApiUsageStateService extends ApplicationListener<PartitionChangeEvent> { public interface TbApiUsageStateService extends ApplicationListener<PartitionChangeEvent> {

5
application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java

@ -22,7 +22,6 @@ import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.id.FirmwareId;
@ -35,7 +34,6 @@ import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.DeviceService;
@ -155,7 +153,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
} }
if (targetFirmwareId.equals(currentFirmwareId)) { if (targetFirmwareId.equals(currentFirmwareId)) {
update(device, firmwareService.findFirmwareById(device.getTenantId(), targetFirmwareId), ts); update(device, firmwareService.findFirmwareInfoById(device.getTenantId(), targetFirmwareId), ts);
isSuccess = true; isSuccess = true;
} else { } else {
log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetFirmwareId, currentFirmwareId); log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetFirmwareId, currentFirmwareId);
@ -187,6 +185,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
List<TsKvEntry> telemetry = new ArrayList<>(); List<TsKvEntry> telemetry = new ArrayList<>();
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle()))); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle())));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion()))); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion())));
telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(DataConstants.TARGET_FIRMWARE_TS, ts)));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.QUEUED.name()))); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.QUEUED.name())));
telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() {

2
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java

@ -55,7 +55,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceM
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueConsumer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.provider.TbCoreQueueFactory; import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService;

2
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java

@ -38,7 +38,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueConsumer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory; import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory;
import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;

2
application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerService.java

@ -16,7 +16,7 @@
package org.thingsboard.server.service.queue; package org.thingsboard.server.service.queue;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
public interface TbCoreConsumerService extends ApplicationListener<PartitionChangeEvent> { public interface TbCoreConsumerService extends ApplicationListener<PartitionChangeEvent> {

2
application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerService.java

@ -16,7 +16,7 @@
package org.thingsboard.server.service.queue; package org.thingsboard.server.service.queue;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
public interface TbRuleEngineConsumerService extends ApplicationListener<PartitionChangeEvent> { public interface TbRuleEngineConsumerService extends ApplicationListener<PartitionChangeEvent> {

2
application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java

@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueConsumer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService;

52
application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java

@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
@ -44,8 +46,10 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
@ -59,6 +63,8 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.firmware.FirmwareService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
@ -125,6 +131,12 @@ public class AccessValidator {
@Autowired @Autowired
protected ApiUsageStateService apiUsageStateService; protected ApiUsageStateService apiUsageStateService;
@Autowired
protected ResourceService resourceService;
@Autowired
protected FirmwareService firmwareService;
private ExecutorService executor; private ExecutorService executor;
@PostConstruct @PostConstruct
@ -217,6 +229,12 @@ public class AccessValidator {
case API_USAGE_STATE: case API_USAGE_STATE:
validateApiUsageState(currentUser, operation, entityId, callback); validateApiUsageState(currentUser, operation, entityId, callback);
return; return;
case TB_RESOURCE:
validateResource(currentUser, operation, entityId, callback);
return;
case FIRMWARE:
validateFirmware(currentUser, operation, entityId, callback);
return;
default: default:
//TODO: add support of other entities //TODO: add support of other entities
throw new IllegalStateException("Not Implemented!"); throw new IllegalStateException("Not Implemented!");
@ -282,6 +300,40 @@ public class AccessValidator {
} }
} }
private void validateFirmware(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else {
FirmwareInfo firmware = firmwareService.findFirmwareInfoById(currentUser.getTenantId(), new FirmwareId(entityId.getId()));
if (firmware == null) {
callback.onSuccess(ValidationResult.entityNotFound("Firmware with requested id wasn't found!"));
} else {
try {
accessControlService.checkPermission(currentUser, Resource.FIRMWARE, operation, entityId, firmware);
} catch (ThingsboardException e) {
callback.onSuccess(ValidationResult.accessDenied(e.getMessage()));
}
callback.onSuccess(ValidationResult.ok(firmware));
}
}
}
private void validateResource(SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
ListenableFuture<TbResourceInfo> resourceFuture = resourceService.findResourceInfoByIdAsync(currentUser.getTenantId(), new TbResourceId(entityId.getId()));
Futures.addCallback(resourceFuture, getCallback(callback, resource -> {
if (resource == null) {
return ValidationResult.entityNotFound("Resource with requested id wasn't found!");
} else {
try {
accessControlService.checkPermission(currentUser, Resource.TB_RESOURCE, operation, entityId, resource);
} catch (ThingsboardException e) {
return ValidationResult.accessDenied(e.getMessage());
}
return ValidationResult.ok(resource);
}
}), executor);
}
private void validateAsset(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { private void validateAsset(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
if (currentUser.isSystemAdmin()) { if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));

2
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java

@ -54,7 +54,7 @@ import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;

2
application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java

@ -18,7 +18,7 @@ package org.thingsboard.server.service.state;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;

2
application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java

@ -46,7 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdate
import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateValueListProto; import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateValueListProto;
import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.TbQueueProducer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;

5
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java

@ -20,10 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.ClusterTopologyChangeEvent; import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;

2
application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java

@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import java.util.List; import java.util.List;

4
application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java

@ -15,8 +15,8 @@
*/ */
package org.thingsboard.server.service.subscription; package org.thingsboard.server.service.subscription;
import org.thingsboard.server.queue.discovery.ClusterTopologyChangeEvent; import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;

19
application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java

@ -22,35 +22,18 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.subscription.SubscriptionManagerService; import org.thingsboard.server.service.subscription.SubscriptionManagerService;
import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import java.util.Collections;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;

3
application/src/main/java/org/thingsboard/server/service/telemetry/AlarmSubscriptionService.java

@ -17,8 +17,7 @@ package org.thingsboard.server.service.telemetry;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.thingsboard.rule.engine.api.RuleEngineAlarmService; import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
/** /**
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.

3
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java

@ -16,8 +16,7 @@
package org.thingsboard.server.service.telemetry; package org.thingsboard.server.service.telemetry;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
/** /**
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.

129
application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java

@ -23,17 +23,18 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cache.firmware.FirmwareCacheWriter; import org.thingsboard.server.cache.firmware.FirmwareDataCache;
import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
@ -45,6 +46,8 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.security.DeviceCredentialsType;
@ -61,15 +64,18 @@ import org.thingsboard.server.dao.device.provision.ProvisionRequest;
import org.thingsboard.server.dao.device.provision.ProvisionResponse; import org.thingsboard.server.dao.device.provision.ProvisionResponse;
import org.thingsboard.server.dao.firmware.FirmwareService; import org.thingsboard.server.dao.firmware.FirmwareService;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetSnmpDevicesResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
@ -92,6 +98,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
/** /**
* Created by ashvayka on 05.10.18. * Created by ashvayka on 05.10.18.
@ -116,7 +123,7 @@ public class DefaultTransportApiService implements TransportApiService {
private final DeviceProvisionService deviceProvisionService; private final DeviceProvisionService deviceProvisionService;
private final TbResourceService resourceService; private final TbResourceService resourceService;
private final FirmwareService firmwareService; private final FirmwareService firmwareService;
private final FirmwareCacheWriter firmwareCacheWriter; private final FirmwareDataCache firmwareDataCache;
private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
@ -125,7 +132,7 @@ public class DefaultTransportApiService implements TransportApiService {
RelationService relationService, DeviceCredentialsService deviceCredentialsService, RelationService relationService, DeviceCredentialsService deviceCredentialsService,
DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService,
TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService,
DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareCacheWriter firmwareCacheWriter) { DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareDataCache firmwareDataCache) {
this.deviceProfileCache = deviceProfileCache; this.deviceProfileCache = deviceProfileCache;
this.tenantProfileCache = tenantProfileCache; this.tenantProfileCache = tenantProfileCache;
this.apiUsageStateService = apiUsageStateService; this.apiUsageStateService = apiUsageStateService;
@ -139,49 +146,49 @@ public class DefaultTransportApiService implements TransportApiService {
this.deviceProvisionService = deviceProvisionService; this.deviceProvisionService = deviceProvisionService;
this.resourceService = resourceService; this.resourceService = resourceService;
this.firmwareService = firmwareService; this.firmwareService = firmwareService;
this.firmwareCacheWriter = firmwareCacheWriter; this.firmwareDataCache = firmwareDataCache;
} }
@Override @Override
public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) { public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) {
TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue(); TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue();
ListenableFuture<TransportApiResponseMsg> result = null;
if (transportApiRequestMsg.hasValidateTokenRequestMsg()) { if (transportApiRequestMsg.hasValidateTokenRequestMsg()) {
ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg(); ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg();
return Futures.transform(validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN), result = validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN);
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
} else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) { } else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) {
TransportProtos.ValidateBasicMqttCredRequestMsg msg = transportApiRequestMsg.getValidateBasicMqttCredRequestMsg(); TransportProtos.ValidateBasicMqttCredRequestMsg msg = transportApiRequestMsg.getValidateBasicMqttCredRequestMsg();
return Futures.transform(validateCredentials(msg), result = validateCredentials(msg);
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
} else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) { } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) {
ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg(); ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg();
return Futures.transform(validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE), result = validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE);
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
} else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) { } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) {
return Futures.transform(handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()), result = handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg());
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
} else if (transportApiRequestMsg.hasEntityProfileRequestMsg()) { } else if (transportApiRequestMsg.hasEntityProfileRequestMsg()) {
return Futures.transform(handle(transportApiRequestMsg.getEntityProfileRequestMsg()), result = handle(transportApiRequestMsg.getEntityProfileRequestMsg());
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
} else if (transportApiRequestMsg.hasLwM2MRequestMsg()) { } else if (transportApiRequestMsg.hasLwM2MRequestMsg()) {
return Futures.transform(handle(transportApiRequestMsg.getLwM2MRequestMsg()), result = handle(transportApiRequestMsg.getLwM2MRequestMsg());
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
} else if (transportApiRequestMsg.hasValidateDeviceLwM2MCredentialsRequestMsg()) { } else if (transportApiRequestMsg.hasValidateDeviceLwM2MCredentialsRequestMsg()) {
ValidateDeviceLwM2MCredentialsRequestMsg msg = transportApiRequestMsg.getValidateDeviceLwM2MCredentialsRequestMsg(); ValidateDeviceLwM2MCredentialsRequestMsg msg = transportApiRequestMsg.getValidateDeviceLwM2MCredentialsRequestMsg();
return Futures.transform(validateCredentials(msg.getCredentialsId(), DeviceCredentialsType.LWM2M_CREDENTIALS), result = validateCredentials(msg.getCredentialsId(), DeviceCredentialsType.LWM2M_CREDENTIALS);
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
} else if (transportApiRequestMsg.hasProvisionDeviceRequestMsg()) { } else if (transportApiRequestMsg.hasProvisionDeviceRequestMsg()) {
return Futures.transform(handle(transportApiRequestMsg.getProvisionDeviceRequestMsg()), result = handle(transportApiRequestMsg.getProvisionDeviceRequestMsg());
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
} else if (transportApiRequestMsg.hasResourceRequestMsg()) { } else if (transportApiRequestMsg.hasResourceRequestMsg()) {
return Futures.transform(handle(transportApiRequestMsg.getResourceRequestMsg()), result = handle(transportApiRequestMsg.getResourceRequestMsg());
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); } else if (transportApiRequestMsg.hasSnmpDevicesRequestMsg()) {
result = handle(transportApiRequestMsg.getSnmpDevicesRequestMsg());
} else if (transportApiRequestMsg.hasDeviceRequestMsg()) {
result = handle(transportApiRequestMsg.getDeviceRequestMsg());
} else if (transportApiRequestMsg.hasDeviceCredentialsRequestMsg()) {
result = handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg());
} else if (transportApiRequestMsg.hasFirmwareRequestMsg()) { } else if (transportApiRequestMsg.hasFirmwareRequestMsg()) {
return Futures.transform(handle(transportApiRequestMsg.getFirmwareRequestMsg()), result = handle(transportApiRequestMsg.getFirmwareRequestMsg());
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
} }
return Futures.transform(getEmptyTransportApiResponseFuture(),
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture),
value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()),
MoreExecutors.directExecutor());
} }
private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) { private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) {
@ -375,6 +382,39 @@ public class DefaultTransportApiService implements TransportApiService {
return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build()); return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build());
} }
private ListenableFuture<TransportApiResponseMsg> handle(GetDeviceRequestMsg requestMsg) {
DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
Device device = deviceService.findDeviceById(TenantId.SYS_TENANT_ID, deviceId);
TransportApiResponseMsg responseMsg;
if (device != null) {
UUID deviceProfileId = device.getDeviceProfileId().getId();
responseMsg = TransportApiResponseMsg.newBuilder()
.setDeviceResponseMsg(TransportProtos.GetDeviceResponseMsg.newBuilder()
.setDeviceProfileIdMSB(deviceProfileId.getMostSignificantBits())
.setDeviceProfileIdLSB(deviceProfileId.getLeastSignificantBits())
.setDeviceTransportConfiguration(ByteString.copyFrom(
dataDecodingEncodingService.encode(device.getDeviceData().getTransportConfiguration())
)))
.build();
} else {
responseMsg = TransportApiResponseMsg.getDefaultInstance();
}
return Futures.immediateFuture(responseMsg);
}
private ListenableFuture<TransportApiResponseMsg> handle(GetDeviceCredentialsRequestMsg requestMsg) {
DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, deviceId);
return Futures.immediateFuture(TransportApiResponseMsg.newBuilder()
.setDeviceCredentialsResponseMsg(TransportProtos.GetDeviceCredentialsResponseMsg.newBuilder()
.setDeviceCredentialsData(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceCredentials))))
.build());
}
private ListenableFuture<TransportApiResponseMsg> handle(GetResourceRequestMsg requestMsg) { private ListenableFuture<TransportApiResponseMsg> handle(GetResourceRequestMsg requestMsg) {
TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType()); ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType());
@ -393,6 +433,22 @@ public class DefaultTransportApiService implements TransportApiService {
return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setResourceResponseMsg(builder).build()); return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setResourceResponseMsg(builder).build());
} }
private ListenableFuture<TransportApiResponseMsg> handle(GetSnmpDevicesRequestMsg requestMsg) {
PageLink pageLink = new PageLink(requestMsg.getPageSize(), requestMsg.getPage());
PageData<UUID> result = deviceService.findDevicesIdsByDeviceProfileTransportType(DeviceTransportType.SNMP, pageLink);
GetSnmpDevicesResponseMsg responseMsg = GetSnmpDevicesResponseMsg.newBuilder()
.addAllIds(result.getData().stream()
.map(UUID::toString)
.collect(Collectors.toList()))
.setHasNextPage(result.hasNext())
.build();
return Futures.immediateFuture(TransportApiResponseMsg.newBuilder()
.setSnmpDevicesResponseMsg(responseMsg)
.build());
}
private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) { private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) {
return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, deviceId), device -> { return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, deviceId), device -> {
if (device == null) { if (device == null) {
@ -473,19 +529,22 @@ public class DefaultTransportApiService implements TransportApiService {
if (firmwareId == null) { if (firmwareId == null) {
builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND); builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND);
} else { } else {
Firmware firmware = firmwareService.findFirmwareById(tenantId, firmwareId); FirmwareInfo firmwareInfo = firmwareService.findFirmwareInfoById(tenantId, firmwareId);
if (firmware == null) { if (firmwareInfo == null) {
builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND); builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND);
} else { } else {
builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS); builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS);
builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()); builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits());
builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()); builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits());
builder.setTitle(firmware.getTitle()); builder.setTitle(firmwareInfo.getTitle());
builder.setVersion(firmware.getVersion()); builder.setVersion(firmwareInfo.getVersion());
builder.setFileName(firmware.getFileName()); builder.setFileName(firmwareInfo.getFileName());
builder.setContentType(firmware.getContentType()); builder.setContentType(firmwareInfo.getContentType());
firmwareCacheWriter.put(firmwareId.toString(), firmware.getData().array()); if (!firmwareDataCache.has(firmwareId.toString())) {
Firmware firmware = firmwareService.findFirmwareById(tenantId, firmwareId);
firmwareDataCache.put(firmwareId.toString(), firmware.getData().array());
}
} }
} }

1
application/src/main/resources/logback.xml

@ -26,6 +26,7 @@
</appender> </appender>
<logger name="org.thingsboard.server" level="INFO" /> <logger name="org.thingsboard.server" level="INFO" />
<logger name="org.thingsboard.server.transport.snmp" level="TRACE" />
<!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />--> <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />-->
<!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />--> <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />-->

17
application/src/main/resources/thingsboard.yml

@ -372,8 +372,8 @@ caffeine:
timeToLiveInMinutes: 20000 timeToLiveInMinutes: 20000
maxSize: 10000 maxSize: 10000
firmwares: firmwares:
timeToLiveInMinutes: 1440 timeToLiveInMinutes: 60
maxSize: 100 maxSize: 10
edges: edges:
timeToLiveInMinutes: 1440 timeToLiveInMinutes: 1440
maxSize: 0 maxSize: 0
@ -496,6 +496,8 @@ audit-log:
"entity_view": "${AUDIT_LOG_MASK_ENTITY_VIEW:W}" "entity_view": "${AUDIT_LOG_MASK_ENTITY_VIEW:W}"
"device_profile": "${AUDIT_LOG_MASK_DEVICE_PROFILE:W}" "device_profile": "${AUDIT_LOG_MASK_DEVICE_PROFILE:W}"
"edge": "${AUDIT_LOG_MASK_EDGE:W}" "edge": "${AUDIT_LOG_MASK_EDGE:W}"
"tb_resource": "${AUDIT_LOG_MASK_RESOURCE:W}"
"firmware": "${AUDIT_LOG_MASK_FIRMWARE:W}"
sink: sink:
# Type of external sink. possible options: none, elasticsearch # Type of external sink. possible options: none, elasticsearch
type: "${AUDIT_LOG_SINK_TYPE:none}" type: "${AUDIT_LOG_SINK_TYPE:none}"
@ -619,9 +621,9 @@ transport:
key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}" key_password: "${COAP_DTLS_KEY_PASSWORD:server_key_password}"
# Key alias # Key alias
key_alias: "${COAP_DTLS_KEY_ALIAS:serveralias}" key_alias: "${COAP_DTLS_KEY_ALIAS:serveralias}"
# Skip certificate validity check for client certificates.
skip_validity_check_for_client_cert: "${COAP_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
x509: x509:
# Skip certificate validity check for client certificates.
skip_validity_check_for_client_cert: "${TB_COAP_X509_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
dtls_session_inactivity_timeout: "${TB_COAP_X509_DTLS_SESSION_INACTIVITY_TIMEOUT:86400000}" dtls_session_inactivity_timeout: "${TB_COAP_X509_DTLS_SESSION_INACTIVITY_TIMEOUT:86400000}"
dtls_session_report_timeout: "${TB_COAP_X509_DTLS_SESSION_REPORT_TIMEOUT:1800000}" dtls_session_report_timeout: "${TB_COAP_X509_DTLS_SESSION_REPORT_TIMEOUT:1800000}"
# Local LwM2M transport parameters # Local LwM2M transport parameters
@ -684,6 +686,13 @@ transport:
alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}" alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}"
# Use redis for Security and Registration stores # Use redis for Security and Registration stores
redis.enabled: "${LWM2M_REDIS_ENABLED:false}" redis.enabled: "${LWM2M_REDIS_ENABLED:false}"
snmp:
enabled: "${SNMP_ENABLED:true}"
response_processing:
# parallelism level for executor (workStealingPool) that is responsible for handling responses from SNMP devices
parallelism_level: "${SNMP_RESPONSE_PROCESSING_PARALLELISM_LEVEL:20}"
# to configure SNMP to work over UDP or TCP
underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}"
# Edges parameters # Edges parameters
edges: edges:

22
common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheReader.java → common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java

@ -15,22 +15,20 @@
*/ */
package org.thingsboard.server.cache.firmware; package org.thingsboard.server.cache.firmware;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
@Service @Service
@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='null')") @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
public class CaffeineFirmwareCacheReader implements FirmwareCacheReader { @RequiredArgsConstructor
public class CaffeineFirmwareCache implements FirmwareDataCache {
private final CacheManager cacheManager; private final CacheManager cacheManager;
public CaffeineFirmwareCacheReader(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
@Override @Override
public byte[] get(String key) { public byte[] get(String key) {
return get(key, 0, 0); return get(key, 0, 0);
@ -57,4 +55,14 @@ public class CaffeineFirmwareCacheReader implements FirmwareCacheReader {
} }
return new byte[0]; return new byte[0];
} }
@Override
public void put(String key, byte[] value) {
cacheManager.getCache(FIRMWARE_CACHE).putIfAbsent(key, value);
}
@Override
public void evict(String key) {
cacheManager.getCache(FIRMWARE_CACHE).evict(key);
}
} }

38
common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCacheWriter.java

@ -1,38 +0,0 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.cache.firmware;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
@Service
@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && ('${cache.type:null}'=='caffeine' || '${cache.type:null}'=='null')")
public class CaffeineFirmwareCacheWriter implements FirmwareCacheWriter {
private final CacheManager cacheManager;
public CaffeineFirmwareCacheWriter(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
@Override
public void put(String key, byte[] value) {
cacheManager.getCache(FIRMWARE_CACHE).putIfAbsent(key, value);
}
}

12
common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheReader.java → common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java

@ -15,8 +15,18 @@
*/ */
package org.thingsboard.server.cache.firmware; package org.thingsboard.server.cache.firmware;
public interface FirmwareCacheReader { public interface FirmwareDataCache {
byte[] get(String key); byte[] get(String key);
byte[] get(String key, int chunkSize, int chunk); byte[] get(String key, int chunkSize, int chunk);
void put(String key, byte[] value);
void evict(String key);
default boolean has(String firmwareId) {
byte[] data = get(firmwareId, 1, 0);
return data != null && data.length > 0;
}
} }

38
common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheWriter.java

@ -1,38 +0,0 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.cache.firmware;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Service;
@Service
@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && '${cache.type:null}'=='redis'")
public class RedisFirmwareCacheWriter extends AbstractRedisFirmwareCache implements FirmwareCacheWriter {
public RedisFirmwareCacheWriter(RedisConnectionFactory redisConnectionFactory) {
super(redisConnectionFactory);
}
@Override
public void put(String key, byte[] value) {
try (RedisConnection connection = redisConnectionFactory.getConnection()) {
connection.set(toFirmwareCacheKey(key), value);
}
}
}

31
common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareCacheReader.java → common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java

@ -15,18 +15,20 @@
*/ */
package org.thingsboard.server.cache.firmware; package org.thingsboard.server.cache.firmware;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
@Service @Service
@ConditionalOnExpression("(('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport') && '${cache.type:null}'=='redis'") @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
public class RedisFirmwareCacheReader extends AbstractRedisFirmwareCache implements FirmwareCacheReader { @RequiredArgsConstructor
public class RedisFirmwareDataCache implements FirmwareDataCache {
public RedisFirmwareCacheReader(RedisConnectionFactory redisConnectionFactory) { private final RedisConnectionFactory redisConnectionFactory;
super(redisConnectionFactory);
}
@Override @Override
public byte[] get(String key) { public byte[] get(String key) {
@ -46,4 +48,21 @@ public class RedisFirmwareCacheReader extends AbstractRedisFirmwareCache impleme
} }
} }
@Override
public void put(String key, byte[] value) {
try (RedisConnection connection = redisConnectionFactory.getConnection()) {
connection.set(toFirmwareCacheKey(key), value);
}
}
@Override
public void evict(String key) {
try (RedisConnection connection = redisConnectionFactory.getConnection()) {
connection.del(toFirmwareCacheKey(key));
}
}
private byte[] toFirmwareCacheKey(String key) {
return String.format("%s::%s", FIRMWARE_CACHE, key).getBytes();
}
} }

3
common/coap-server/src/main/java/org/thingsboard/server/coapserver/CoapServerContext.java

@ -19,11 +19,10 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j @Slf4j
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") @TbCoapServerComponent
@Component @Component
public class CoapServerContext { public class CoapServerContext {

3
common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java

@ -23,7 +23,6 @@ import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.scandium.DTLSConnector; import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -39,7 +38,7 @@ import java.util.concurrent.TimeUnit;
@Slf4j @Slf4j
@Component @Component
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") @TbCoapServerComponent
public class DefaultCoapServerService implements CoapServerService { public class DefaultCoapServerService implements CoapServerService {
@Autowired @Autowired

9
common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java

@ -39,7 +39,6 @@ import java.util.Collections;
import java.util.Optional; import java.util.Optional;
@Slf4j @Slf4j
@ConditionalOnExpression("'${transport.coap.enabled}'=='true'")
@ConditionalOnProperty(prefix = "transport.coap.dtls", value = "enabled", havingValue = "true", matchIfMissing = false) @ConditionalOnProperty(prefix = "transport.coap.dtls", value = "enabled", havingValue = "true", matchIfMissing = false)
@Component @Component
public class TbCoapDtlsSettings { public class TbCoapDtlsSettings {
@ -50,7 +49,7 @@ public class TbCoapDtlsSettings {
@Value("${transport.coap.dtls.bind_port}") @Value("${transport.coap.dtls.bind_port}")
private Integer port; private Integer port;
@Value("${transport.coap.dtls.mode}") @Value("${transport.coap.dtls.mode:NO_AUTH}")
private String mode; private String mode;
@Value("${transport.coap.dtls.key_store}") @Value("${transport.coap.dtls.key_store}")
@ -65,13 +64,13 @@ public class TbCoapDtlsSettings {
@Value("${transport.coap.dtls.key_alias}") @Value("${transport.coap.dtls.key_alias}")
private String keyAlias; private String keyAlias;
@Value("${transport.coap.dtls.skip_validity_check_for_client_cert}") @Value("${transport.coap.dtls.x509.skip_validity_check_for_client_cert:false}")
private boolean skipValidityCheckForClientCert; private boolean skipValidityCheckForClientCert;
@Value("${transport.coap.dtls.x509.dtls_session_inactivity_timeout}") @Value("${transport.coap.dtls.x509.dtls_session_inactivity_timeout:86400000}")
private long dtlsSessionInactivityTimeout; private long dtlsSessionInactivityTimeout;
@Value("${transport.coap.dtls.x509.dtls_session_report_timeout}") @Value("${transport.coap.dtls.x509.dtls_session_report_timeout:1800000}")
private long dtlsSessionReportTimeout; private long dtlsSessionReportTimeout;
@Autowired @Autowired

26
common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapServerComponent.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.coapserver;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')")
public @interface TbCoapServerComponent {
}

4
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
@ -32,6 +33,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.device.provision.ProvisionRequest;
import java.util.List; import java.util.List;
import java.util.UUID;
public interface DeviceService { public interface DeviceService {
@ -93,6 +95,8 @@ public interface DeviceService {
Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile); Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile);
PageData<UUID> findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType, PageLink pageLink);
Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);

3
common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.dao.firmware; package org.thingsboard.server.dao.firmware;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -40,6 +41,8 @@ public interface FirmwareService {
FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId); FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId);
ListenableFuture<FirmwareInfo> findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId);
PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink); PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink);
PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink); PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink);

3
common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.dao.resource; package org.thingsboard.server.dao.resource;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.TbResourceInfo;
@ -34,6 +35,8 @@ public interface ResourceService {
TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId); TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId);
ListenableFuture<TbResourceInfo> findResourceInfoByIdAsync(TenantId tenantId, TbResourceId resourceId);
PageData<TbResourceInfo> findAllTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink); PageData<TbResourceInfo> findAllTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink);
PageData<TbResourceInfo> findTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink); PageData<TbResourceInfo> findTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink);

4
common/data/pom.xml

@ -87,6 +87,10 @@
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<artifactId>protobuf-dynamic</artifactId> <artifactId>protobuf-dynamic</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

1
common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java

@ -99,6 +99,7 @@ public class DataConstants {
public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version"; public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version";
public static final String TARGET_FIRMWARE_TITLE = "target_fw_title"; public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";
public static final String TARGET_FIRMWARE_VERSION = "target_fw_version"; public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";
public static final String TARGET_FIRMWARE_TS = "target_fw_ts";
public static final String FIRMWARE_STATE = "fw_state"; public static final String FIRMWARE_STATE = "fw_state";
//attributes //attributes

3
common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java

@ -18,6 +18,7 @@ package org.thingsboard.server.common.data;
public enum DeviceTransportType { public enum DeviceTransportType {
DEFAULT, DEFAULT,
MQTT, MQTT,
COAP,
LWM2M, LWM2M,
COAP SNMP
} }

9
common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.common.data; package org.thingsboard.server.common.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -25,7 +26,7 @@ import org.thingsboard.server.common.data.id.TenantId;
@Slf4j @Slf4j
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> implements HasTenantId { public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> implements HasName, HasTenantId {
private static final long serialVersionUID = 3168391583570815419L; private static final long serialVersionUID = 3168391583570815419L;
@ -65,4 +66,10 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
public String getSearchText() { public String getSearchText() {
return title; return title;
} }
@Override
@JsonIgnore
public String getName() {
return title;
}
} }

9
common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.common.data; package org.thingsboard.server.common.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -25,7 +26,7 @@ import org.thingsboard.server.common.data.validation.NoXss;
@Slf4j @Slf4j
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class TbResourceInfo extends SearchTextBased<TbResourceId> implements HasTenantId { public class TbResourceInfo extends SearchTextBased<TbResourceId> implements HasName, HasTenantId {
private static final long serialVersionUID = 7282664529021651736L; private static final long serialVersionUID = 7282664529021651736L;
@ -53,6 +54,12 @@ public class TbResourceInfo extends SearchTextBased<TbResourceId> implements Has
this.searchText = resourceInfo.getSearchText(); this.searchText = resourceInfo.getSearchText();
} }
@Override
@JsonIgnore
public String getName() {
return title;
}
@Override @Override
public String getSearchText() { public String getSearchText() {
return searchText != null ? searchText : title; return searchText != null ? searchText : title;

6
common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareCacheWriter.java → common/data/src/main/java/org/thingsboard/server/common/data/TbTransportService.java

@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.cache.firmware; package org.thingsboard.server.common.data;
public interface FirmwareCacheWriter { public interface TbTransportService {
void put(String key, byte[] value); String getName();
} }

11
common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java

@ -21,6 +21,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
import java.io.Serializable;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo( @JsonTypeInfo(
use = JsonTypeInfo.Id.NAME, use = JsonTypeInfo.Id.NAME,
@ -29,11 +31,14 @@ import org.thingsboard.server.common.data.DeviceTransportType;
@JsonSubTypes({ @JsonSubTypes({
@JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"), @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"),
@JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"), @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"),
@JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP"),
@JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"), @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"),
@JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP")}) @JsonSubTypes.Type(value = SnmpDeviceTransportConfiguration.class, name = "SNMP")})
public interface DeviceTransportConfiguration { public interface DeviceTransportConfiguration extends Serializable {
@JsonIgnore @JsonIgnore
DeviceTransportType getType(); DeviceTransportType getType();
default void validate() {
}
} }

85
common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java

@ -0,0 +1,85 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.device.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.ToString;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.transport.snmp.AuthenticationProtocol;
import org.thingsboard.server.common.data.transport.snmp.PrivacyProtocol;
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
import java.util.Objects;
@Data
@ToString(of = {"host", "port", "protocolVersion"})
public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration {
private String host;
private Integer port;
private SnmpProtocolVersion protocolVersion;
/*
* For SNMP v1 and v2c
* */
private String community;
/*
* For SNMP v3
* */
private String username;
private String securityName;
private String contextName;
private AuthenticationProtocol authenticationProtocol;
private String authenticationPassphrase;
private PrivacyProtocol privacyProtocol;
private String privacyPassphrase;
private String engineId;
@Override
public DeviceTransportType getType() {
return DeviceTransportType.SNMP;
}
@Override
public void validate() {
if (!isValid()) {
throw new IllegalArgumentException("Transport configuration is not valid");
}
}
@JsonIgnore
private boolean isValid() {
boolean isValid = StringUtils.isNotBlank(host) && port != null && protocolVersion != null;
if (isValid) {
switch (protocolVersion) {
case V1:
case V2C:
isValid = StringUtils.isNotEmpty(community);
break;
case V3:
isValid = StringUtils.isNotBlank(username) && StringUtils.isNotBlank(securityName)
&& contextName != null && authenticationProtocol != null
&& StringUtils.isNotBlank(authenticationPassphrase)
&& privacyProtocol != null && privacyPassphrase != null && engineId != null;
break;
}
}
return isValid;
}
}

13
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java

@ -29,13 +29,18 @@ import java.io.Serializable;
include = JsonTypeInfo.As.PROPERTY, include = JsonTypeInfo.As.PROPERTY,
property = "type") property = "type")
@JsonSubTypes({ @JsonSubTypes({
@JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"), @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),
@JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"), @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
@JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"), @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"),
@JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP")}) @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP"),
@JsonSubTypes.Type(value = SnmpDeviceProfileTransportConfiguration.class, name = "SNMP")
})
public interface DeviceProfileTransportConfiguration extends Serializable { public interface DeviceProfileTransportConfiguration extends Serializable {
@JsonIgnore @JsonIgnore
DeviceTransportType getType(); DeviceTransportType getType();
default void validate() {
}
} }

52
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java

@ -0,0 +1,52 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.device.profile;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
import java.util.List;
@Data
public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
private Integer timeoutMs;
private Integer retries;
private List<SnmpCommunicationConfig> communicationConfigs;
@Override
public DeviceTransportType getType() {
return DeviceTransportType.SNMP;
}
@Override
public void validate() {
if (!isValid()) {
throw new IllegalArgumentException("SNMP transport configuration is not valid");
}
}
@JsonIgnore
private boolean isValid() {
return timeoutMs != null && timeoutMs >= 0 && retries != null && retries >= 0
&& communicationConfigs != null
&& communicationConfigs.stream().allMatch(config -> config != null && config.isValid())
&& communicationConfigs.stream().flatMap(config -> config.getAllMappings().stream()).map(SnmpMapping::getOid)
.distinct().count() == communicationConfigs.stream().mapToInt(config -> config.getAllMappings().size()).sum();
}
}

45
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/AuthenticationProtocol.java

@ -0,0 +1,45 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp;
import java.util.Arrays;
import java.util.Optional;
public enum AuthenticationProtocol {
SHA_1("1.3.6.1.6.3.10.1.1.3"),
SHA_224("1.3.6.1.6.3.10.1.1.4"),
SHA_256("1.3.6.1.6.3.10.1.1.5"),
SHA_384("1.3.6.1.6.3.10.1.1.6"),
SHA_512("1.3.6.1.6.3.10.1.1.7"),
MD5("1.3.6.1.6.3.10.1.1.2");
// oids taken from org.snmp4j.security.SecurityProtocol implementations
private final String oid;
AuthenticationProtocol(String oid) {
this.oid = oid;
}
public String getOid() {
return oid;
}
public static Optional<AuthenticationProtocol> forName(String name) {
return Arrays.stream(values())
.filter(protocol -> protocol.name().equalsIgnoreCase(name))
.findFirst();
}
}

43
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/PrivacyProtocol.java

@ -0,0 +1,43 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp;
import java.util.Arrays;
import java.util.Optional;
public enum PrivacyProtocol {
DES("1.3.6.1.6.3.10.1.2.2"),
AES_128("1.3.6.1.6.3.10.1.2.4"),
AES_192("1.3.6.1.4.1.4976.2.2.1.1.1"),
AES_256("1.3.6.1.4.1.4976.2.2.1.1.2");
// oids taken from org.snmp4j.security.SecurityProtocol implementations
private final String oid;
PrivacyProtocol(String oid) {
this.oid = oid;
}
public String getOid() {
return oid;
}
public static Optional<PrivacyProtocol> forName(String name) {
return Arrays.stream(values())
.filter(protocol -> protocol.name().equalsIgnoreCase(name))
.findFirst();
}
}

25
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java

@ -0,0 +1,25 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp;
public enum SnmpCommunicationSpec {
TELEMETRY_QUERYING,
CLIENT_ATTRIBUTES_QUERYING,
SHARED_ATTRIBUTES_SETTING,
TO_DEVICE_RPC_REQUEST,
}

41
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMapping.java

@ -0,0 +1,41 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.kv.DataType;
import java.util.regex.Pattern;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SnmpMapping {
private String oid;
private String key;
private DataType dataType;
private static final Pattern OID_PATTERN = Pattern.compile("^\\.?([0-2])((\\.0)|(\\.[1-9][0-9]*))*$");
@JsonIgnore
public boolean isValid() {
return StringUtils.isNotEmpty(oid) && OID_PATTERN.matcher(oid).matches() && StringUtils.isNotBlank(key);
}
}

32
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp;
public enum SnmpMethod {
GET(-96),
SET(-93);
// codes taken from org.snmp4j.PDU class
private final int code;
SnmpMethod(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}

32
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpProtocolVersion.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp;
public enum SnmpProtocolVersion {
V1(0),
V2C(1),
V3(3);
private final int code;
SnmpProtocolVersion(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}

23
common/cache/src/main/java/org/thingsboard/server/cache/firmware/AbstractRedisFirmwareCache.java → common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/MultipleMappingsSnmpCommunicationConfig.java

@ -13,21 +13,24 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.cache.firmware; package org.thingsboard.server.common.data.transport.snmp.config;
import org.springframework.data.redis.connection.RedisConnectionFactory; import lombok.Data;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; import java.util.List;
public abstract class AbstractRedisFirmwareCache { @Data
public abstract class MultipleMappingsSnmpCommunicationConfig implements SnmpCommunicationConfig {
protected List<SnmpMapping> mappings;
protected final RedisConnectionFactory redisConnectionFactory; @Override
public boolean isValid() {
protected AbstractRedisFirmwareCache(RedisConnectionFactory redisConnectionFactory) { return mappings != null && !mappings.isEmpty() && mappings.stream().allMatch(mapping -> mapping != null && mapping.isValid());
this.redisConnectionFactory = redisConnectionFactory;
} }
protected byte[] toFirmwareCacheKey(String key) { @Override
return String.format("%s::%s", FIRMWARE_CACHE, key).getBytes(); public List<SnmpMapping> getAllMappings() {
return mappings;
} }
} }

36
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/RepeatingQueryingSnmpCommunicationConfig.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
@EqualsAndHashCode(callSuper = true)
@Data
public abstract class RepeatingQueryingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
private Long queryingFrequencyMs;
@Override
public SnmpMethod getMethod() {
return SnmpMethod.GET;
}
@Override
public boolean isValid() {
return queryingFrequencyMs != null && queryingFrequencyMs > 0 && super.isValid();
}
}

56
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java

@ -0,0 +1,56 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.config;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
import org.thingsboard.server.common.data.transport.snmp.config.impl.ClientAttributesQueryingSnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.config.impl.SharedAttributesSettingSnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.config.impl.TelemetryQueryingSnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.config.impl.ToDeviceRpcRequestSnmpCommunicationConfig;
import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "spec")
@JsonSubTypes({
@Type(value = TelemetryQueryingSnmpCommunicationConfig.class, name = "TELEMETRY_QUERYING"),
@Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"),
@Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING"),
@Type(value = ToDeviceRpcRequestSnmpCommunicationConfig.class, name = "TO_DEVICE_RPC_REQUEST")
})
public interface SnmpCommunicationConfig {
SnmpCommunicationSpec getSpec();
@JsonIgnore
default SnmpMethod getMethod() {
return null;
}
@JsonIgnore
List<SnmpMapping> getAllMappings();
@JsonIgnore
boolean isValid();
}

28
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ClientAttributesQueryingSnmpCommunicationConfig.java

@ -0,0 +1,28 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.config.impl;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
public class ClientAttributesQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING;
}
}

34
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/SharedAttributesSettingSnmpCommunicationConfig.java

@ -0,0 +1,34 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.config.impl;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig;
public class SharedAttributesSettingSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING;
}
@Override
public SnmpMethod getMethod() {
return SnmpMethod.SET;
}
}

32
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/TelemetryQueryingSnmpCommunicationConfig.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.config.impl;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
@EqualsAndHashCode(callSuper = true)
@Data
public class TelemetryQueryingSnmpCommunicationConfig extends RepeatingQueryingSnmpCommunicationConfig {
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.TELEMETRY_QUERYING;
}
}

26
common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/impl/ToDeviceRpcRequestSnmpCommunicationConfig.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.transport.snmp.config.impl;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.config.MultipleMappingsSnmpCommunicationConfig;
public class ToDeviceRpcRequestSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig {
@Override
public SnmpCommunicationSpec getSpec() {
return SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST;
}
}

2
common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java

@ -16,5 +16,5 @@
package org.thingsboard.server.common.msg.session; package org.thingsboard.server.common.msg.session;
public enum FeatureType { public enum FeatureType {
ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE
} }

4
common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java

@ -30,7 +30,9 @@ public enum SessionMsgType {
SESSION_OPEN, SESSION_CLOSE, SESSION_OPEN, SESSION_CLOSE,
CLAIM_REQUEST(); CLAIM_REQUEST(),
GET_FIRMWARE_REQUEST;
private final boolean requiresRulesProcessing; private final boolean requiresRulesProcessing;

19
common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java

@ -19,19 +19,23 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
import org.thingsboard.server.queue.util.AfterContextReady;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -56,6 +60,8 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
@Autowired(required = false) @Autowired(required = false)
private TbQueueRuleEngineSettings ruleEngineSettings; private TbQueueRuleEngineSettings ruleEngineSettings;
@Autowired
private ApplicationContext applicationContext;
private List<ServiceType> serviceTypes; private List<ServiceType> serviceTypes;
private ServiceInfo serviceInfo; private ServiceInfo serviceInfo;
@ -102,6 +108,19 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
serviceInfo = builder.build(); serviceInfo = builder.build();
} }
@AfterContextReady
public void setTransports() {
serviceInfo = ServiceInfo.newBuilder(serviceInfo)
.addAllTransports(getTransportServices().stream()
.map(TbTransportService::getName)
.collect(Collectors.toSet()))
.build();
}
private Collection<TbTransportService> getTransportServices() {
return applicationContext.getBeansOfType(TbTransportService.class).values();
}
@Override @Override
public ServiceInfo getServiceInfo() { public ServiceInfo getServiceInfo() {
return serviceInfo; return serviceInfo;

20
common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java

@ -15,26 +15,27 @@
*/ */
package org.thingsboard.server.queue.discovery; package org.thingsboard.server.queue.discovery;
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction; import com.google.common.hash.HashFunction;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
import org.thingsboard.server.common.msg.queue.ServiceQueue; import org.thingsboard.server.common.msg.queue.ServiceQueue;
import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -46,7 +47,6 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@ -186,6 +186,8 @@ public class HashPartitionService implements PartitionService {
applicationEventPublisher.publishEvent(new ClusterTopologyChangeEvent(this, changes)); applicationEventPublisher.publishEvent(new ClusterTopologyChangeEvent(this, changes));
} }
} }
applicationEventPublisher.publishEvent(new ServiceListChangedEvent(otherServices, currentService));
} }
@Override @Override
@ -219,6 +221,14 @@ public class HashPartitionService implements PartitionService {
} }
} }
@Override
public int resolvePartitionIndex(UUID entityId, int partitions) {
int hash = hashFunction.newHasher()
.putLong(entityId.getMostSignificantBits())
.putLong(entityId.getLeastSignificantBits()).hash().asInt();
return Math.abs(hash % partitions);
}
private Map<ServiceQueueKey, List<ServiceInfo>> getServiceKeyListMap(List<ServiceInfo> services) { private Map<ServiceQueueKey, List<ServiceInfo>> getServiceKeyListMap(List<ServiceInfo> services) {
final Map<ServiceQueueKey, List<ServiceInfo>> currentMap = new HashMap<>(); final Map<ServiceQueueKey, List<ServiceInfo>> currentMap = new HashMap<>();
services.forEach(serviceInfo -> { services.forEach(serviceInfo -> {

4
common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java

@ -20,9 +20,11 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID;
/** /**
* Once application is ready or cluster topology changes, this Service will produce {@link PartitionChangeEvent} * Once application is ready or cluster topology changes, this Service will produce {@link PartitionChangeEvent}
@ -55,4 +57,6 @@ public interface PartitionService {
* @return * @return
*/ */
TopicPartitionInfo getNotificationsTopic(ServiceType serviceType, String serviceId); TopicPartitionInfo getNotificationsTopic(ServiceType serviceType, String serviceId);
int resolvePartitionIndex(UUID entityId, int partitions);
} }

1
common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEventListener.java

@ -17,6 +17,7 @@ package org.thingsboard.server.queue.discovery;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.thingsboard.server.queue.discovery.event.TbApplicationEvent;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;

5
common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java

@ -33,12 +33,14 @@ import org.apache.zookeeper.KeeperException;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@ -77,7 +79,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
private volatile boolean stopped = true; private volatile boolean stopped = true;
public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider, PartitionService partitionService) { public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider,
PartitionService partitionService) {
this.serviceInfoProvider = serviceInfoProvider; this.serviceInfoProvider = serviceInfoProvider;
this.partitionService = partitionService; this.partitionService = partitionService;
} }

3
common/queue/src/main/java/org/thingsboard/server/queue/discovery/ClusterTopologyChangeEvent.java → common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ClusterTopologyChangeEvent.java

@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.queue.discovery; package org.thingsboard.server.queue.discovery.event;
import lombok.Getter; import lombok.Getter;
import org.springframework.context.ApplicationEvent;
import org.thingsboard.server.common.msg.queue.ServiceQueueKey; import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
import java.util.Set; import java.util.Set;

3
common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionChangeEvent.java → common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java

@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.queue.discovery; package org.thingsboard.server.queue.discovery.event;
import lombok.Getter; import lombok.Getter;
import org.springframework.context.ApplicationEvent;
import org.thingsboard.server.common.msg.queue.ServiceQueueKey; import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;

35
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/ServiceListChangedEvent.java

@ -0,0 +1,35 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.queue.discovery.event;
import lombok.Getter;
import lombok.ToString;
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
import java.util.List;
@Getter
@ToString
public class ServiceListChangedEvent extends TbApplicationEvent {
private final List<ServiceInfo> otherServices;
private final ServiceInfo currentService;
public ServiceListChangedEvent(List<ServiceInfo> otherServices, ServiceInfo currentService) {
super(otherServices);
this.otherServices = otherServices;
this.currentService = currentService;
}
}

2
common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEvent.java → common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/TbApplicationEvent.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.thingsboard.server.queue.discovery; package org.thingsboard.server.queue.discovery.event;
import lombok.Getter; import lombok.Getter;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;

35
common/queue/src/main/java/org/thingsboard/server/queue/util/AfterContextReady.java

@ -0,0 +1,35 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.queue.util;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.Order;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventListener(ContextRefreshedEvent.class)
@Order
public @interface AfterContextReady {
@AliasFor(annotation = Order.class, attribute = "value")
int order() default Integer.MAX_VALUE;
}

35
common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java

@ -0,0 +1,35 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.queue.util;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.Order;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventListener(ApplicationReadyEvent.class)
@Order
public @interface AfterStartUp {
@AliasFor(annotation = Order.class, attribute = "value")
int order() default Integer.MAX_VALUE;
}

29
common/queue/src/main/java/org/thingsboard/server/queue/util/TbSnmpTransportComponent.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.queue.util;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')")
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface TbSnmpTransportComponent {
}

37
common/queue/src/main/proto/queue.proto

@ -34,6 +34,7 @@ message ServiceInfo {
int64 tenantIdMSB = 3; int64 tenantIdMSB = 3;
int64 tenantIdLSB = 4; int64 tenantIdLSB = 4;
repeated QueueInfo ruleEngineQueues = 5; repeated QueueInfo ruleEngineQueues = 5;
repeated string transports = 6;
} }
/** /**
@ -246,6 +247,36 @@ message GetEntityProfileResponseMsg {
bytes apiState = 3; bytes apiState = 3;
} }
message GetDeviceRequestMsg {
int64 deviceIdMSB = 1;
int64 deviceIdLSB = 2;
}
message GetDeviceResponseMsg {
int64 deviceProfileIdMSB = 1;
int64 deviceProfileIdLSB = 2;
bytes deviceTransportConfiguration = 3;
}
message GetDeviceCredentialsRequestMsg {
int64 deviceIdMSB = 1;
int64 deviceIdLSB = 2;
}
message GetDeviceCredentialsResponseMsg {
bytes deviceCredentialsData = 1;
}
message GetSnmpDevicesRequestMsg {
int32 page = 1;
int32 pageSize = 2;
}
message GetSnmpDevicesResponseMsg {
repeated string ids = 1;
bool hasNextPage = 2;
}
message EntityUpdateMsg { message EntityUpdateMsg {
string entityType = 1; string entityType = 1;
bytes data = 2; bytes data = 2;
@ -590,6 +621,9 @@ message TransportApiRequestMsg {
ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8; ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8;
GetResourceRequestMsg resourceRequestMsg = 9; GetResourceRequestMsg resourceRequestMsg = 9;
GetFirmwareRequestMsg firmwareRequestMsg = 10; GetFirmwareRequestMsg firmwareRequestMsg = 10;
GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11;
GetDeviceRequestMsg deviceRequestMsg = 12;
GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13;
} }
/* Response from ThingsBoard Core Service to Transport Service */ /* Response from ThingsBoard Core Service to Transport Service */
@ -598,9 +632,12 @@ message TransportApiResponseMsg {
GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2; GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2;
GetEntityProfileResponseMsg entityProfileResponseMsg = 3; GetEntityProfileResponseMsg entityProfileResponseMsg = 3;
ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4; ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4;
GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5;
LwM2MResponseMsg lwM2MResponseMsg = 6; LwM2MResponseMsg lwM2MResponseMsg = 6;
GetResourceResponseMsg resourceResponseMsg = 7; GetResourceResponseMsg resourceResponseMsg = 7;
GetFirmwareResponseMsg firmwareResponseMsg = 8; GetFirmwareResponseMsg firmwareResponseMsg = 8;
GetDeviceResponseMsg deviceResponseMsg = 9;
GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10;
} }
/* Messages that are handled by ThingsBoard Core Service */ /* Messages that are handled by ThingsBoard Core Service */

48
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java

@ -28,12 +28,12 @@ import org.eclipse.californium.core.observe.ObserveRelation;
import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource; import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.core.server.resources.ResourceObserver; import org.eclipse.californium.core.server.resources.ResourceObserver;
import org.springframework.util.StringUtils;
import org.thingsboard.server.coapserver.CoapServerService; import org.thingsboard.server.coapserver.CoapServerService;
import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo; import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TransportPayloadType; import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration;
@ -120,6 +120,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
processExchangeGetRequest(exchange, featureType.get()); processExchangeGetRequest(exchange, featureType.get());
} else if (featureType.get() == FeatureType.ATTRIBUTES) { } else if (featureType.get() == FeatureType.ATTRIBUTES) {
processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);
} else if (featureType.get() == FeatureType.FIRMWARE) {
processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST);
} else { } else {
log.trace("Invalid feature type parameter"); log.trace("Invalid feature type parameter");
exchange.respond(CoAP.ResponseCode.BAD_REQUEST); exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
@ -201,7 +203,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
Request request = advanced.getRequest(); Request request = advanced.getRequest();
String dtlsSessionIdStr = request.getSourceContext().get(DTLS_SESSION_ID_KEY); String dtlsSessionIdStr = request.getSourceContext().get(DTLS_SESSION_ID_KEY);
if (!StringUtils.isEmpty(dtlsSessionIdStr)) { if (StringUtils.isNotEmpty(dtlsSessionIdStr)) {
if (dtlsSessionIdMap != null) { if (dtlsSessionIdMap != null) {
TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = dtlsSessionIdMap TbCoapDtlsSessionInfo tbCoapDtlsSessionInfo = dtlsSessionIdMap
.computeIfPresent(dtlsSessionIdStr, (dtlsSessionId, dtlsSessionInfo) -> { .computeIfPresent(dtlsSessionIdStr, (dtlsSessionId, dtlsSessionInfo) -> {
@ -323,6 +325,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
coapTransportAdaptor.convertToGetAttributes(sessionId, request), coapTransportAdaptor.convertToGetAttributes(sessionId, request),
new CoapNoOpCallback(exchange)); new CoapNoOpCallback(exchange));
break; break;
case GET_FIRMWARE_REQUEST:
TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build();
transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange));
break;
} }
} catch (AdaptorException e) { } catch (AdaptorException e) {
log.trace("[{}] Failed to decode message: ", sessionId, e); log.trace("[{}] Failed to decode message: ", sessionId, e);
@ -424,6 +434,40 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
} }
} }
private class FirmwareCallback implements TransportServiceCallback<TransportProtos.GetFirmwareResponseMsg> {
private final CoapExchange exchange;
FirmwareCallback(CoapExchange exchange) {
this.exchange = exchange;
}
@Override
public void onSuccess(TransportProtos.GetFirmwareResponseMsg msg) {
String title = exchange.getQueryParameter("title");
String version = exchange.getQueryParameter("version");
if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) {
if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) {
String firmwareId = new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()).toString();
String strChunkSize = exchange.getQueryParameter("size");
String strChunk = exchange.getQueryParameter("chunk");
int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize);
int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk);
exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getFirmwareDataCache().get(firmwareId, chunkSize, chunk));
} else {
exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
}
} else {
exchange.respond(CoAP.ResponseCode.NOT_FOUND);
}
}
@Override
public void onError(Throwable e) {
log.warn("Failed to process request", e);
exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
}
}
private static class CoapSessionListener implements SessionMsgListener { private static class CoapSessionListener implements SessionMsgListener {
private final CoapExchange exchange; private final CoapExchange exchange;

12
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java

@ -18,11 +18,12 @@ package org.thingsboard.server.transport.coap;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapResource; import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.core.CoapServer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.coapserver.CoapServerService; import org.thingsboard.server.coapserver.CoapServerService;
import org.thingsboard.server.coapserver.TbCoapServerComponent;
import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource; import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -30,9 +31,9 @@ import javax.annotation.PreDestroy;
import java.net.UnknownHostException; import java.net.UnknownHostException;
@Service("CoapTransportService") @Service("CoapTransportService")
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.coap.enabled}'=='true')") @TbCoapServerComponent
@Slf4j @Slf4j
public class CoapTransportService { public class CoapTransportService implements TbTransportService {
private static final String V1 = "v1"; private static final String V1 = "v1";
private static final String API = "api"; private static final String API = "api";
@ -66,4 +67,9 @@ public class CoapTransportService {
public void shutdown() { public void shutdown() {
log.info("CoAP transport stopped!"); log.info("CoAP transport stopped!");
} }
@Override
public String getName() {
return "COAP";
}
} }

10
common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java

@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.transport.SessionMsgListener; import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.TransportContext; import org.thingsboard.server.common.transport.TransportContext;
@ -70,7 +71,7 @@ import java.util.function.Consumer;
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.http.enabled}'=='true')") @ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.http.enabled}'=='true')")
@RequestMapping("/api/v1") @RequestMapping("/api/v1")
@Slf4j @Slf4j
public class DeviceApiController { public class DeviceApiController implements TbTransportService {
@Autowired @Autowired
private HttpTransportContext transportContext; private HttpTransportContext transportContext;
@ -299,7 +300,7 @@ public class DeviceApiController {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND)); responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND));
} else if (title.equals(firmwareResponseMsg.getTitle()) && version.equals(firmwareResponseMsg.getVersion())) { } else if (title.equals(firmwareResponseMsg.getTitle()) && version.equals(firmwareResponseMsg.getVersion())) {
String firmwareId = new UUID(firmwareResponseMsg.getFirmwareIdMSB(), firmwareResponseMsg.getFirmwareIdLSB()).toString(); String firmwareId = new UUID(firmwareResponseMsg.getFirmwareIdMSB(), firmwareResponseMsg.getFirmwareIdLSB()).toString();
ByteArrayResource resource = new ByteArrayResource(transportContext.getFirmwareCacheReader().get(firmwareId, chuckSize, chuck)); ByteArrayResource resource = new ByteArrayResource(transportContext.getFirmwareDataCache().get(firmwareId, chuckSize, chuck));
ResponseEntity<ByteArrayResource> response = ResponseEntity.ok() ResponseEntity<ByteArrayResource> response = ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmwareResponseMsg.getFileName()) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmwareResponseMsg.getFileName())
.header("x-filename", firmwareResponseMsg.getFileName()) .header("x-filename", firmwareResponseMsg.getFileName())
@ -407,4 +408,9 @@ public class DeviceApiController {
} }
} }
@Override
public String getName() {
return "HTTP";
}
} }

3
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java

@ -20,13 +20,14 @@ import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
public interface LwM2mTransportService { public interface LwM2mTransportService extends TbTransportService {
void onRegistered(Registration registration, Collection<Observation> previousObsersations); void onRegistered(Registration registration, Collection<Observation> previousObsersations);

7
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java

@ -40,6 +40,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.common.transport.service.DefaultTransportService;
@ -1353,4 +1354,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() : objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() :
objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId))); objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)));
} }
@Override
public String getName() {
return "LWM2M";
}
} }

2
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java

@ -452,7 +452,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId); log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId);
ack(ctx, msgId); ack(ctx, msgId);
try { try {
byte[] firmwareChunk = context.getFirmwareCacheReader().get(firmwareId, chunkSize, chunk); byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk);
deviceSessionCtx.getPayloadAdaptor() deviceSessionCtx.getPayloadAdaptor()
.convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk) .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk)
.ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);

8
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java

@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.TbTransportService;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@ -38,7 +39,7 @@ import javax.annotation.PreDestroy;
@Service("MqttTransportService") @Service("MqttTransportService")
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')") @ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')")
@Slf4j @Slf4j
public class MqttTransportService { public class MqttTransportService implements TbTransportService {
@Value("${transport.mqtt.bind_address}") @Value("${transport.mqtt.bind_address}")
private String host; private String host;
@ -90,4 +91,9 @@ public class MqttTransportService {
} }
log.info("MQTT transport stopped!"); log.info("MQTT transport stopped!");
} }
@Override
public String getName() {
return "MQTT";
}
} }

1
common/transport/pom.xml

@ -40,6 +40,7 @@
<module>http</module> <module>http</module>
<module>coap</module> <module>coap</module>
<module>lwm2m</module> <module>lwm2m</module>
<module>snmp</module>
</modules> </modules>
</project> </project>

68
common/transport/snmp/pom.xml

@ -0,0 +1,68 @@
<!--
Copyright © 2016-2021 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>3.3.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>snmp</artifactId>
<packaging>jar</packaging>
<name>Thingsboard SNMP Transport Common</name>
<url>https://thingsboard.io</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../../..</main.dir>
</properties>
<dependencies>
<dependency>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>transport-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
</dependency>
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j-agent</artifactId>
<version>3.3.6</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

270
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java

@ -0,0 +1,270 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.transport.DeviceUpdatedEvent;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.TransportDeviceProfileCache;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.queue.util.AfterStartUp;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.service.ProtoTransportEntityService;
import org.thingsboard.server.transport.snmp.service.SnmpAuthService;
import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.stream.Collectors;
@TbSnmpTransportComponent
@Component
@Slf4j
@RequiredArgsConstructor
public class SnmpTransportContext extends TransportContext {
@Getter
private final SnmpTransportService snmpTransportService;
private final TransportDeviceProfileCache deviceProfileCache;
private final TransportService transportService;
private final ProtoTransportEntityService protoEntityService;
private final SnmpTransportBalancingService balancingService;
@Getter
private final SnmpAuthService snmpAuthService;
private final Map<DeviceId, DeviceSessionContext> sessions = new ConcurrentHashMap<>();
private Collection<DeviceId> allSnmpDevicesIds = new ConcurrentLinkedDeque<>();
@AfterStartUp(order = 2)
public void initDevicesSessions() {
log.info("Initializing SNMP devices sessions");
allSnmpDevicesIds = protoEntityService.getAllSnmpDevicesIds().stream()
.map(DeviceId::new)
.collect(Collectors.toList());
log.trace("Found all SNMP devices ids: {}", allSnmpDevicesIds);
List<DeviceId> managedDevicesIds = allSnmpDevicesIds.stream()
.filter(deviceId -> balancingService.isManagedByCurrentTransport(deviceId.getId()))
.collect(Collectors.toList());
log.info("SNMP devices managed by current SNMP transport: {}", managedDevicesIds);
managedDevicesIds.stream()
.map(protoEntityService::getDeviceById)
.collect(Collectors.toList())
.forEach(this::establishDeviceSession);
}
private void establishDeviceSession(Device device) {
if (device == null) return;
log.info("Establishing SNMP session for device {}", device.getId());
DeviceProfileId deviceProfileId = device.getDeviceProfileId();
DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId);
DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType());
return;
}
SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
DeviceSessionContext deviceSessionContext;
try {
deviceSessionContext = new DeviceSessionContext(
device, deviceProfile, credentials.getCredentialsId(),
profileTransportConfiguration, deviceTransportConfiguration, this
);
registerSessionMsgListener(deviceSessionContext);
} catch (Exception e) {
log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.toString());
return;
}
sessions.put(device.getId(), deviceSessionContext);
snmpTransportService.createQueryingTasks(deviceSessionContext);
log.info("Established SNMP device session for device {}", device.getId());
}
private void updateDeviceSession(DeviceSessionContext sessionContext, Device device, DeviceProfile deviceProfile) {
log.info("Updating SNMP session for device {}", device.getId());
DeviceCredentials credentials = protoEntityService.getDeviceCredentialsByDeviceId(device.getId());
if (credentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType());
destroyDeviceSession(sessionContext);
return;
}
SnmpDeviceProfileTransportConfiguration newProfileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
SnmpDeviceTransportConfiguration newDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
try {
if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) {
sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration);
sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
snmpTransportService.cancelQueryingTasks(sessionContext);
snmpTransportService.createQueryingTasks(sessionContext);
} else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) {
sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration);
sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration);
} else {
log.trace("Configuration of the device {} was not updated", device);
}
} catch (Exception e) {
log.error("Failed to update session for SNMP device {}: {}", sessionContext.getDeviceId(), e.getMessage());
destroyDeviceSession(sessionContext);
}
}
private void destroyDeviceSession(DeviceSessionContext sessionContext) {
if (sessionContext == null) return;
log.info("Destroying SNMP device session for device {}", sessionContext.getDevice().getId());
sessionContext.close();
snmpAuthService.cleanUpSnmpAuthInfo(sessionContext);
transportService.deregisterSession(sessionContext.getSessionInfo());
snmpTransportService.cancelQueryingTasks(sessionContext);
sessions.remove(sessionContext.getDeviceId());
log.trace("Unregistered and removed session");
}
private void registerSessionMsgListener(DeviceSessionContext deviceSessionContext) {
transportService.process(DeviceTransportType.SNMP,
TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceSessionContext.getToken()).build(),
new TransportServiceCallback<>() {
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
if (msg.hasDeviceInfo()) {
SessionInfoProto sessionInfo = SessionInfoCreator.create(
msg, SnmpTransportContext.this, UUID.randomUUID()
);
transportService.registerAsyncSession(sessionInfo, deviceSessionContext);
transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), TransportServiceCallback.EMPTY);
deviceSessionContext.setSessionInfo(sessionInfo);
deviceSessionContext.setDeviceInfo(msg.getDeviceInfo());
} else {
log.warn("[{}] Failed to process device auth", deviceSessionContext.getDeviceId());
}
}
@Override
public void onError(Throwable e) {
log.warn("[{}] Failed to process device auth: {}", deviceSessionContext.getDeviceId(), e);
}
});
}
@EventListener(DeviceUpdatedEvent.class)
public void onDeviceUpdatedOrCreated(DeviceUpdatedEvent deviceUpdatedEvent) {
Device device = deviceUpdatedEvent.getDevice();
log.trace("Got creating or updating device event for device {}", device);
DeviceTransportType transportType = Optional.ofNullable(device.getDeviceData().getTransportConfiguration())
.map(DeviceTransportConfiguration::getType)
.orElse(null);
if (!allSnmpDevicesIds.contains(device.getId())) {
if (transportType != DeviceTransportType.SNMP) {
return;
}
allSnmpDevicesIds.add(device.getId());
if (balancingService.isManagedByCurrentTransport(device.getId().getId())) {
establishDeviceSession(device);
}
} else {
if (balancingService.isManagedByCurrentTransport(device.getId().getId())) {
DeviceSessionContext sessionContext = sessions.get(device.getId());
if (transportType == DeviceTransportType.SNMP) {
if (sessionContext != null) {
updateDeviceSession(sessionContext, device, deviceProfileCache.get(device.getDeviceProfileId()));
} else {
establishDeviceSession(device);
}
} else {
log.trace("Transport type was changed to {}", transportType);
destroyDeviceSession(sessionContext);
}
}
}
}
public void onDeviceDeleted(DeviceSessionContext sessionContext) {
destroyDeviceSession(sessionContext);
}
public void onDeviceProfileUpdated(DeviceProfile deviceProfile, DeviceSessionContext sessionContext) {
updateDeviceSession(sessionContext, sessionContext.getDevice(), deviceProfile);
}
public void onSnmpTransportListChanged() {
log.trace("SNMP transport list changed. Updating sessions");
List<DeviceId> deleted = new LinkedList<>();
for (DeviceId deviceId : allSnmpDevicesIds) {
if (balancingService.isManagedByCurrentTransport(deviceId.getId())) {
if (!sessions.containsKey(deviceId)) {
Device device = protoEntityService.getDeviceById(deviceId);
if (device != null) {
log.info("SNMP device {} is now managed by current transport node", deviceId);
establishDeviceSession(device);
} else {
deleted.add(deviceId);
}
}
} else {
Optional.ofNullable(sessions.get(deviceId))
.ifPresent(sessionContext -> {
log.info("SNMP session for device {} is not managed by current transport node anymore", deviceId);
destroyDeviceSession(sessionContext);
});
}
}
log.trace("Removing deleted SNMP devices: {}", deleted);
allSnmpDevicesIds.removeAll(deleted);
}
public Collection<DeviceSessionContext> getSessions() {
return sessions.values();
}
}

35
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/ServiceListChangedEventListener.java

@ -0,0 +1,35 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.event;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.service.SnmpTransportBalancingService;
@TbSnmpTransportComponent
@Component
@RequiredArgsConstructor
public class ServiceListChangedEventListener extends TbApplicationEventListener<ServiceListChangedEvent> {
private final SnmpTransportBalancingService snmpTransportBalancingService;
@Override
protected void onTbApplicationEvent(ServiceListChangedEvent event) {
snmpTransportBalancingService.onServiceListChanged(event);
}
}

24
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEvent.java

@ -0,0 +1,24 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.event;
import org.thingsboard.server.queue.discovery.event.TbApplicationEvent;
public class SnmpTransportListChangedEvent extends TbApplicationEvent {
public SnmpTransportListChangedEvent() {
super(new Object());
}
}

34
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/event/SnmpTransportListChangedEventListener.java

@ -0,0 +1,34 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.event;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.SnmpTransportContext;
@TbSnmpTransportComponent
@Component
@RequiredArgsConstructor
public class SnmpTransportListChangedEventListener extends TbApplicationEventListener<SnmpTransportListChangedEvent> {
private final SnmpTransportContext snmpTransportContext;
@Override
protected void onTbApplicationEvent(SnmpTransportListChangedEvent event) {
snmpTransportContext.onSnmpTransportListChanged();
}
}

171
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduService.java

@ -0,0 +1,171 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.service;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.ScopedPDU;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@TbSnmpTransportComponent
@Service
@Slf4j
public class PduService {
public PDU createPdu(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
PDU pdu = setUpPdu(sessionContext);
pdu.setType(communicationConfig.getMethod().getCode());
pdu.addAll(communicationConfig.getAllMappings().stream()
.filter(mapping -> values.isEmpty() || values.containsKey(mapping.getKey()))
.map(mapping -> Optional.ofNullable(values.get(mapping.getKey()))
.map(value -> {
Variable variable = toSnmpVariable(value, mapping.getDataType());
return new VariableBinding(new OID(mapping.getOid()), variable);
})
.orElseGet(() -> new VariableBinding(new OID(mapping.getOid()))))
.collect(Collectors.toList()));
return pdu;
}
public PDU createSingleVariablePdu(DeviceSessionContext sessionContext, SnmpMethod snmpMethod, String oid, String value, DataType dataType) {
PDU pdu = setUpPdu(sessionContext);
pdu.setType(snmpMethod.getCode());
Variable variable = value == null ? Null.instance : toSnmpVariable(value, dataType);
pdu.add(new VariableBinding(new OID(oid), variable));
return pdu;
}
private Variable toSnmpVariable(String value, DataType dataType) {
dataType = dataType == null ? DataType.STRING : dataType;
Variable variable;
switch (dataType) {
case LONG:
try {
variable = new Integer32(Integer.parseInt(value));
break;
} catch (NumberFormatException ignored) {
}
case DOUBLE:
case BOOLEAN:
case STRING:
case JSON:
default:
variable = new OctetString(value);
}
return variable;
}
private PDU setUpPdu(DeviceSessionContext sessionContext) {
PDU pdu;
SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
SnmpProtocolVersion snmpVersion = deviceTransportConfiguration.getProtocolVersion();
switch (snmpVersion) {
case V1:
case V2C:
pdu = new PDU();
break;
case V3:
ScopedPDU scopedPdu = new ScopedPDU();
scopedPdu.setContextName(new OctetString(deviceTransportConfiguration.getContextName()));
scopedPdu.setContextEngineID(new OctetString(deviceTransportConfiguration.getEngineId()));
pdu = scopedPdu;
break;
default:
throw new UnsupportedOperationException("SNMP version " + snmpVersion + " is not supported");
}
return pdu;
}
public JsonObject processPdu(PDU pdu, List<SnmpMapping> responseMappings) {
Map<OID, String> values = processPdu(pdu);
Map<OID, SnmpMapping> mappings = new HashMap<>();
if (responseMappings != null) {
for (SnmpMapping mapping : responseMappings) {
OID oid = new OID(mapping.getOid());
mappings.put(oid, mapping);
}
}
JsonObject data = new JsonObject();
values.forEach((oid, value) -> {
log.trace("Processing variable binding: {} - {}", oid, value);
SnmpMapping mapping = mappings.get(oid);
if (mapping == null) {
log.debug("No SNMP mapping for oid {}", oid);
return;
}
processValue(mapping.getKey(), mapping.getDataType(), value, data);
});
return data;
}
public Map<OID, String> processPdu(PDU pdu) {
return IntStream.range(0, pdu.size())
.mapToObj(pdu::get)
.filter(Objects::nonNull)
.filter(variableBinding -> !(variableBinding.getVariable() instanceof Null))
.collect(Collectors.toMap(VariableBinding::getOid, VariableBinding::toValueString));
}
public void processValue(String key, DataType dataType, String value, JsonObject result) {
switch (dataType) {
case LONG:
result.addProperty(key, Long.parseLong(value));
break;
case BOOLEAN:
result.addProperty(key, Boolean.parseBoolean(value));
break;
case DOUBLE:
result.addProperty(key, Double.parseDouble(value));
break;
case STRING:
case JSON:
default:
result.addProperty(key, value);
}
}
}

110
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/ProtoTransportEntityService.java

@ -0,0 +1,110 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.service;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.data.DeviceData;
import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@TbSnmpTransportComponent
@Service
@RequiredArgsConstructor
public class ProtoTransportEntityService {
private final TransportService transportService;
private final DataDecodingEncodingService dataDecodingEncodingService;
public Device getDeviceById(DeviceId id) {
TransportProtos.GetDeviceResponseMsg deviceProto = transportService.getDevice(TransportProtos.GetDeviceRequestMsg.newBuilder()
.setDeviceIdMSB(id.getId().getMostSignificantBits())
.setDeviceIdLSB(id.getId().getLeastSignificantBits())
.build());
if (deviceProto == null) {
return null;
}
DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(
deviceProto.getDeviceProfileIdMSB(), deviceProto.getDeviceProfileIdLSB())
);
Device device = new Device();
device.setId(id);
device.setDeviceProfileId(deviceProfileId);
DeviceTransportConfiguration deviceTransportConfiguration = (DeviceTransportConfiguration) dataDecodingEncodingService.decode(
deviceProto.getDeviceTransportConfiguration().toByteArray()
).orElseThrow(() -> new IllegalStateException("Can't find device transport configuration"));
DeviceData deviceData = new DeviceData();
deviceData.setTransportConfiguration(deviceTransportConfiguration);
device.setDeviceData(deviceData);
return device;
}
public DeviceCredentials getDeviceCredentialsByDeviceId(DeviceId deviceId) {
TransportProtos.GetDeviceCredentialsResponseMsg deviceCredentialsResponse = transportService.getDeviceCredentials(
TransportProtos.GetDeviceCredentialsRequestMsg.newBuilder()
.setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
.build()
);
return (DeviceCredentials) dataDecodingEncodingService.decode(deviceCredentialsResponse.getDeviceCredentialsData().toByteArray())
.orElseThrow(() -> new IllegalArgumentException("Device credentials not found"));
}
public List<UUID> getAllSnmpDevicesIds() {
List<UUID> result = new ArrayList<>();
int page = 0;
int pageSize = 512;
boolean hasNextPage = true;
while (hasNextPage) {
TransportProtos.GetSnmpDevicesResponseMsg responseMsg = requestSnmpDevicesIds(page, pageSize);
result.addAll(responseMsg.getIdsList().stream()
.map(UUID::fromString)
.collect(Collectors.toList()));
hasNextPage = responseMsg.getHasNextPage();
page++;
}
return result;
}
private TransportProtos.GetSnmpDevicesResponseMsg requestSnmpDevicesIds(int page, int pageSize) {
TransportProtos.GetSnmpDevicesRequestMsg requestMsg = TransportProtos.GetSnmpDevicesRequestMsg.newBuilder()
.setPage(page)
.setPageSize(pageSize)
.build();
return transportService.getSnmpDevicesIds(requestMsg);
}
}

121
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpAuthService.java

@ -0,0 +1,121 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.service;
import lombok.RequiredArgsConstructor;
import org.snmp4j.AbstractTarget;
import org.snmp4j.CommunityTarget;
import org.snmp4j.Target;
import org.snmp4j.UserTarget;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModel;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.service.SnmpTransportService;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
import java.util.Optional;
@Service
@TbSnmpTransportComponent
@RequiredArgsConstructor
public class SnmpAuthService {
private final SnmpTransportService snmpTransportService;
@Value("${transport.snmp.underlying_protocol}")
private String snmpUnderlyingProtocol;
public Target setUpSnmpTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
AbstractTarget target;
SnmpProtocolVersion protocolVersion = deviceTransportConfig.getProtocolVersion();
switch (protocolVersion) {
case V1:
CommunityTarget communityTargetV1 = new CommunityTarget();
communityTargetV1.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv1);
communityTargetV1.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
communityTargetV1.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
target = communityTargetV1;
break;
case V2C:
CommunityTarget communityTargetV2 = new CommunityTarget();
communityTargetV2.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
communityTargetV2.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
communityTargetV2.setCommunity(new OctetString(deviceTransportConfig.getCommunity()));
target = communityTargetV2;
break;
case V3:
OctetString username = new OctetString(deviceTransportConfig.getUsername());
OctetString securityName = new OctetString(deviceTransportConfig.getSecurityName());
OctetString engineId = new OctetString(deviceTransportConfig.getEngineId());
OID authenticationProtocol = new OID(deviceTransportConfig.getAuthenticationProtocol().getOid());
OID privacyProtocol = new OID(deviceTransportConfig.getPrivacyProtocol().getOid());
OctetString authenticationPassphrase = new OctetString(deviceTransportConfig.getAuthenticationPassphrase());
authenticationPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(authenticationProtocol, authenticationPassphrase, engineId.getValue()));
OctetString privacyPassphrase = new OctetString(deviceTransportConfig.getPrivacyPassphrase());
privacyPassphrase = new OctetString(SecurityProtocols.getInstance().passwordToKey(privacyProtocol, authenticationProtocol, privacyPassphrase, engineId.getValue()));
USM usm = snmpTransportService.getSnmp().getUSM();
if (usm.hasUser(engineId, securityName)) {
usm.removeAllUsers(username, engineId);
}
usm.addLocalizedUser(
engineId.getValue(), username,
authenticationProtocol, authenticationPassphrase.getValue(),
privacyProtocol, privacyPassphrase.getValue()
);
UserTarget userTarget = new UserTarget();
userTarget.setSecurityName(securityName);
userTarget.setAuthoritativeEngineID(engineId.getValue());
userTarget.setSecurityModel(SecurityModel.SECURITY_MODEL_USM);
userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
target = userTarget;
break;
default:
throw new UnsupportedOperationException("SNMP protocol version " + protocolVersion + " is not supported");
}
Address address = GenericAddress.parse(snmpUnderlyingProtocol + ":" + deviceTransportConfig.getHost() + "/" + deviceTransportConfig.getPort());
target.setAddress(Optional.ofNullable(address).orElseThrow(() -> new IllegalArgumentException("Address of the SNMP device is invalid")));
target.setTimeout(profileTransportConfig.getTimeoutMs());
target.setRetries(profileTransportConfig.getRetries());
target.setVersion(protocolVersion.getCode());
return target;
}
public void cleanUpSnmpAuthInfo(DeviceSessionContext sessionContext) {
SnmpDeviceTransportConfiguration deviceTransportConfiguration = sessionContext.getDeviceTransportConfiguration();
if (deviceTransportConfiguration.getProtocolVersion() == SnmpProtocolVersion.V3) {
OctetString username = new OctetString(deviceTransportConfiguration.getUsername());
OctetString engineId = new OctetString(deviceTransportConfiguration.getEngineId());
snmpTransportService.getSnmp().getUSM().removeAllUsers(username, engineId);
}
}
}

92
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportBalancingService.java

@ -0,0 +1,92 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.event.SnmpTransportListChangedEvent;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@TbSnmpTransportComponent
@Service
@RequiredArgsConstructor
@Slf4j
public class SnmpTransportBalancingService {
private final PartitionService partitionService;
private final ApplicationEventPublisher eventPublisher;
private final SnmpTransportService snmpTransportService;
private int snmpTransportsCount = 1;
private Integer currentTransportPartitionIndex = 0;
public void onServiceListChanged(ServiceListChangedEvent event) {
log.trace("Got service list changed event: {}", event);
recalculatePartitions(event.getOtherServices(), event.getCurrentService());
}
public boolean isManagedByCurrentTransport(UUID entityId) {
boolean isManaged = resolvePartitionIndexForEntity(entityId) == currentTransportPartitionIndex;
if (!isManaged) {
log.trace("Entity {} is not managed by current SNMP transport node", entityId);
}
return isManaged;
}
private int resolvePartitionIndexForEntity(UUID entityId) {
return partitionService.resolvePartitionIndex(entityId, snmpTransportsCount);
}
private void recalculatePartitions(List<ServiceInfo> otherServices, ServiceInfo currentService) {
log.info("Recalculating partitions for SNMP transports");
List<ServiceInfo> snmpTransports = Stream.concat(otherServices.stream(), Stream.of(currentService))
.filter(service -> service.getTransportsList().contains(snmpTransportService.getName()))
.sorted(Comparator.comparing(ServiceInfo::getServiceId))
.collect(Collectors.toList());
log.trace("Found SNMP transports: {}", snmpTransports);
int previousCurrentTransportPartitionIndex = currentTransportPartitionIndex;
int previousSnmpTransportsCount = snmpTransportsCount;
if (!snmpTransports.isEmpty()) {
for (int i = 0; i < snmpTransports.size(); i++) {
if (snmpTransports.get(i).equals(currentService)) {
currentTransportPartitionIndex = i;
break;
}
}
snmpTransportsCount = snmpTransports.size();
}
if (snmpTransportsCount != previousSnmpTransportsCount || currentTransportPartitionIndex != previousCurrentTransportPartitionIndex) {
log.info("SNMP transports partitions have changed: transports count = {}, current transport partition index = {}", snmpTransportsCount, currentTransportPartitionIndex);
eventPublisher.publishEvent(new SnmpTransportListChangedEvent());
} else {
log.info("SNMP transports partitions have not changed");
}
}
}

351
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java

@ -0,0 +1,351 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.service;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.MPv3;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.smi.OctetString;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod;
import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@TbSnmpTransportComponent
@Service
@Slf4j
@RequiredArgsConstructor
public class SnmpTransportService implements TbTransportService {
private final TransportService transportService;
private final PduService pduService;
@Getter
private Snmp snmp;
private ScheduledExecutorService queryingExecutor;
private ExecutorService responseProcessingExecutor;
private final Map<SnmpCommunicationSpec, ResponseDataMapper> responseDataMappers = new EnumMap<>(SnmpCommunicationSpec.class);
private final Map<SnmpCommunicationSpec, ResponseProcessor> responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class);
@Value("${transport.snmp.response_processing.parallelism_level}")
private Integer responseProcessingParallelismLevel;
@Value("${transport.snmp.underlying_protocol}")
private String snmpUnderlyingProtocol;
@PostConstruct
private void init() throws IOException {
queryingExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("snmp-querying"));
responseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel);
initializeSnmp();
configureResponseDataMappers();
configureResponseProcessors();
log.info("SNMP transport service initialized");
}
private void initializeSnmp() throws IOException {
TransportMapping<?> transportMapping;
switch (snmpUnderlyingProtocol) {
case "udp":
transportMapping = new DefaultUdpTransportMapping();
break;
case "tcp":
transportMapping = new DefaultTcpTransportMapping();
break;
default:
throw new IllegalArgumentException("Underlying protocol " + snmpUnderlyingProtocol + " for SNMP is not supported");
}
snmp = new Snmp(transportMapping);
snmp.listen();
USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
SecurityModels.getInstance().addSecurityModel(usm);
}
public void createQueryingTasks(DeviceSessionContext sessionContext) {
List<ScheduledFuture<?>> queryingTasks = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
.filter(communicationConfig -> communicationConfig instanceof RepeatingQueryingSnmpCommunicationConfig)
.map(config -> {
RepeatingQueryingSnmpCommunicationConfig repeatingCommunicationConfig = (RepeatingQueryingSnmpCommunicationConfig) config;
Long queryingFrequency = repeatingCommunicationConfig.getQueryingFrequencyMs();
return queryingExecutor.scheduleWithFixedDelay(() -> {
try {
if (sessionContext.isActive()) {
sendRequest(sessionContext, repeatingCommunicationConfig);
}
} catch (Exception e) {
log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.toString());
}
}, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS);
})
.collect(Collectors.toList());
sessionContext.getQueryingTasks().addAll(queryingTasks);
}
public void cancelQueryingTasks(DeviceSessionContext sessionContext) {
sessionContext.getQueryingTasks().forEach(task -> task.cancel(true));
sessionContext.getQueryingTasks().clear();
}
private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) {
sendRequest(sessionContext, communicationConfig, Collections.emptyMap());
}
private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
PDU request = pduService.createPdu(sessionContext, communicationConfig, values);
RequestInfo requestInfo = new RequestInfo(communicationConfig.getSpec(), communicationConfig.getAllMappings());
sendRequest(sessionContext, request, requestInfo);
}
private void sendRequest(DeviceSessionContext sessionContext, PDU request, RequestInfo requestInfo) {
if (request.size() > 0) {
log.trace("Executing SNMP request for device {}. Variables bindings: {}", sessionContext.getDeviceId(), request.getVariableBindings());
try {
snmp.send(request, sessionContext.getTarget(), requestInfo, sessionContext);
} catch (IOException e) {
log.error("Failed to send SNMP request to device {}: {}", sessionContext.getDeviceId(), e.toString());
}
}
}
public void onAttributeUpdate(DeviceSessionContext sessionContext, TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification) {
sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
.filter(config -> config.getSpec() == SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING)
.findFirst()
.ifPresent(communicationConfig -> {
Map<String, String> sharedAttributes = JsonConverter.toJson(attributeUpdateNotification).entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().isJsonPrimitive() ? entry.getValue().getAsString() : entry.getValue().toString()
));
sendRequest(sessionContext, communicationConfig, sharedAttributes);
});
}
public void onToDeviceRpcRequest(DeviceSessionContext sessionContext, TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg) {
SnmpMethod snmpMethod = SnmpMethod.valueOf(toDeviceRpcRequestMsg.getMethodName());
JsonObject params = JsonConverter.parse(toDeviceRpcRequestMsg.getParams()).getAsJsonObject();
String key = Optional.ofNullable(params.get("key")).map(JsonElement::getAsString).orElse(null);
String value = Optional.ofNullable(params.get("value")).map(JsonElement::getAsString).orElse(null);
if (value == null && snmpMethod == SnmpMethod.SET) {
throw new IllegalArgumentException("Value must be specified for SNMP method 'SET'");
}
SnmpCommunicationConfig communicationConfig = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream()
.filter(config -> config.getSpec() == SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No communication config found with RPC spec"));
SnmpMapping snmpMapping = communicationConfig.getAllMappings().stream()
.filter(mapping -> mapping.getKey().equals(key))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No SNMP mapping found in the config for specified key"));
String oid = snmpMapping.getOid();
DataType dataType = snmpMapping.getDataType();
PDU request = pduService.createSingleVariablePdu(sessionContext, snmpMethod, oid, value, dataType);
RequestInfo requestInfo = new RequestInfo(toDeviceRpcRequestMsg.getRequestId(), communicationConfig.getSpec(), communicationConfig.getAllMappings());
sendRequest(sessionContext, request, requestInfo);
}
public void processResponseEvent(DeviceSessionContext sessionContext, ResponseEvent event) {
((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext);
if (event.getError() != null) {
log.warn("SNMP response error: {}", event.getError().toString());
return;
}
PDU response = event.getResponse();
if (response == null) {
log.debug("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID());
return;
}
RequestInfo requestInfo = (RequestInfo) event.getUserObject();
responseProcessingExecutor.execute(() -> {
processResponse(sessionContext, response, requestInfo);
});
}
private void processResponse(DeviceSessionContext sessionContext, PDU response, RequestInfo requestInfo) {
ResponseProcessor responseProcessor = responseProcessors.get(requestInfo.getCommunicationSpec());
if (responseProcessor == null) return;
JsonObject responseData = responseDataMappers.get(requestInfo.getCommunicationSpec()).map(response, requestInfo);
if (responseData.entrySet().isEmpty()) {
log.debug("No values is the SNMP response for device {}. Request id: {}", sessionContext.getDeviceId(), response.getRequestID());
return;
}
responseProcessor.process(responseData, requestInfo, sessionContext);
reportActivity(sessionContext.getSessionInfo());
}
private void configureResponseDataMappers() {
responseDataMappers.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (pdu, requestInfo) -> {
JsonObject responseData = new JsonObject();
pduService.processPdu(pdu).forEach((oid, value) -> {
requestInfo.getResponseMappings().stream()
.filter(snmpMapping -> snmpMapping.getOid().equals(oid.toDottedString()))
.findFirst()
.ifPresent(snmpMapping -> {
pduService.processValue(snmpMapping.getKey(), snmpMapping.getDataType(), value, responseData);
});
});
return responseData;
});
ResponseDataMapper defaultResponseDataMapper = (pdu, requestInfo) -> {
return pduService.processPdu(pdu, requestInfo.getResponseMappings());
};
Arrays.stream(SnmpCommunicationSpec.values())
.forEach(communicationSpec -> {
responseDataMappers.putIfAbsent(communicationSpec, defaultResponseDataMapper);
});
}
private void configureResponseProcessors() {
responseProcessors.put(SnmpCommunicationSpec.TELEMETRY_QUERYING, (responseData, requestInfo, sessionContext) -> {
TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(responseData);
transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, null);
log.debug("Posted telemetry for SNMP device {}: {}", sessionContext.getDeviceId(), responseData);
});
responseProcessors.put(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, (responseData, requestInfo, sessionContext) -> {
TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(responseData);
transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, null);
log.debug("Posted attributes for SNMP device {}: {}", sessionContext.getDeviceId(), responseData);
});
responseProcessors.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (responseData, requestInfo, sessionContext) -> {
TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder()
.setRequestId(requestInfo.getRequestId())
.setPayload(JsonConverter.toJson(responseData))
.build();
transportService.process(sessionContext.getSessionInfo(), rpcResponseMsg, null);
log.debug("Posted RPC response {} for device {}", responseData, sessionContext.getDeviceId());
});
}
private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
.setAttributeSubscription(true)
.setRpcSubscription(true)
.setLastActivityTime(System.currentTimeMillis())
.build(), TransportServiceCallback.EMPTY);
}
@Override
public String getName() {
return "SNMP";
}
@PreDestroy
public void shutdown() {
log.info("Stopping SNMP transport!");
if (queryingExecutor != null) {
queryingExecutor.shutdownNow();
}
if (responseProcessingExecutor != null) {
responseProcessingExecutor.shutdownNow();
}
if (snmp != null) {
try {
snmp.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
log.info("SNMP transport stopped!");
}
@Data
private static class RequestInfo {
private Integer requestId;
private SnmpCommunicationSpec communicationSpec;
private List<SnmpMapping> responseMappings;
public RequestInfo(Integer requestId, SnmpCommunicationSpec communicationSpec, List<SnmpMapping> responseMappings) {
this.requestId = requestId;
this.communicationSpec = communicationSpec;
this.responseMappings = responseMappings;
}
public RequestInfo(SnmpCommunicationSpec communicationSpec, List<SnmpMapping> responseMappings) {
this.communicationSpec = communicationSpec;
this.responseMappings = responseMappings;
}
}
private interface ResponseDataMapper {
JsonObject map(PDU pdu, RequestInfo requestInfo);
}
private interface ResponseProcessor {
void process(JsonObject responseData, RequestInfo requestInfo, DeviceSessionContext sessionContext);
}
}

146
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java

@ -0,0 +1,146 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.session;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.Target;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
import org.thingsboard.server.transport.snmp.SnmpTransportContext;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public class DeviceSessionContext extends DeviceAwareSessionContext implements SessionMsgListener, ResponseListener {
@Getter
private Target target;
private final String token;
@Getter
@Setter
private SnmpDeviceProfileTransportConfiguration profileTransportConfiguration;
@Getter
@Setter
private SnmpDeviceTransportConfiguration deviceTransportConfiguration;
@Getter
private final Device device;
private final SnmpTransportContext snmpTransportContext;
private final AtomicInteger msgIdSeq = new AtomicInteger(0);
@Getter
private boolean isActive = true;
@Getter
private final List<ScheduledFuture<?>> queryingTasks = new LinkedList<>();
public DeviceSessionContext(Device device, DeviceProfile deviceProfile, String token,
SnmpDeviceProfileTransportConfiguration profileTransportConfiguration,
SnmpDeviceTransportConfiguration deviceTransportConfiguration,
SnmpTransportContext snmpTransportContext) throws Exception {
super(UUID.randomUUID());
super.setDeviceId(device.getId());
super.setDeviceProfile(deviceProfile);
this.device = device;
this.token = token;
this.snmpTransportContext = snmpTransportContext;
this.profileTransportConfiguration = profileTransportConfiguration;
this.deviceTransportConfiguration = deviceTransportConfiguration;
initializeTarget(profileTransportConfiguration, deviceTransportConfiguration);
}
@Override
public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto newSessionInfo, DeviceProfile deviceProfile) {
super.onDeviceProfileUpdate(newSessionInfo, deviceProfile);
if (isActive) {
snmpTransportContext.onDeviceProfileUpdated(deviceProfile, this);
}
}
@Override
public void onDeviceDeleted(DeviceId deviceId) {
snmpTransportContext.onDeviceDeleted(this);
}
@Override
public void onResponse(ResponseEvent event) {
if (isActive) {
snmpTransportContext.getSnmpTransportService().processResponseEvent(this, event);
}
}
public void initializeTarget(SnmpDeviceProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) throws Exception {
log.trace("Initializing target for SNMP session of device {}", device);
this.target = snmpTransportContext.getSnmpAuthService().setUpSnmpTarget(profileTransportConfig, deviceTransportConfig);
log.debug("SNMP target initialized: {}", target);
}
public void close() {
isActive = false;
}
public String getToken() {
return token;
}
@Override
public int nextMsgId() {
return msgIdSeq.incrementAndGet();
}
@Override
public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) {
}
@Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
snmpTransportContext.getSnmpTransportService().onAttributeUpdate(this, attributeUpdateNotification);
}
@Override
public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) {
}
@Override
public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest);
}
@Override
public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) {
}
}

196
common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java

@ -0,0 +1,196 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp;
import org.snmp4j.CommandResponderEvent;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.TransportMapping;
import org.snmp4j.agent.BaseAgent;
import org.snmp4j.agent.CommandProcessor;
import org.snmp4j.agent.DuplicateRegistrationException;
import org.snmp4j.agent.MOGroup;
import org.snmp4j.agent.ManagedObject;
import org.snmp4j.agent.mo.MOAccessImpl;
import org.snmp4j.agent.mo.MOScalar;
import org.snmp4j.agent.mo.snmp.RowStatus;
import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB;
import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
import org.snmp4j.agent.mo.snmp.StorageType;
import org.snmp4j.agent.mo.snmp.VacmMIB;
import org.snmp4j.agent.security.MutableVACM;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModel;
import org.snmp4j.security.USM;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.TransportMappings;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Scanner;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class SnmpDeviceSimulatorV2 extends BaseAgent {
public static class RequestProcessor extends CommandProcessor {
private final Consumer<CommandResponderEvent> processor;
public RequestProcessor(Consumer<CommandResponderEvent> processor) {
super(new OctetString(MPv3.createLocalEngineID()));
this.processor = processor;
}
@Override
public void processPdu(CommandResponderEvent event) {
processor.accept(event);
}
}
private final Target target;
private final Address address;
private Snmp snmp;
private final String password;
public SnmpDeviceSimulatorV2(int port, String password) throws IOException {
super(new File("conf.agent"), new File("bootCounter.agent"), new RequestProcessor(event -> {
System.out.println("aboba");
((Snmp) event.getSource()).cancel(event.getPDU(), event1 -> System.out.println("canceled"));
}));
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(password));
this.address = GenericAddress.parse("udp:0.0.0.0/" + port);
target.setAddress(address);
target.setRetries(2);
target.setTimeout(1500);
target.setVersion(SnmpConstants.version2c);
this.target = target;
this.password = password;
}
public void start() throws IOException {
init();
addShutdownHook();
getServer().addContext(new OctetString("public"));
finishInit();
run();
sendColdStartNotification();
snmp = new Snmp(transportMappings[0]);
}
public void setUpMappings(Map<String, String> oidToResponseMappings) {
unregisterManagedObject(getSnmpv2MIB());
oidToResponseMappings.forEach((oid, response) -> {
registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response)));
});
}
public void sendTrap(String host, int port, Map<String, String> values) throws IOException {
PDU pdu = new PDU();
pdu.addAll(values.entrySet().stream()
.map(entry -> new VariableBinding(new OID(entry.getKey()), new OctetString(entry.getValue())))
.collect(Collectors.toList()));
pdu.setType(PDU.TRAP);
CommunityTarget remoteTarget = (CommunityTarget) getTarget().clone();
remoteTarget.setAddress(new UdpAddress(host + "/" + port));
snmp.send(pdu, remoteTarget);
}
@Override
protected void registerManagedObjects() {
}
protected void registerManagedObject(ManagedObject mo) {
try {
server.register(mo, null);
} catch (DuplicateRegistrationException ex) {
throw new RuntimeException(ex);
}
}
protected void unregisterManagedObject(MOGroup moGroup) {
moGroup.unregisterMOs(server, getContext(moGroup));
}
@Override
protected void addNotificationTargets(SnmpTargetMIB targetMIB,
SnmpNotificationMIB notificationMIB) {
}
@Override
protected void addViews(VacmMIB vacm) {
vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c, new OctetString(
"cpublic"), new OctetString("v1v2group"),
StorageType.nonVolatile);
vacm.addAccess(new OctetString("v1v2group"), new OctetString("public"),
SecurityModel.SECURITY_MODEL_ANY, SecurityLevel.NOAUTH_NOPRIV,
MutableVACM.VACM_MATCH_EXACT, new OctetString("fullReadView"),
new OctetString("fullWriteView"), new OctetString(
"fullNotifyView"), StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("fullReadView"), new OID("1.3"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
}
protected void addUsmUser(USM usm) {
}
protected void initTransportMappings() {
transportMappings = new TransportMapping[]{TransportMappings.getInstance().createTransportMapping(address)};
}
protected void unregisterManagedObjects() {
}
protected void addCommunities(SnmpCommunityMIB communityMIB) {
Variable[] com2sec = new Variable[]{
new OctetString("public"),
new OctetString("cpublic"),
getAgent().getContextEngineID(),
new OctetString("public"),
new OctetString(),
new Integer32(StorageType.nonVolatile),
new Integer32(RowStatus.active)
};
SnmpCommunityMIB.SnmpCommunityEntryRow row = communityMIB.getSnmpCommunityEntry().createRow(
new OctetString("public2public").toSubIndex(true), com2sec);
communityMIB.getSnmpCommunityEntry().addRow(row);
}
public Target getTarget() {
return target;
}
}

745
common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV3.java

@ -0,0 +1,745 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp;
import org.snmp4j.MessageDispatcherImpl;
import org.snmp4j.TransportMapping;
import org.snmp4j.agent.BaseAgent;
import org.snmp4j.agent.CommandProcessor;
import org.snmp4j.agent.DuplicateRegistrationException;
import org.snmp4j.agent.MOGroup;
import org.snmp4j.agent.ManagedObject;
import org.snmp4j.agent.mo.DefaultMOMutableRow2PC;
import org.snmp4j.agent.mo.DefaultMOTable;
import org.snmp4j.agent.mo.MOAccessImpl;
import org.snmp4j.agent.mo.MOColumn;
import org.snmp4j.agent.mo.MOMutableColumn;
import org.snmp4j.agent.mo.MOMutableTableModel;
import org.snmp4j.agent.mo.MOScalar;
import org.snmp4j.agent.mo.MOTableIndex;
import org.snmp4j.agent.mo.MOTableRow;
import org.snmp4j.agent.mo.MOTableSubIndex;
import org.snmp4j.agent.mo.ext.AgentppSimulationMib;
import org.snmp4j.agent.mo.snmp.RowStatus;
import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB;
import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
import org.snmp4j.agent.mo.snmp.StorageType;
import org.snmp4j.agent.mo.snmp.TransportDomains;
import org.snmp4j.agent.mo.snmp.VacmMIB;
import org.snmp4j.agent.mo.snmp4j.example.Snmp4jHeartbeatMib;
import org.snmp4j.agent.security.MutableVACM;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.MessageProcessingModel;
import org.snmp4j.security.AuthHMAC192SHA256;
import org.snmp4j.security.AuthMD5;
import org.snmp4j.security.AuthSHA;
import org.snmp4j.security.PrivAES128;
import org.snmp4j.security.PrivAES192;
import org.snmp4j.security.PrivAES256;
import org.snmp4j.security.PrivDES;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModel;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.security.UsmUser;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.Gauge32;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.SMIConstants;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.smi.TimeTicks;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.Variable;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.TransportMappings;
import org.snmp4j.util.ThreadPool;
import java.io.File;
import java.io.IOException;
import java.util.Map;
/**
* The TestAgent is a sample SNMP agent implementation of all
* features (MIB implementations) provided by the SNMP4J-Agent framework.
*
* Note, for snmp4s, this code is mostly a copy from snmp4j.
* And don't remove snmp users
*
*/
public class SnmpDeviceSimulatorV3 extends BaseAgent {
protected String address;
private Snmp4jHeartbeatMib heartbeatMIB;
private AgentppSimulationMib agentppSimulationMIB;
public SnmpDeviceSimulatorV3(CommandProcessor processor) throws IOException {
super(new File("SNMP4JTestAgentBC.cfg"), new File("SNMP4JTestAgentConfig.cfg"),
processor);
agent.setWorkerPool(ThreadPool.create("RequestPool", 4));
}
public void setUpMappings(Map<String, String> oidToResponseMappings) {
unregisterManagedObject(getSnmpv2MIB());
oidToResponseMappings.forEach((oid, response) -> {
registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response)));
});
}
protected void registerManagedObject(ManagedObject mo) {
try {
server.register(mo, null);
} catch (DuplicateRegistrationException ex) {
throw new RuntimeException(ex);
}
}
protected void unregisterManagedObject(MOGroup moGroup) {
moGroup.unregisterMOs(server, getContext(moGroup));
}
protected void registerManagedObjects() {
try {
server.register(createStaticIfTable(), null);
server.register(createStaticIfXTable(), null);
agentppSimulationMIB.registerMOs(server, null);
heartbeatMIB.registerMOs(server, null);
} catch (DuplicateRegistrationException ex) {
ex.printStackTrace();
}
}
protected void addNotificationTargets(SnmpTargetMIB targetMIB,
SnmpNotificationMIB notificationMIB) {
targetMIB.addDefaultTDomains();
targetMIB.addTargetAddress(new OctetString("notificationV2c"),
TransportDomains.transportDomainUdpIpv4,
new OctetString(new UdpAddress("127.0.0.1/162").getValue()),
200, 1,
new OctetString("notify"),
new OctetString("v2c"),
StorageType.permanent);
targetMIB.addTargetAddress(new OctetString("notificationV3"),
TransportDomains.transportDomainUdpIpv4,
new OctetString(new UdpAddress("127.0.0.1/1162").getValue()),
200, 1,
new OctetString("notify"),
new OctetString("v3notify"),
StorageType.permanent);
targetMIB.addTargetParams(new OctetString("v2c"),
MessageProcessingModel.MPv2c,
SecurityModel.SECURITY_MODEL_SNMPv2c,
new OctetString("cpublic"),
SecurityLevel.AUTH_PRIV,
StorageType.permanent);
targetMIB.addTargetParams(new OctetString("v3notify"),
MessageProcessingModel.MPv3,
SecurityModel.SECURITY_MODEL_USM,
new OctetString("v3notify"),
SecurityLevel.NOAUTH_NOPRIV,
StorageType.permanent);
notificationMIB.addNotifyEntry(new OctetString("default"),
new OctetString("notify"),
SnmpNotificationMIB.SnmpNotifyTypeEnum.inform,
StorageType.permanent);
}
protected void addViews(VacmMIB vacm) {
vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv1,
new OctetString("cpublic"),
new OctetString("v1v2group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c,
new OctetString("cpublic"),
new OctetString("v1v2group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("SHADES"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("MD5DES"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("TEST"),
new OctetString("v3test"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("SHA"),
new OctetString("v3restricted"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("SHAAES128"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("SHAAES192"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("SHAAES256"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("MD5AES128"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("MD5AES192"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("MD5AES256"),
new OctetString("v3group"),
StorageType.nonVolatile);
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("aboba"),
new OctetString("v3group"),
StorageType.nonVolatile);
//============================================//
// agent5-auth-priv
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("agent5"),
new OctetString("v3group"),
StorageType.nonVolatile);
//===========================================//
// agent002
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("agent002"),
new OctetString("v3group"),
StorageType.nonVolatile);
//===========================================//
// user001-auth-no-priv
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("user001"),
new OctetString("group001"),
StorageType.nonVolatile);
//===========================================//
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("v3notify"),
new OctetString("v3group"),
StorageType.nonVolatile);
//===========================================//
// group auth no priv
vacm.addGroup(SecurityModel.SECURITY_MODEL_USM,
new OctetString("v3notify-auth"),
new OctetString("group001"),
StorageType.nonVolatile);
//===========================================//
// my conf
vacm.addAccess(new OctetString("group001"), new OctetString("public"),
SecurityModel.SECURITY_MODEL_USM,
SecurityLevel.AUTH_NOPRIV,
MutableVACM.VACM_MATCH_EXACT,
new OctetString("fullReadView"),
new OctetString("fullWriteView"),
new OctetString("fullNotifyView"),
StorageType.nonVolatile);
vacm.addAccess(new OctetString("v1v2group"), new OctetString("public"),
SecurityModel.SECURITY_MODEL_ANY,
SecurityLevel.NOAUTH_NOPRIV,
MutableVACM.VACM_MATCH_EXACT,
new OctetString("fullReadView"),
new OctetString("fullWriteView"),
new OctetString("fullNotifyView"),
StorageType.nonVolatile);
vacm.addAccess(new OctetString("v3group"), new OctetString(),
SecurityModel.SECURITY_MODEL_USM,
SecurityLevel.AUTH_PRIV,
MutableVACM.VACM_MATCH_EXACT,
new OctetString("fullReadView"),
new OctetString("fullWriteView"),
new OctetString("fullNotifyView"),
StorageType.nonVolatile);
vacm.addAccess(new OctetString("v3restricted"), new OctetString(),
SecurityModel.SECURITY_MODEL_USM,
SecurityLevel.NOAUTH_NOPRIV,
MutableVACM.VACM_MATCH_EXACT,
new OctetString("restrictedReadView"),
new OctetString("restrictedWriteView"),
new OctetString("restrictedNotifyView"),
StorageType.nonVolatile);
vacm.addAccess(new OctetString("v3test"), new OctetString(),
SecurityModel.SECURITY_MODEL_USM,
SecurityLevel.AUTH_PRIV,
MutableVACM.VACM_MATCH_EXACT,
new OctetString("testReadView"),
new OctetString("testWriteView"),
new OctetString("testNotifyView"),
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("fullReadView"), new OID("1.3"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("fullWriteView"), new OID("1.3"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("fullNotifyView"), new OID("1.3"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("restrictedReadView"),
new OID("1.3.6.1.2"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("restrictedWriteView"),
new OID("1.3.6.1.2.1"),
new OctetString(),
VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
new OID("1.3.6.1.2"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("restrictedNotifyView"),
new OID("1.3.6.1.6.3.1"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("testReadView"),
new OID("1.3.6.1.2"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("testReadView"),
new OID("1.3.6.1.2.1.1"),
new OctetString(), VacmMIB.vacmViewExcluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("testWriteView"),
new OID("1.3.6.1.2.1"),
new OctetString(),
VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
vacm.addViewTreeFamily(new OctetString("testNotifyView"),
new OID("1.3.6.1.2"),
new OctetString(), VacmMIB.vacmViewIncluded,
StorageType.nonVolatile);
}
protected void addUsmUser(USM usm) {
UsmUser user = new UsmUser(new OctetString("SHADES"),
AuthSHA.ID,
new OctetString("SHADESAuthPassword"),
PrivDES.ID,
new OctetString("SHADESPrivPassword"));
// usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
usm.addUser(user.getSecurityName(), null, user);
user = new UsmUser(new OctetString("TEST"),
AuthSHA.ID,
new OctetString("maplesyrup"),
PrivDES.ID,
new OctetString("maplesyrup"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("SHA"),
AuthSHA.ID,
new OctetString("SHAAuthPassword"),
null,
null);
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("SHADES"),
AuthSHA.ID,
new OctetString("SHADESAuthPassword"),
PrivDES.ID,
new OctetString("SHADESPrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("MD5DES"),
AuthMD5.ID,
new OctetString("MD5DESAuthPassword"),
PrivDES.ID,
new OctetString("MD5DESPrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("SHAAES128"),
AuthSHA.ID,
new OctetString("SHAAES128AuthPassword"),
PrivAES128.ID,
new OctetString("SHAAES128PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("SHAAES192"),
AuthSHA.ID,
new OctetString("SHAAES192AuthPassword"),
PrivAES192.ID,
new OctetString("SHAAES192PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("SHAAES256"),
AuthSHA.ID,
new OctetString("SHAAES256AuthPassword"),
PrivAES256.ID,
new OctetString("SHAAES256PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("MD5AES128"),
AuthMD5.ID,
new OctetString("MD5AES128AuthPassword"),
PrivAES128.ID,
new OctetString("MD5AES128PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("MD5AES192"),
AuthHMAC192SHA256.ID,
new OctetString("MD5AES192AuthPassword"),
PrivAES192.ID,
new OctetString("MD5AES192PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
//==============================================================
user = new UsmUser(new OctetString("MD5AES256"),
AuthMD5.ID,
new OctetString("MD5AES256AuthPassword"),
PrivAES256.ID,
new OctetString("MD5AES256PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
user = new UsmUser(new OctetString("MD5AES256"),
AuthMD5.ID,
new OctetString("MD5AES256AuthPassword"),
PrivAES256.ID,
new OctetString("MD5AES256PrivPassword"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
OctetString securityName = new OctetString("aboba");
OctetString authenticationPassphrase = new OctetString("abobaaboba");
OctetString privacyPassphrase = new OctetString("abobaaboba");
OID authenticationProtocol = AuthSHA.ID;
OID privacyProtocol = PrivDES.ID; // FIXME: to config
user = new UsmUser(securityName, authenticationProtocol, authenticationPassphrase, privacyProtocol, privacyPassphrase);
usm.addUser(user);
//===============================================================//
user = new UsmUser(new OctetString("agent5"),
AuthSHA.ID,
new OctetString("authpass"),
PrivDES.ID,
new OctetString("privpass"));
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
//===============================================================//
// user001
user = new UsmUser(new OctetString("user001"),
AuthSHA.ID,
new OctetString("authpass"),
null, null);
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
//===============================================================//
// user002
user = new UsmUser(new OctetString("user001"),
null,
null,
null, null);
usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);
//===============================================================//
user = new UsmUser(new OctetString("v3notify"),
null,
null,
null,
null);
usm.addUser(user.getSecurityName(), null, user);
this.usm = usm;
}
private static DefaultMOTable createStaticIfXTable() {
MOTableSubIndex[] subIndexes =
new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
MOColumn[] columns = new MOColumn[19];
int c = 0;
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
MOAccessImpl.ACCESS_READ_ONLY); // ifName
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifInMulticastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifInBroadcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifOutMulticastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifOutBroadcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInOctets
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInUcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInMulticastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCInBroadcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutOctets
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutUcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutMulticastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_COUNTER32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHCOutBroadcastPkts
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_WRITE); // ifLinkUpDownTrapEnable
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_GAUGE32,
MOAccessImpl.ACCESS_READ_ONLY); // ifHighSpeed
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_WRITE); // ifPromiscuousMode
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_ONLY); // ifConnectorPresent
columns[c++] =
new MOMutableColumn(c, SMIConstants.SYNTAX_OCTET_STRING, // ifAlias
MOAccessImpl.ACCESS_READ_WRITE, null);
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_TIMETICKS,
MOAccessImpl.ACCESS_READ_ONLY); // ifCounterDiscontinuityTime
DefaultMOTable ifXTable =
new DefaultMOTable(new OID("1.3.6.1.2.1.31.1.1.1"), indexDef, columns);
MOMutableTableModel model = (MOMutableTableModel) ifXTable.getModel();
Variable[] rowValues1 = new Variable[] {
new OctetString("Ethernet-0"),
new Integer32(1),
new Integer32(2),
new Integer32(3),
new Integer32(4),
new Integer32(5),
new Integer32(6),
new Integer32(7),
new Integer32(8),
new Integer32(9),
new Integer32(10),
new Integer32(11),
new Integer32(12),
new Integer32(13),
new Integer32(14),
new Integer32(15),
new Integer32(16),
new OctetString("My eth"),
new TimeTicks(1000)
};
Variable[] rowValues2 = new Variable[] {
new OctetString("Loopback"),
new Integer32(21),
new Integer32(22),
new Integer32(23),
new Integer32(24),
new Integer32(25),
new Integer32(26),
new Integer32(27),
new Integer32(28),
new Integer32(29),
new Integer32(30),
new Integer32(31),
new Integer32(32),
new Integer32(33),
new Integer32(34),
new Integer32(35),
new Integer32(36),
new OctetString("My loop"),
new TimeTicks(2000)
};
model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
ifXTable.setVolatile(true);
return ifXTable;
}
private static DefaultMOTable createStaticIfTable() {
MOTableSubIndex[] subIndexes =
new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
MOColumn[] columns = new MOColumn[8];
int c = 0;
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_ONLY); // ifIndex
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
MOAccessImpl.ACCESS_READ_ONLY); // ifDescr
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_ONLY); // ifType
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_GAUGE32,
MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING,
MOAccessImpl.ACCESS_READ_ONLY); // ifPhysAddress
columns[c++] =
new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, // ifAdminStatus
MOAccessImpl.ACCESS_READ_WRITE, null);
columns[c++] =
new MOColumn(c, SMIConstants.SYNTAX_INTEGER,
MOAccessImpl.ACCESS_READ_ONLY); // ifOperStatus
DefaultMOTable ifTable =
new DefaultMOTable(new OID("1.3.6.1.2.1.2.2.1"), indexDef, columns);
MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel();
Variable[] rowValues1 = new Variable[] {
new Integer32(1),
new OctetString("eth0"),
new Integer32(6),
new Integer32(1500),
new Gauge32(100000000),
new OctetString("00:00:00:00:01"),
new Integer32(1),
new Integer32(1)
};
Variable[] rowValues2 = new Variable[] {
new Integer32(2),
new OctetString("loopback"),
new Integer32(24),
new Integer32(1500),
new Gauge32(10000000),
new OctetString("00:00:00:00:02"),
new Integer32(1),
new Integer32(1)
};
model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
ifTable.setVolatile(true);
return ifTable;
}
private static DefaultMOTable createStaticSnmp4sTable() {
MOTableSubIndex[] subIndexes =
new MOTableSubIndex[] { new MOTableSubIndex(SMIConstants.SYNTAX_INTEGER) };
MOTableIndex indexDef = new MOTableIndex(subIndexes, false);
MOColumn[] columns = new MOColumn[8];
int c = 0;
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_NULL, MOAccessImpl.ACCESS_READ_ONLY); // testNull
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // testBoolean
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifType
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY); // ifMtu
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_GAUGE32, MOAccessImpl.ACCESS_READ_ONLY); // ifSpeed
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_OCTET_STRING, MOAccessImpl.ACCESS_READ_ONLY); //ifPhysAddress
columns[c++] = new MOMutableColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_WRITE,
null);
// ifAdminStatus
columns[c++] = new MOColumn(c, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY);
// ifOperStatus
DefaultMOTable ifTable =
new DefaultMOTable(new OID("1.3.6.1.4.1.50000.1.1"), indexDef, columns);
MOMutableTableModel model = (MOMutableTableModel) ifTable.getModel();
Variable[] rowValues1 = new Variable[] {
new Integer32(1),
new OctetString("eth0"),
new Integer32(6),
new Integer32(1500),
new Gauge32(100000000),
new OctetString("00:00:00:00:01"),
new Integer32(1),
new Integer32(1)
};
Variable[] rowValues2 = new Variable[] {
new Integer32(2),
new OctetString("loopback"),
new Integer32(24),
new Integer32(1500),
new Gauge32(10000000),
new OctetString("00:00:00:00:02"),
new Integer32(1),
new Integer32(1)
};
model.addRow(new DefaultMOMutableRow2PC(new OID("1"), rowValues1));
model.addRow(new DefaultMOMutableRow2PC(new OID("2"), rowValues2));
ifTable.setVolatile(true);
return ifTable;
}
protected void initTransportMappings() throws IOException {
transportMappings = new TransportMapping[2];
Address addr = GenericAddress.parse(address);
TransportMapping tm =
TransportMappings.getInstance().createTransportMapping(addr);
transportMappings[0] = tm;
transportMappings[1] = new DefaultTcpTransportMapping(new TcpAddress(address));
}
public void start(String ip, String port) throws IOException {
address = ip + "/" + port;
//BasicConfigurator.configure();
init();
addShutdownHook();
// loadConfig(ImportModes.REPLACE_CREATE);
getServer().addContext(new OctetString("public"));
finishInit();
run();
sendColdStartNotification();
}
protected void unregisterManagedObjects() {
// here we should unregister those objects previously registered...
}
protected void addCommunities(SnmpCommunityMIB communityMIB) {
Variable[] com2sec = new Variable[] {
new OctetString("public"), // community name
new OctetString("cpublic"), // security name
getAgent().getContextEngineID(), // local engine ID
new OctetString("public"), // default context name
new OctetString(), // transport tag
new Integer32(StorageType.nonVolatile), // storage type
new Integer32(RowStatus.active) // row status
};
MOTableRow row =
communityMIB.getSnmpCommunityEntry().createRow(
new OctetString("public2public").toSubIndex(true), com2sec);
communityMIB.getSnmpCommunityEntry().addRow((SnmpCommunityMIB.SnmpCommunityEntryRow) row);
// snmpCommunityMIB.setSourceAddressFiltering(true);
}
protected void registerSnmpMIBs() {
heartbeatMIB = new Snmp4jHeartbeatMib(super.getNotificationOriginator(),
new OctetString(),
super.snmpv2MIB.getSysUpTime());
agentppSimulationMIB = new AgentppSimulationMib();
super.registerSnmpMIBs();
}
protected void initMessageDispatcher() {
this.dispatcher = new MessageDispatcherImpl();
this.mpv3 = new MPv3(this.agent.getContextEngineID().getValue());
this.usm = new USM(SecurityProtocols.getInstance(), this.agent.getContextEngineID(), this.updateEngineBoots());
SecurityModels.getInstance().addSecurityModel(this.usm);
SecurityProtocols.getInstance().addDefaultProtocols();
this.dispatcher.addMessageProcessingModel(new MPv1());
this.dispatcher.addMessageProcessingModel(new MPv2c());
this.dispatcher.addMessageProcessingModel(this.mpv3);
this.initSnmpSession();
}
}

49
common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java

@ -0,0 +1,49 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp;
import java.io.IOException;
import java.util.Map;
import java.util.Scanner;
public class SnmpTestV2 {
public static void main(String[] args) throws IOException {
SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public");
device.start();
device.setUpMappings(Map.of(
".1.3.6.1.2.1.1.1.50", "12",
".1.3.6.1.2.1.2.1.52", "56",
".1.3.6.1.2.1.3.1.54", "yes",
".1.3.6.1.2.1.7.1.58", ""
));
// while (true) {
// new Scanner(System.in).nextLine();
// device.sendTrap("127.0.0.1", 1062, Map.of(".1.3.6.1.2.87.1.56", "12"));
// System.out.println("sent");
// }
// Snmp snmp = new Snmp(device.transportMappings[0]);
// device.snmp.addCommandResponder(event -> {
// System.out.println(event);
// });
new Scanner(System.in).nextLine();
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save