Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard

pull/4537/head
Igor Kulikov 5 years ago
parent
commit
cf405be4f3
  1. 20
      application/src/main/data/upgrade/3.2.2/schema_update.sql
  2. 4
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  3. 9
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  4. 15
      application/src/main/java/org/thingsboard/server/controller/FirmwareController.java
  5. 4
      application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java
  6. 24
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  7. 141
      application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java
  8. 2
      application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java
  9. 113
      application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java
  10. 2
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
  11. 10
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  12. 61
      application/src/main/resources/thingsboard.yml
  13. 30
      application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java
  14. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  15. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java
  16. 36
      common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java
  17. 14
      common/data/src/main/java/org/thingsboard/server/common/data/Device.java
  18. 6
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java
  19. 6
      common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java
  20. 25
      common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java
  21. 13
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttTopics.java
  22. 30
      common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java
  23. 30
      common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java
  24. 91
      common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java
  25. 2
      common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java
  26. 3
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java
  27. 11
      common/queue/src/main/proto/queue.proto
  28. 23
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
  29. 37
      common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
  30. 61
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java
  31. 12
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportContextBootstrap.java
  32. 33
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java
  33. 27
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/client/LwM2mSoftwareUpdate.java
  34. 38
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java
  35. 47
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java
  36. 162
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java
  37. 29
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java
  38. 395
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java
  39. 160
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
  40. 22
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java
  41. 99
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java
  42. 20
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
  43. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java
  44. 32
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.java
  45. 37
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandlerUtil.java
  46. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java
  47. 205
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
  48. 85
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java
  49. 31
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java
  50. 16
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java
  51. 21
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java
  52. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java
  53. 26
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java
  54. 18
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java
  55. 27
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFirmwareUpdate.java
  56. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java
  57. 0
      common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
  58. 0
      common/transport/lwm2m/src/main/resources/lwm2mserver.jks
  59. 88
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
  60. 8
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
  61. 3
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java
  62. 7
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java
  63. 4
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java
  64. 217
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java
  65. 2
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
  66. 23
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  67. 34
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  68. 96
      dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java
  69. 7
      dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java
  70. 4
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  71. 16
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java
  72. 18
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java
  73. 21
      dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java
  74. 27
      dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java
  75. 9
      dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
  76. 10
      dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
  77. 28
      dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java
  78. 14
      dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java
  79. 10
      dao/src/main/resources/sql/schema-entities-hsql.sql
  80. 1
      dao/src/main/resources/sql/schema-entities-idx.sql
  81. 20
      dao/src/main/resources/sql/schema-entities.sql
  82. 5
      dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java
  83. 43
      dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java
  84. 232
      dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java
  85. 0
      transport/lwm2m/src/main/data/lwm2mserver.jks
  86. 2
      ui-ngx/src/app/core/http/entity.service.ts
  87. 15
      ui-ngx/src/app/core/http/firmware.service.ts
  88. 94
      ui-ngx/src/app/core/services/menu.service.ts
  89. 4
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.html
  90. 75
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts
  91. 79
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.html
  92. 20
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss
  93. 176
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts
  94. 143
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html
  95. 34
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss
  96. 257
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts
  97. 3
      ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html
  98. 6
      ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.ts
  99. 3
      ui-ngx/src/app/modules/home/components/event/event-table-config.ts
  100. 6
      ui-ngx/src/app/modules/home/components/home-components.module.ts

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

@ -63,6 +63,8 @@ CREATE TABLE IF NOT EXISTS firmware (
id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
created_time bigint NOT NULL, created_time bigint NOT NULL,
tenant_id uuid NOT NULL, tenant_id uuid NOT NULL,
device_profile_id uuid,
type varchar(32) NOT NULL,
title varchar(255) NOT NULL, title varchar(255) NOT NULL,
version varchar(255) NOT NULL, version varchar(255) NOT NULL,
file_name varchar(255), file_name varchar(255),
@ -77,10 +79,12 @@ CREATE TABLE IF NOT EXISTS firmware (
); );
ALTER TABLE device_profile ALTER TABLE device_profile
ADD COLUMN IF NOT EXISTS firmware_id uuid; ADD COLUMN IF NOT EXISTS firmware_id uuid,
ADD COLUMN IF NOT EXISTS software_id uuid;
ALTER TABLE device ALTER TABLE device
ADD COLUMN IF NOT EXISTS firmware_id uuid; ADD COLUMN IF NOT EXISTS firmware_id uuid,
ADD COLUMN IF NOT EXISTS software_id uuid;
DO $$ DO $$
BEGIN BEGIN
@ -90,11 +94,23 @@ DO $$
FOREIGN KEY (firmware_id) REFERENCES firmware(id); FOREIGN KEY (firmware_id) REFERENCES firmware(id);
END IF; END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN
ALTER TABLE device_profile
ADD CONSTRAINT fk_software_device_profile
FOREIGN KEY (firmware_id) REFERENCES firmware(id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN
ALTER TABLE device ALTER TABLE device
ADD CONSTRAINT fk_firmware_device ADD CONSTRAINT fk_firmware_device
FOREIGN KEY (firmware_id) REFERENCES firmware(id); FOREIGN KEY (firmware_id) REFERENCES firmware(id);
END IF; END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN
ALTER TABLE device
ADD CONSTRAINT fk_software_device
FOREIGN KEY (firmware_id) REFERENCES firmware(id);
END IF;
END; END;
$$; $$;

4
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -132,7 +132,7 @@ import org.thingsboard.server.service.firmware.FirmwareStateService;
import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.edge.rpc.EdgeGrpcService; import org.thingsboard.server.service.edge.rpc.EdgeGrpcService;
import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
import org.thingsboard.server.service.lwm2m.LwM2MModelsRepository; import org.thingsboard.server.service.lwm2m.LwM2MServerSecurityInfoRepository;
import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.resource.TbResourceService; import org.thingsboard.server.service.resource.TbResourceService;
@ -267,7 +267,7 @@ public abstract class BaseController {
protected TbDeviceProfileCache deviceProfileCache; protected TbDeviceProfileCache deviceProfileCache;
@Autowired @Autowired
protected LwM2MModelsRepository lwM2MModelsRepository; protected LwM2MServerSecurityInfoRepository lwM2MServerSecurityInfoRepository;
@Autowired(required = false) @Autowired(required = false)
protected EdgeService edgeService; protected EdgeService edgeService;

9
application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java

@ -146,12 +146,16 @@ public class DeviceProfileController extends BaseController {
checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE); checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
boolean isFirmwareChanged = false; boolean isFirmwareChanged = false;
boolean isSoftwareChanged = false;
if (!created) { if (!created) {
DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId()); DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId());
if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) { if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) {
isFirmwareChanged = true; isFirmwareChanged = true;
} }
if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) {
isSoftwareChanged = true;
}
} }
DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
@ -164,9 +168,8 @@ public class DeviceProfileController extends BaseController {
null, null,
created ? ActionType.ADDED : ActionType.UPDATED, null); created ? ActionType.ADDED : ActionType.UPDATED, null);
if (isFirmwareChanged) { firmwareStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged);
firmwareStateService.update(savedDeviceProfile);
}
sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(), sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
return savedDeviceProfile; return savedDeviceProfile;

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

@ -35,6 +35,8 @@ 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.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.FirmwareType;
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.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;
@ -133,6 +135,8 @@ public class FirmwareController extends BaseController {
Firmware firmware = new Firmware(firmwareId); Firmware firmware = new Firmware(firmwareId);
firmware.setCreatedTime(info.getCreatedTime()); firmware.setCreatedTime(info.getCreatedTime());
firmware.setTenantId(getTenantId()); firmware.setTenantId(getTenantId());
firmware.setDeviceProfileId(info.getDeviceProfileId());
firmware.setType(info.getType());
firmware.setTitle(info.getTitle()); firmware.setTitle(info.getTitle());
firmware.setVersion(info.getVersion()); firmware.setVersion(info.getVersion());
firmware.setAdditionalInfo(info.getAdditionalInfo()); firmware.setAdditionalInfo(info.getAdditionalInfo());
@ -175,17 +179,22 @@ public class FirmwareController extends BaseController {
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/firmwares/{hasData}", method = RequestMethod.GET) @RequestMapping(value = "/firmwares/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public PageData<FirmwareInfo> getFirmwares(@PathVariable("hasData") boolean hasData, public PageData<FirmwareInfo> getFirmwares(@PathVariable("deviceProfileId") String strDeviceProfileId,
@PathVariable("type") String strType,
@PathVariable("hasData") boolean hasData,
@RequestParam int pageSize, @RequestParam int pageSize,
@RequestParam int page, @RequestParam int page,
@RequestParam(required = false) String textSearch, @RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty, @RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException { @RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("deviceProfileId", strDeviceProfileId);
checkParameter("type", strType);
try { try {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndHasData(getTenantId(), hasData, pageLink)); return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(),
new DeviceProfileId(toUUID(strDeviceProfileId)), FirmwareType.valueOf(strType), hasData, pageLink));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }

4
application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java

@ -47,9 +47,9 @@ public class Lwm2mController extends BaseController {
@RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{securityMode}/{bootstrapServerIs}", method = RequestMethod.GET) @RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{securityMode}/{bootstrapServerIs}", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public ServerSecurityConfig getLwm2mBootstrapSecurityInfo(@PathVariable("securityMode") String securityMode, public ServerSecurityConfig getLwm2mBootstrapSecurityInfo(@PathVariable("securityMode") String securityMode,
@PathVariable("bootstrapServerIs") boolean bootstrapServerIs) throws ThingsboardException { @PathVariable("bootstrapServerIs") boolean bootstrapServer) throws ThingsboardException {
try { try {
return lwM2MModelsRepository.getBootstrapSecurityInfo(securityMode, bootstrapServerIs); return lwM2MServerSecurityInfoRepository.getServerSecurityInfo(securityMode, bootstrapServer);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }

24
application/src/main/java/org/thingsboard/server/controller/UserController.java

@ -32,6 +32,7 @@ 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.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
@ -89,13 +90,14 @@ public class UserController extends BaseController {
try { try {
UserId userId = new UserId(toUUID(strUserId)); UserId userId = new UserId(toUUID(strUserId));
User user = checkUserId(userId, Operation.READ); User user = checkUserId(userId, Operation.READ);
if(!user.getAdditionalInfo().isNull()) { if(user.getAdditionalInfo().isObject()) {
processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), DEFAULT_DASHBOARD); ObjectNode additionalInfo = (ObjectNode) user.getAdditionalInfo();
processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), HOME_DASHBOARD); processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD);
} processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD);
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
if(userCredentials.isEnabled()) { if(userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) {
addUserCredentialsEnabled((ObjectNode) user.getAdditionalInfo()); additionalInfo.put("userCredentialsEnabled", true);
}
} }
return user; return user;
} catch (Exception e) { } catch (Exception e) {
@ -103,14 +105,6 @@ public class UserController extends BaseController {
} }
} }
private void addUserCredentialsEnabled(ObjectNode additionalInfo) {
if(!additionalInfo.isNull()) {
if(!additionalInfo.has("userCredentialsEnabled")) {
additionalInfo.put("userCredentialsEnabled", true);
}
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET) @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET)
@ResponseBody @ResponseBody

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

@ -19,13 +19,17 @@ import com.google.common.util.concurrent.FutureCallback;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
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.FirmwareInfo; import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.firmware.FirmwareUtil;
import org.thingsboard.server.common.data.firmware.FirmwareType;
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;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@ -43,37 +47,49 @@ 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.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.queue.TbClusterService;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM;
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM_ALGORITHM; import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM;
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_SIZE; import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM_ALGORITHM;
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_TITLE; import static org.thingsboard.server.common.data.firmware.FirmwareKey.SIZE;
import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION; import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION;
import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey;
import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTargetTelemetryKey;
import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTelemetryKey;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
@Slf4j @Slf4j
@Service @Service
@TbCoreComponent @TbCoreComponent
public class DefaultFirmwareStateService implements FirmwareStateService { public class DefaultFirmwareStateService implements FirmwareStateService {
private final TbClusterService tbClusterService;
private final FirmwareService firmwareService; private final FirmwareService firmwareService;
private final DeviceService deviceService; private final DeviceService deviceService;
private final DeviceProfileService deviceProfileService; private final DeviceProfileService deviceProfileService;
private final RuleEngineTelemetryService telemetryService; private final RuleEngineTelemetryService telemetryService;
private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer; private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer;
public DefaultFirmwareStateService(FirmwareService firmwareService, public DefaultFirmwareStateService(TbClusterService tbClusterService, FirmwareService firmwareService,
DeviceService deviceService, DeviceService deviceService,
DeviceProfileService deviceProfileService, DeviceProfileService deviceProfileService,
RuleEngineTelemetryService telemetryService, RuleEngineTelemetryService telemetryService,
TbCoreQueueFactory coreQueueFactory) { TbCoreQueueFactory coreQueueFactory) {
this.tbClusterService = tbClusterService;
this.firmwareService = firmwareService; this.firmwareService = firmwareService;
this.deviceService = deviceService; this.deviceService = deviceService;
this.deviceProfileService = deviceProfileService; this.deviceProfileService = deviceProfileService;
@ -83,6 +99,11 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
@Override @Override
public void update(Device device, Device oldDevice) { public void update(Device device, Device oldDevice) {
updateFirmware(device, oldDevice);
updateSoftware(device, oldDevice);
}
private void updateFirmware(Device device, Device oldDevice) {
FirmwareId newFirmwareId = device.getFirmwareId(); FirmwareId newFirmwareId = device.getFirmwareId();
if (newFirmwareId == null) { if (newFirmwareId == null) {
DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
@ -97,35 +118,84 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
} }
if (!newFirmwareId.equals(oldFirmwareId)) { if (!newFirmwareId.equals(oldFirmwareId)) {
// Device was updated and new firmware is different from previous firmware. // Device was updated and new firmware is different from previous firmware.
send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis()); send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE);
} }
} else { } else {
// Device was updated and new firmware is not set. // Device was updated and new firmware is not set.
remove(device); remove(device, FIRMWARE);
} }
} else if (newFirmwareId != null) { } else if (newFirmwareId != null) {
// Device was created and firmware is defined. // Device was created and firmware is defined.
send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis()); send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE);
}
}
private void updateSoftware(Device device, Device oldDevice) {
FirmwareId newSoftwareId = device.getSoftwareId();
if (newSoftwareId == null) {
DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
newSoftwareId = newDeviceProfile.getSoftwareId();
}
if (oldDevice != null) {
if (newSoftwareId != null) {
FirmwareId oldSoftwareId = oldDevice.getSoftwareId();
if (oldSoftwareId == null) {
DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId());
oldSoftwareId = oldDeviceProfile.getSoftwareId();
}
if (!newSoftwareId.equals(oldSoftwareId)) {
// Device was updated and new firmware is different from previous firmware.
send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE);
}
} else {
// Device was updated and new firmware is not set.
remove(device, SOFTWARE);
}
} else if (newSoftwareId != null) {
// Device was created and firmware is defined.
send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE);
} }
} }
@Override @Override
public void update(DeviceProfile deviceProfile) { public void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged) {
TenantId tenantId = deviceProfile.getTenantId(); TenantId tenantId = deviceProfile.getTenantId();
if (isFirmwareChanged) {
update(tenantId, deviceProfile, FIRMWARE);
}
if (isSoftwareChanged) {
update(tenantId, deviceProfile, SOFTWARE);
}
}
private void update(TenantId tenantId, DeviceProfile deviceProfile, FirmwareType firmwareType) {
Function<PageLink, PageData<Device>> getDevicesFunction;
Consumer<Device> updateConsumer; Consumer<Device> updateConsumer;
switch (firmwareType) {
case FIRMWARE:
getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pl);
break;
case SOFTWARE:
getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId, deviceProfile.getName(), pl);
break;
default:
log.warn("Unsupported firmware type: [{}]", firmwareType);
return;
}
if (deviceProfile.getFirmwareId() != null) { if (deviceProfile.getFirmwareId() != null) {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts); updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, firmwareType);
} else { } else {
updateConsumer = this::remove; updateConsumer = d -> remove(d, firmwareType);
} }
PageLink pageLink = new PageLink(100); PageLink pageLink = new PageLink(100);
PageData<Device> pageData; PageData<Device> pageData;
do { do {
pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pageLink); pageData = getDevicesFunction.apply(pageLink);
pageData.getData().forEach(updateConsumer); pageData.getData().forEach(updateConsumer);
if (pageData.hasNext()) { if (pageData.hasNext()) {
@ -140,16 +210,17 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB())); FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()));
DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB()));
FirmwareType firmwareType = FirmwareType.valueOf(msg.getType());
long ts = msg.getTs(); long ts = msg.getTs();
Device device = deviceService.findDeviceById(tenantId, deviceId); Device device = deviceService.findDeviceById(tenantId, deviceId);
if (device == null) { if (device == null) {
log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId); log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId);
} else { } else {
FirmwareId currentFirmwareId = device.getFirmwareId(); FirmwareId currentFirmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
if (currentFirmwareId == null) { if (currentFirmwareId == null) {
currentFirmwareId = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()).getFirmwareId(); DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId());
currentFirmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType);
} }
if (targetFirmwareId.equals(currentFirmwareId)) { if (targetFirmwareId.equals(currentFirmwareId)) {
@ -162,7 +233,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
return isSuccess; return isSuccess;
} }
private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts) { private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts, FirmwareType firmwareType) {
ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder() ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
@ -170,6 +241,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()) .setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits())
.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()) .setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits())
.setType(firmwareType.name())
.setTs(ts) .setTs(ts)
.build(); .build();
@ -183,10 +255,10 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null);
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(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle())));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion()))); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion())));
telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(DataConstants.TARGET_FIRMWARE_TS, ts))); telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts)));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.QUEUED.name()))); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.QUEUED.name())));
telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() {
@Override @Override
@ -206,7 +278,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
TenantId tenantId = device.getTenantId(); TenantId tenantId = device.getTenantId();
DeviceId deviceId = device.getId(); DeviceId deviceId = device.getId();
BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.INITIATED.name())); BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.INITIATED.name()));
telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() {
@Override @Override
@ -221,13 +293,12 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
}); });
List<AttributeKvEntry> attributes = new ArrayList<>(); List<AttributeKvEntry> attributes = new ArrayList<>();
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), TITLE), firmware.getTitle())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), VERSION), firmware.getVersion())));
attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(firmware.getType(), SIZE), firmware.getDataSize())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM_ALGORITHM), firmware.getChecksumAlgorithm())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM), firmware.getChecksum())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_TITLE, firmware.getTitle())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_VERSION, firmware.getVersion())));
attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, firmware.getDataSize())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM_ALGORITHM, firmware.getChecksumAlgorithm())));
attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM, firmware.getChecksum())));
telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
@ -241,13 +312,15 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
}); });
} }
private void remove(Device device) { private void remove(Device device, FirmwareType firmwareType) {
telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, FirmwareUtil.getAttributeKeys(firmwareType),
Arrays.asList(FIRMWARE_TITLE, FIRMWARE_VERSION, FIRMWARE_SIZE, FIRMWARE_CHECKSUM_ALGORITHM, FIRMWARE_CHECKSUM),
new FutureCallback<>() { new FutureCallback<>() {
@Override @Override
public void onSuccess(@Nullable Void tmp) { public void onSuccess(@Nullable Void tmp) {
log.trace("[{}] Success remove target firmware attributes!", device.getId()); log.trace("[{}] Success remove target firmware attributes!", device.getId());
Set<AttributeKey> keysToNotify = new HashSet<>();
FirmwareUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key)));
tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null);
} }
@Override @Override

2
application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java

@ -23,7 +23,7 @@ public interface FirmwareStateService {
void update(Device device, Device oldDevice); void update(Device device, Device oldDevice);
void update(DeviceProfile deviceProfile); void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged);
boolean process(ToFirmwareStateServiceMsg msg); boolean process(ToFirmwareStateServiceMsg msg);

113
application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java → application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java

@ -16,14 +16,15 @@
package org.thingsboard.server.service.lwm2m; package org.thingsboard.server.service.lwm2m;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.Hex;
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.lwm2m.ServerSecurityConfig; import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap; import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig;
import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import java.math.BigInteger; import java.math.BigInteger;
@ -42,96 +43,59 @@ import java.security.spec.KeySpec;
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'")
public class LwM2MModelsRepository { public class LwM2MServerSecurityInfoRepository {
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; private final LwM2MTransportServerConfig serverConfig;
private final LwM2MTransportBootstrapConfig bootstrapConfig;
@Autowired
LwM2MTransportConfigServer contextServer;
@Autowired
LwM2MTransportConfigBootstrap contextBootStrap;
/** /**
* @param securityMode * @param securityMode
* @param bootstrapServerIs * @param bootstrapServer
* @return ServerSecurityConfig more value is default: Important - port, host, publicKey * @return ServerSecurityConfig more value is default: Important - port, host, publicKey
*/ */
public ServerSecurityConfig getBootstrapSecurityInfo(String securityMode, boolean bootstrapServerIs) { public ServerSecurityConfig getServerSecurityInfo(String securityMode, boolean bootstrapServer) {
LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(securityMode.toLowerCase()); LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(securityMode.toLowerCase());
return getBootstrapServer(bootstrapServerIs, lwM2MSecurityMode); ServerSecurityConfig result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig, lwM2MSecurityMode);
result.setBootstrapServerIs(bootstrapServer);
return result;
} }
/** private ServerSecurityConfig getServerSecurityConfig(LwM2MSecureServerConfig serverConfig, LwM2MSecurityMode mode) {
* @param bootstrapServerIs
* @param mode
* @return ServerSecurityConfig more value is default: Important - port, host, publicKey
*/
private ServerSecurityConfig getBootstrapServer(boolean bootstrapServerIs, LwM2MSecurityMode mode) {
ServerSecurityConfig bsServ = new ServerSecurityConfig(); ServerSecurityConfig bsServ = new ServerSecurityConfig();
bsServ.setBootstrapServerIs(bootstrapServerIs); bsServ.setServerId(serverConfig.getId());
if (bootstrapServerIs) { switch (mode) {
bsServ.setServerId(contextBootStrap.getBootstrapServerId()); case NO_SEC:
switch (mode) { bsServ.setHost(serverConfig.getHost());
case NO_SEC: bsServ.setPort(serverConfig.getPort());
bsServ.setHost(contextBootStrap.getBootstrapHost()); bsServ.setServerPublicKey("");
bsServ.setPort(contextBootStrap.getBootstrapPortNoSec()); break;
bsServ.setServerPublicKey(""); case PSK:
break; bsServ.setHost(serverConfig.getSecureHost());
case PSK: bsServ.setPort(serverConfig.getSecurePort());
bsServ.setHost(contextBootStrap.getBootstrapHostSecurity()); bsServ.setServerPublicKey("");
bsServ.setPort(contextBootStrap.getBootstrapPortSecurity()); break;
bsServ.setServerPublicKey(""); case RPK:
break; case X509:
case RPK: bsServ.setHost(serverConfig.getSecureHost());
case X509: bsServ.setPort(serverConfig.getSecurePort());
bsServ.setHost(contextBootStrap.getBootstrapHostSecurity()); bsServ.setServerPublicKey(getPublicKey(serverConfig.getCertificateAlias(), this.serverConfig.getPublicX(), this.serverConfig.getPublicY()));
bsServ.setPort(contextBootStrap.getBootstrapPortSecurity()); break;
bsServ.setServerPublicKey(getPublicKey (contextBootStrap.getBootstrapAlias(), this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY())); default:
break; break;
default:
break;
}
} else {
bsServ.setServerId(contextServer.getServerId());
switch (mode) {
case NO_SEC:
bsServ.setHost(contextServer.getServerHost());
bsServ.setPort(contextServer.getServerPortNoSec());
bsServ.setServerPublicKey("");
break;
case PSK:
bsServ.setHost(contextServer.getServerHostSecurity());
bsServ.setPort(contextServer.getServerPortSecurity());
bsServ.setServerPublicKey("");
break;
case RPK:
case X509:
bsServ.setHost(contextServer.getServerHostSecurity());
bsServ.setPort(contextServer.getServerPortSecurity());
bsServ.setServerPublicKey(getPublicKey (contextServer.getServerAlias(), this.contextServer.getServerPublicX(), this.contextServer.getServerPublicY()));
break;
default:
break;
}
} }
return bsServ; return bsServ;
} }
private String getPublicKey (String alias, String publicServerX, String publicServerY) { private String getPublicKey(String alias, String publicServerX, String publicServerY) {
String publicKey = getServerPublicKeyX509(alias); String publicKey = getServerPublicKeyX509(alias);
return publicKey != null ? publicKey : getRPKPublicKey(publicServerX, publicServerY); return publicKey != null ? publicKey : getRPKPublicKey(publicServerX, publicServerY);
} }
/**
* @param alias
* @return PublicKey format HexString or null
*/
private String getServerPublicKeyX509(String alias) { private String getServerPublicKeyX509(String alias) {
try { try {
X509Certificate serverCertificate = (X509Certificate) contextServer.getKeyStoreValue().getCertificate(alias); X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(alias);
return Hex.encodeHexString(serverCertificate.getEncoded()); return Hex.encodeHexString(serverCertificate.getEncoded());
} catch (CertificateEncodingException | KeyStoreException e) { } catch (CertificateEncodingException | KeyStoreException e) {
e.printStackTrace(); e.printStackTrace();
@ -139,11 +103,6 @@ public class LwM2MModelsRepository {
return null; return null;
} }
/**
* @param publicServerX
* @param publicServerY
* @return PublicKey format HexString or null
*/
private String getRPKPublicKey(String publicServerX, String publicServerY) { private String getRPKPublicKey(String publicServerX, String publicServerY) {
try { try {
/** Get Elliptic Curve Parameter spec for secp256r1 */ /** Get Elliptic Curve Parameter spec for secp256r1 */

2
application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java

@ -185,7 +185,7 @@ public class DefaultTbResourceService implements TbResourceService {
instance.setId(0); instance.setId(0);
List<LwM2mResourceObserve> resources = new ArrayList<>(); List<LwM2mResourceObserve> resources = new ArrayList<>();
obj.resources.forEach((k, v) -> { obj.resources.forEach((k, v) -> {
if (!v.operations.isExecutable()) { if (v.operations.isReadable()) {
LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false); LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
resources.add(lwM2MResourceObserve); resources.add(lwM2MResourceObserve);
} }

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

@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData;
import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.firmware.FirmwareUtil;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
@ -512,16 +514,17 @@ public class DefaultTransportApiService implements TransportApiService {
private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetFirmwareRequestMsg requestMsg) { private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetFirmwareRequestMsg requestMsg) {
TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
FirmwareType firmwareType = FirmwareType.valueOf(requestMsg.getType());
Device device = deviceService.findDeviceById(tenantId, deviceId); Device device = deviceService.findDeviceById(tenantId, deviceId);
if (device == null) { if (device == null) {
return getEmptyTransportApiResponseFuture(); return getEmptyTransportApiResponseFuture();
} }
FirmwareId firmwareId = device.getFirmwareId(); FirmwareId firmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
if (firmwareId == null) { if (firmwareId == null) {
firmwareId = deviceProfileCache.find(device.getDeviceProfileId()).getFirmwareId(); DeviceProfile deviceProfile = deviceProfileCache.find(device.getDeviceProfileId());
firmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType);
} }
TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder(); TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder();
@ -537,6 +540,7 @@ public class DefaultTransportApiService implements TransportApiService {
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.setType(firmwareInfo.getType().name());
builder.setTitle(firmwareInfo.getTitle()); builder.setTitle(firmwareInfo.getTitle());
builder.setVersion(firmwareInfo.getVersion()); builder.setVersion(firmwareInfo.getVersion());
builder.setFileName(firmwareInfo.getFileName()); builder.setFileName(firmwareInfo.getFileName());

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

@ -630,38 +630,14 @@ transport:
lwm2m: lwm2m:
# Enable/disable lvm2m transport protocol. # Enable/disable lvm2m transport protocol.
enabled: "${LWM2M_ENABLED:true}" enabled: "${LWM2M_ENABLED:true}"
# We choose a default timeout a bit higher to the MAX_TRANSMIT_WAIT(62-93s) which is the time from starting to
# send a Confirmable message to the time when an acknowledgement is no longer expected.
# DEFAULT_TIMEOUT = 2 * 60 * 1000l; 2 min in ms
timeout: "${LWM2M_TIMEOUT:120000}"
recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}"
response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}"
registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}"
update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}"
un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}"
secure:
# Certificate_x509:
# To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format
# Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}"
# key_store_path_file: "${KEY_STORE_PATH_FILE:/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks"
key_store_path_file: "${KEY_STORE_PATH_FILE:}"
key_store_password: "${LWM2M_KEYSTORE_PASSWORD_SERVER:server_ks_password}"
root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}"
enable_gen_new_key_psk_rpk: "${ENABLE_GEN_NEW_KEY_PSK_RPK:false}"
server: server:
id: "${LWM2M_SERVER_ID:123}" id: "${LWM2M_SERVER_ID:123}"
bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}"
bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC:5685}" bind_port: "${LWM2M_BIND_PORT:5685}"
secure: security:
bind_address_security: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}" bind_address: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}"
bind_port_security: "${LWM2M_BIND_PORT_SECURITY:5686}" bind_port: "${LWM2M_BIND_PORT_SECURITY:5686}"
# create_rpk: "${CREATE_RPK:}"
# Only for RPK: Public & Private Key. If the keystore file is missing or not working # Only for RPK: Public & Private Key. If the keystore file is missing or not working
# - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d0301070342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b]
# - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b],
# - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)]
public_x: "${LWM2M_SERVER_PUBLIC_X:05064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f358}" public_x: "${LWM2M_SERVER_PUBLIC_X:05064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f358}"
public_y: "${LWM2M_SERVER_PUBLIC_Y:5eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}" public_y: "${LWM2M_SERVER_PUBLIC_Y:5eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}"
private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}" private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}"
@ -671,19 +647,34 @@ transport:
enable: "${LWM2M_ENABLED_BS:true}" enable: "${LWM2M_ENABLED_BS:true}"
id: "${LWM2M_SERVER_ID_BS:111}" id: "${LWM2M_SERVER_ID_BS:111}"
bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC_BS:5687}" bind_port: "${LWM2M_BIND_PORT_BS:5687}"
secure: security:
bind_address_security: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}"
bind_port_security: "${LWM2M_BIND_PORT_SECURITY_BS:5688}" bind_port: "${LWM2M_BIND_PORT_SECURITY_BS:5688}"
# Only for RPK: Public & Private Key. If the keystore file is missing or not working # Only for RPK: Public & Private Key. If the keystore file is missing or not working
# - Elliptic Curve parameters : [secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)]
# - Public Key (Hex): [3059301306072a8648ce3d020106082a8648ce3d030107034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34]
# - Private Key (Hex): [308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34],
public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}" public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}"
public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:3fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}" public_y: "${LWM2M_SERVER_PUBLIC_Y_BS:3fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}"
private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_BS:308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}" private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_BS:308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}"
# Only Certificate_x509: # Only Certificate_x509:
alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}" alias: "${LWM2M_KEYSTORE_ALIAS_BS:bootstrap}"
security:
# Certificate_x509:
# To get helps about files format and how to generate it, see: https://github.com/eclipse/leshan/wiki/Credential-files-format
# Create new X509 Certificates: common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
key_store_type: "${LWM2M_KEYSTORE_TYPE:JKS}"
# key_store_path_file: "${KEY_STORE_PATH_FILE:/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks"
key_store: "${LWM2M_KEY_STORE:lwm2mserver.jks}"
key_store_password: "${LWM2M_KEY_STORE_PASSWORD:server_ks_password}"
root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}"
enable_gen_new_key_psk_rpk: "${ENABLE_GEN_NEW_KEY_PSK_RPK:false}"
timeout: "${LWM2M_TIMEOUT:120000}"
recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}"
response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}"
registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}"
update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}"
un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}"
log_max_length: "${LWM2M_LOG_MAX_LENGTH:100}"
# 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: snmp:

30
application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java

@ -24,10 +24,13 @@ import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile;
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.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.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
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.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
@ -38,6 +41,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
public abstract class BaseFirmwareControllerTest extends AbstractControllerTest { public abstract class BaseFirmwareControllerTest extends AbstractControllerTest {
@ -53,6 +57,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
private Tenant savedTenant; private Tenant savedTenant;
private User tenantAdmin; private User tenantAdmin;
private DeviceProfileId deviceProfileId;
@Before @Before
public void beforeTest() throws Exception { public void beforeTest() throws Exception {
@ -71,6 +76,11 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
tenantAdmin.setLastName("Downs"); tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
Assert.assertNotNull(savedDeviceProfile);
deviceProfileId = savedDeviceProfile.getId();
} }
@After @After
@ -84,6 +94,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test @Test
public void testSaveFirmware() throws Exception { public void testSaveFirmware() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo(); FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION); firmwareInfo.setVersion(VERSION);
@ -107,6 +119,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test @Test
public void testSaveFirmwareData() throws Exception { public void testSaveFirmwareData() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo(); FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION); firmwareInfo.setVersion(VERSION);
@ -137,6 +151,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test @Test
public void testUpdateFirmwareFromDifferentTenant() throws Exception { public void testUpdateFirmwareFromDifferentTenant() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo(); FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION); firmwareInfo.setVersion(VERSION);
@ -150,6 +166,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test @Test
public void testFindFirmwareInfoById() throws Exception { public void testFindFirmwareInfoById() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo(); FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION); firmwareInfo.setVersion(VERSION);
@ -163,6 +181,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test @Test
public void testFindFirmwareById() throws Exception { public void testFindFirmwareById() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo(); FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION); firmwareInfo.setVersion(VERSION);
@ -180,6 +200,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test @Test
public void testDeleteFirmware() throws Exception { public void testDeleteFirmware() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo(); FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION); firmwareInfo.setVersion(VERSION);
@ -197,6 +219,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
List<FirmwareInfo> firmwares = new ArrayList<>(); List<FirmwareInfo> firmwares = new ArrayList<>();
for (int i = 0; i < 165; i++) { for (int i = 0; i < 165; i++) {
FirmwareInfo firmwareInfo = new FirmwareInfo(); FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION + i); firmwareInfo.setVersion(VERSION + i);
@ -238,6 +262,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
for (int i = 0; i < 165; i++) { for (int i = 0; i < 165; i++) {
FirmwareInfo firmwareInfo = new FirmwareInfo(); FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION + i); firmwareInfo.setVersion(VERSION + i);
@ -257,7 +283,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
PageLink pageLink = new PageLink(24); PageLink pageLink = new PageLink(24);
PageData<FirmwareInfo> pageData; PageData<FirmwareInfo> pageData;
do { do {
pageData = doGetTypedWithPageLink("/api/firmwares/true?", pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/true?",
new TypeReference<>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedFirmwaresWithData.addAll(pageData.getData()); loadedFirmwaresWithData.addAll(pageData.getData());
@ -269,7 +295,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
List<FirmwareInfo> loadedFirmwaresWithoutData = new ArrayList<>(); List<FirmwareInfo> loadedFirmwaresWithoutData = new ArrayList<>();
pageLink = new PageLink(24); pageLink = new PageLink(24);
do { do {
pageData = doGetTypedWithPageLink("/api/firmwares/false?", pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/false?",
new TypeReference<>() { new TypeReference<>() {
}, pageLink); }, pageLink);
loadedFirmwaresWithoutData.addAll(pageData.getData()); loadedFirmwaresWithoutData.addAll(pageData.getData());

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

@ -65,6 +65,8 @@ public interface DeviceService {
PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink); PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink);
PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink);
PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink); PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink);

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

@ -18,6 +18,8 @@ package org.thingsboard.server.dao.firmware;
import com.google.common.util.concurrent.ListenableFuture; 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.firmware.FirmwareType;
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.PageData;
@ -37,7 +39,7 @@ public interface FirmwareService {
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> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink);
void deleteFirmware(TenantId tenantId, FirmwareId firmwareId); void deleteFirmware(TenantId tenantId, FirmwareId firmwareId);

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

@ -93,22 +93,26 @@ public class DataConstants {
public static final String USERNAME = "username"; public static final String USERNAME = "username";
public static final String PASSWORD = "password"; public static final String PASSWORD = "password";
//firmware //<<<<<<< HEAD
//telemetry //=======
public static final String CURRENT_FIRMWARE_TITLE = "cur_fw_title"; // //firmware
public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version"; // //telemetry
public static final String TARGET_FIRMWARE_TITLE = "target_fw_title"; // public static final String CURRENT_FIRMWARE_TITLE = "current_fw_title";
public static final String TARGET_FIRMWARE_VERSION = "target_fw_version"; // public static final String CURRENT_FIRMWARE_VERSION = "current_fw_version";
public static final String TARGET_FIRMWARE_TS = "target_fw_ts"; // public static final String TARGET_FIRMWARE_TITLE = "target_fw_title";
public static final String FIRMWARE_STATE = "fw_state"; // public static final String TARGET_FIRMWARE_VERSION = "target_fw_version";
// public static final String TARGET_FIRMWARE_TS = "target_fw_ts";
//attributes // public static final String FIRMWARE_STATE = "fw_state";
//telemetry //
public static final String FIRMWARE_TITLE = "fw_title"; // //attributes
public static final String FIRMWARE_VERSION = "fw_version"; // //telemetry
public static final String FIRMWARE_SIZE = "fw_size"; // public static final String FIRMWARE_TITLE = "fw_title";
public static final String FIRMWARE_CHECKSUM = "fw_checksum"; // public static final String FIRMWARE_VERSION = "fw_version";
public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm"; // public static final String FIRMWARE_SIZE = "fw_size";
// public static final String FIRMWARE_CHECKSUM = "fw_checksum";
// public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm";
//>>>>>>> origin/master
public static final String EDGE_MSG_SOURCE = "edge"; public static final String EDGE_MSG_SOURCE = "edge";
public static final String MSG_SOURCE_KEY = "source"; public static final String MSG_SOURCE_KEY = "source";
} }

14
common/data/src/main/java/org/thingsboard/server/common/data/Device.java

@ -32,7 +32,7 @@ import java.io.IOException;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Slf4j @Slf4j
public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId { public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId, HasFirmware {
private static final long serialVersionUID = 2807343040519543363L; private static final long serialVersionUID = 2807343040519543363L;
@ -50,6 +50,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
private byte[] deviceDataBytes; private byte[] deviceDataBytes;
private FirmwareId firmwareId; private FirmwareId firmwareId;
private FirmwareId softwareId;
public Device() { public Device() {
super(); super();
@ -69,6 +70,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.deviceProfileId = device.getDeviceProfileId(); this.deviceProfileId = device.getDeviceProfileId();
this.setDeviceData(device.getDeviceData()); this.setDeviceData(device.getDeviceData());
this.firmwareId = device.getFirmwareId(); this.firmwareId = device.getFirmwareId();
this.softwareId = device.getSoftwareId();
} }
public Device updateDevice(Device device) { public Device updateDevice(Device device) {
@ -79,6 +81,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.label = device.getLabel(); this.label = device.getLabel();
this.deviceProfileId = device.getDeviceProfileId(); this.deviceProfileId = device.getDeviceProfileId();
this.setDeviceData(device.getDeviceData()); this.setDeviceData(device.getDeviceData());
this.setFirmwareId(device.getFirmwareId());
this.setSoftwareId(device.getSoftwareId());
return this; return this;
} }
@ -171,6 +175,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.firmwareId = firmwareId; this.firmwareId = firmwareId;
} }
public FirmwareId getSoftwareId() {
return softwareId;
}
public void setSoftwareId(FirmwareId softwareId) {
this.softwareId = softwareId;
}
@Override @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();

6
common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java

@ -36,7 +36,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Slf4j @Slf4j
public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId { public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId, HasFirmware {
private TenantId tenantId; private TenantId tenantId;
@NoXss @NoXss
@ -59,6 +59,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
private FirmwareId firmwareId; private FirmwareId firmwareId;
private FirmwareId softwareId;
public DeviceProfile() { public DeviceProfile() {
super(); super();
} }
@ -77,6 +79,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
this.defaultQueueName = deviceProfile.getDefaultQueueName(); this.defaultQueueName = deviceProfile.getDefaultQueueName();
this.setProfileData(deviceProfile.getProfileData()); this.setProfileData(deviceProfile.getProfileData());
this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
this.firmwareId = deviceProfile.getFirmwareId();
this.softwareId = deviceProfile.getSoftwareId();
} }
@Override @Override

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

@ -19,6 +19,8 @@ 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;
import org.thingsboard.server.common.data.firmware.FirmwareType;
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;
@ -30,6 +32,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
private static final long serialVersionUID = 3168391583570815419L; private static final long serialVersionUID = 3168391583570815419L;
private TenantId tenantId; private TenantId tenantId;
private DeviceProfileId deviceProfileId;
private FirmwareType type;
private String title; private String title;
private String version; private String version;
private boolean hasData; private boolean hasData;
@ -51,6 +55,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
public FirmwareInfo(FirmwareInfo firmwareInfo) { public FirmwareInfo(FirmwareInfo firmwareInfo) {
super(firmwareInfo); super(firmwareInfo);
this.tenantId = firmwareInfo.getTenantId(); this.tenantId = firmwareInfo.getTenantId();
this.deviceProfileId = firmwareInfo.getDeviceProfileId();
this.type = firmwareInfo.getType();
this.title = firmwareInfo.getTitle(); this.title = firmwareInfo.getTitle();
this.version = firmwareInfo.getVersion(); this.version = firmwareInfo.getVersion();
this.hasData = firmwareInfo.isHasData(); this.hasData = firmwareInfo.isHasData();

25
common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.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;
import org.thingsboard.server.common.data.id.FirmwareId;
public interface HasFirmware {
FirmwareId getFirmwareId();
FirmwareId getSoftwareId();
}

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

@ -31,6 +31,7 @@ public class MqttTopics {
private static final String SUB_TOPIC = "+"; private static final String SUB_TOPIC = "+";
private static final String PROVISION = "/provision"; private static final String PROVISION = "/provision";
private static final String FIRMWARE = "/fw"; private static final String FIRMWARE = "/fw";
private static final String SOFTWARE = "/sw";
private static final String CHUNK = "/chunk/"; private static final String CHUNK = "/chunk/";
private static final String ERROR = "/error"; private static final String ERROR = "/error";
@ -75,9 +76,17 @@ public class MqttTopics {
// v2 topics // v2 topics
public static final String BASE_DEVICE_API_TOPIC_V2 = "v2"; public static final String BASE_DEVICE_API_TOPIC_V2 = "v2";
public static final String DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/"; public static final String REQUEST_ID_PATTERN = "(?<requestId>\\d+)";
public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + SUB_TOPIC + CHUNK + SUB_TOPIC; public static final String CHUNK_PATTERN = "(?<chunk>\\d+)";
public static final String DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN;
public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC;
public static final String DEVICE_FIRMWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + ERROR; public static final String DEVICE_FIRMWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + ERROR;
public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT = BASE_DEVICE_API_TOPIC_V2 + "%s" + RESPONSE + "/"+ "%s" + CHUNK + "%d";
public static final String DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN;
public static final String DEVICE_SOFTWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC;
public static final String DEVICE_SOFTWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + ERROR;
private MqttTopics() { private MqttTopics() {
} }

30
common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java

@ -0,0 +1,30 @@
/**
* 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.firmware;
import lombok.Getter;
public enum FirmwareKey {
TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm");
@Getter
private final String value;
FirmwareKey(String value) {
this.value = value;
}
}

30
common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java

@ -0,0 +1,30 @@
/**
* 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.firmware;
import lombok.Getter;
public enum FirmwareType {
FIRMWARE("fw"), SOFTWARE("sw");
@Getter
private final String keyPrefix;
FirmwareType(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
}

91
common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java

@ -0,0 +1,91 @@
/**
* 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.firmware;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.HasFirmware;
import org.thingsboard.server.common.data.id.FirmwareId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
@Slf4j
public class FirmwareUtil {
public static final List<String> ALL_FW_ATTRIBUTE_KEYS;
public static final List<String> ALL_SW_ATTRIBUTE_KEYS;
static {
ALL_FW_ATTRIBUTE_KEYS = new ArrayList<>();
for (FirmwareKey key : FirmwareKey.values()) {
ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(FIRMWARE, key));
}
ALL_SW_ATTRIBUTE_KEYS = new ArrayList<>();
for (FirmwareKey key : FirmwareKey.values()) {
ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(SOFTWARE, key));
}
}
public static List<String> getAttributeKeys(FirmwareType firmwareType) {
switch (firmwareType) {
case FIRMWARE:
return ALL_FW_ATTRIBUTE_KEYS;
case SOFTWARE:
return ALL_SW_ATTRIBUTE_KEYS;
}
return Collections.emptyList();
}
public static String getAttributeKey(FirmwareType type, FirmwareKey key) {
return type.getKeyPrefix() + "_" + key.getValue();
}
public static String getTargetTelemetryKey(FirmwareType type, FirmwareKey key) {
return getTelemetryKey("target_", type, key);
}
public static String getCurrentTelemetryKey(FirmwareType type, FirmwareKey key) {
return getTelemetryKey("current_", type, key);
}
private static String getTelemetryKey(String prefix, FirmwareType type, FirmwareKey key) {
return prefix + type.getKeyPrefix() + "_" + key.getValue();
}
public static String getTelemetryKey(FirmwareType type, FirmwareKey key) {
return type.getKeyPrefix() + "_" + key.getValue();
}
public static FirmwareId getFirmwareId(HasFirmware entity, FirmwareType firmwareType) {
switch (firmwareType) {
case FIRMWARE:
return entity.getFirmwareId();
case SOFTWARE:
return entity.getSoftwareId();
default:
log.warn("Unsupported firmware type: [{}]", firmwareType);
return null;
}
}
}

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, FIRMWARE ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE, SOFTWARE
} }

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

@ -32,7 +32,8 @@ public enum SessionMsgType {
CLAIM_REQUEST(), CLAIM_REQUEST(),
GET_FIRMWARE_REQUEST; GET_FIRMWARE_REQUEST,
GET_SOFTWARE_REQUEST;
private final boolean requiresRulesProcessing; private final boolean requiresRulesProcessing;

11
common/queue/src/main/proto/queue.proto

@ -388,16 +388,18 @@ message GetFirmwareRequestMsg {
int64 deviceIdLSB = 2; int64 deviceIdLSB = 2;
int64 tenantIdMSB = 3; int64 tenantIdMSB = 3;
int64 tenantIdLSB = 4; int64 tenantIdLSB = 4;
string type = 5;
} }
message GetFirmwareResponseMsg { message GetFirmwareResponseMsg {
ResponseStatus responseStatus = 1; ResponseStatus responseStatus = 1;
int64 firmwareIdMSB = 2; int64 firmwareIdMSB = 2;
int64 firmwareIdLSB = 3; int64 firmwareIdLSB = 3;
string title = 4; string type = 4;
string version = 5; string title = 5;
string contentType = 6; string version = 6;
string fileName = 7; string contentType = 7;
string fileName = 8;
} }
//Used to report session state to tb-Service and persist this state in the cache on the tb-Service level. //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level.
@ -711,4 +713,5 @@ message ToFirmwareStateServiceMsg {
int64 deviceIdLSB = 5; int64 deviceIdLSB = 5;
int64 firmwareIdMSB = 6; int64 firmwareIdMSB = 6;
int64 firmwareIdLSB = 7; int64 firmwareIdLSB = 7;
string type = 8;
} }

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

@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.common.msg.session.FeatureType;
import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.SessionMsgType;
@ -122,6 +123,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);
} else if (featureType.get() == FeatureType.FIRMWARE) { } else if (featureType.get() == FeatureType.FIRMWARE) {
processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST); processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST);
} else if (featureType.get() == FeatureType.SOFTWARE) {
processRequest(exchange, SessionMsgType.GET_SOFTWARE_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);
@ -326,12 +329,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
new CoapNoOpCallback(exchange)); new CoapNoOpCallback(exchange));
break; break;
case GET_FIRMWARE_REQUEST: case GET_FIRMWARE_REQUEST:
TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() getFirmwareCallback(sessionInfo, exchange, FirmwareType.FIRMWARE);
.setTenantIdMSB(sessionInfo.getTenantIdMSB()) break;
.setTenantIdLSB(sessionInfo.getTenantIdLSB()) case GET_SOFTWARE_REQUEST:
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) getFirmwareCallback(sessionInfo, exchange, FirmwareType.SOFTWARE);
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build();
transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange));
break; break;
} }
} catch (AdaptorException e) { } catch (AdaptorException e) {
@ -340,6 +341,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
} }
} }
private void getFirmwareCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, FirmwareType firmwareType) {
TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setType(firmwareType.name()).build();
transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange));
}
private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) { private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) {
tokenToNotificationCounterMap.remove(token); tokenToNotificationCounterMap.remove(token);
return tokenToSessionIdMap.remove(token); return tokenToSessionIdMap.remove(token);

37
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.firmware.FirmwareType;
import org.thingsboard.server.common.data.TbTransportService; 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;
@ -210,8 +211,29 @@ public class DeviceApiController implements TbTransportService {
public DeferredResult<ResponseEntity> getFirmware(@PathVariable("deviceToken") String deviceToken, public DeferredResult<ResponseEntity> getFirmware(@PathVariable("deviceToken") String deviceToken,
@RequestParam(value = "title") String title, @RequestParam(value = "title") String title,
@RequestParam(value = "version") String version, @RequestParam(value = "version") String version,
@RequestParam(value = "chunkSize", required = false, defaultValue = "0") int chunkSize, @RequestParam(value = "size", required = false, defaultValue = "0") int size,
@RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) { @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.FIRMWARE);
}
@RequestMapping(value = "/{deviceToken}/software", method = RequestMethod.GET)
public DeferredResult<ResponseEntity> getSoftware(@PathVariable("deviceToken") String deviceToken,
@RequestParam(value = "title") String title,
@RequestParam(value = "version") String version,
@RequestParam(value = "size", required = false, defaultValue = "0") int size,
@RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.SOFTWARE);
}
@RequestMapping(value = "/provision", method = RequestMethod.POST)
public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) {
DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json),
new DeviceProvisionCallback(responseWriter));
return responseWriter;
}
private DeferredResult<ResponseEntity> getFirmwareCallback(String deviceToken, String title, String version, int size, int chunk, FirmwareType firmwareType) {
DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>(); DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
@ -219,20 +241,13 @@ public class DeviceApiController implements TbTransportService {
.setTenantIdMSB(sessionInfo.getTenantIdMSB()) .setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB()) .setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build(); .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, chunkSize, chunk)); .setType(firmwareType.name()).build();
transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, size, chunk));
})); }));
return responseWriter; return responseWriter;
} }
@RequestMapping(value = "/provision", method = RequestMethod.POST)
public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) {
DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json),
new DeviceProvisionCallback(responseWriter));
return responseWriter;
}
private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> { private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> {
private final TransportContext transportContext; private final TransportContext transportContext;
private final DeferredResult<ResponseEntity> responseWriter; private final DeferredResult<ResponseEntity> responseWriter;

61
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java

@ -25,10 +25,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSecurityStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore;
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContextServer; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.AlgorithmParameters; import java.security.AlgorithmParameters;
@ -55,7 +58,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig; import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
@Slf4j @Slf4j
@Component @Component
@ -66,10 +69,10 @@ public class LwM2MTransportBootstrapServerConfiguration {
private boolean pskMode = false; private boolean pskMode = false;
@Autowired @Autowired
private LwM2MTransportContextBootstrap contextBs; private LwM2MTransportServerConfig serverConfig;
@Autowired @Autowired
private LwM2mTransportContextServer contextS; private LwM2MTransportContextBootstrap contextBs;
@Autowired @Autowired
private LwM2MBootstrapSecurityStore lwM2MBootstrapSecurityStore; private LwM2MBootstrapSecurityStore lwM2MBootstrapSecurityStore;
@ -81,19 +84,18 @@ public class LwM2MTransportBootstrapServerConfiguration {
@Bean @Bean
public LeshanBootstrapServer getLeshanBootstrapServer() { public LeshanBootstrapServer getLeshanBootstrapServer() {
log.info("Prepare and start BootstrapServer... PostConstruct"); log.info("Prepare and start BootstrapServer... PostConstruct");
return this.getLhBootstrapServer(this.contextBs.getCtxBootStrap().getBootstrapPortNoSec(), this.contextBs.getCtxBootStrap().getBootstrapPortSecurity()); return this.getLhBootstrapServer(this.contextBs.getCtxBootStrap().getPort(), this.contextBs.getCtxBootStrap().getSecurePort());
} }
public LeshanBootstrapServer getLhBootstrapServer(Integer bootstrapPortNoSec, Integer bootstrapSecurePort) { public LeshanBootstrapServer getLhBootstrapServer(Integer bootstrapPortNoSec, Integer bootstrapSecurePort) {
LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder(); LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder();
builder.setLocalAddress(this.contextBs.getCtxBootStrap().getBootstrapHost(), bootstrapPortNoSec); builder.setLocalAddress(this.contextBs.getCtxBootStrap().getHost(), bootstrapPortNoSec);
builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getBootstrapHostSecurity(), bootstrapSecurePort); builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getSecureHost(), bootstrapSecurePort);
/** Create CoAP Config */ /** Create CoAP Config */
builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort)); builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort));
/** Define model provider (Create Models )*/ /** Define model provider (Create Models )*/
// builder.setModel(new StaticModel(contextS.getLwM2MTransportConfigServer().getModelsValueCommon()));
/** Create credentials */ /** Create credentials */
this.setServerWithCredentials(builder); this.setServerWithCredentials(builder);
@ -107,14 +109,13 @@ public class LwM2MTransportBootstrapServerConfiguration {
/** Create and Set DTLS Config */ /** Create and Set DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
dtlsConfig.setRecommendedSupportedGroupsOnly(this.contextS.getLwM2MTransportConfigServer().isRecommendedSupportedGroups()); dtlsConfig.setRecommendedSupportedGroupsOnly(serverConfig.isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getLwM2MTransportConfigServer().isRecommendedCiphers()); dtlsConfig.setRecommendedCipherSuitesOnly(serverConfig.isRecommendedCiphers());
if (this.pskMode) { if (this.pskMode) {
dtlsConfig.setSupportedCipherSuites( dtlsConfig.setSupportedCipherSuites(
TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CCM_8,
TLS_PSK_WITH_AES_128_CBC_SHA256); TLS_PSK_WITH_AES_128_CBC_SHA256);
} } else {
else {
dtlsConfig.setSupportedCipherSuites( dtlsConfig.setSupportedCipherSuites(
TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CCM_8,
TLS_PSK_WITH_AES_128_CBC_SHA256, TLS_PSK_WITH_AES_128_CBC_SHA256,
@ -134,10 +135,10 @@ public class LwM2MTransportBootstrapServerConfiguration {
private void setServerWithCredentials(LeshanBootstrapServerBuilder builder) { private void setServerWithCredentials(LeshanBootstrapServerBuilder builder) {
try { try {
if (this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue() != null) { if (serverConfig.getKeyStoreValue() != null) {
KeyStore keyStoreServer = this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue(); KeyStore keyStoreServer = serverConfig.getKeyStoreValue();
if (this.setBuilderX509(builder)) { if (this.setBuilderX509(builder)) {
X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getLwM2MTransportConfigServer().getRootAlias()); X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(serverConfig.getRootCertificateAlias());
if (rootCAX509Cert != null) { if (rootCAX509Cert != null) {
X509Certificate[] trustedCertificates = new X509Certificate[1]; X509Certificate[] trustedCertificates = new X509Certificate[1];
trustedCertificates[0] = rootCAX509Cert; trustedCertificates[0] = rootCAX509Cert;
@ -168,12 +169,10 @@ public class LwM2MTransportBootstrapServerConfiguration {
* For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
*/ */
try { try {
X509Certificate serverCertificate = (X509Certificate) this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias()); X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getCertificateAlias());
PrivateKey privateKey = (PrivateKey) this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getLwM2MTransportConfigServer().getKeyStorePasswordServer() == null ? null : this.contextS.getLwM2MTransportConfigServer().getKeyStorePasswordServer().toCharArray()); PrivateKey privateKey = (PrivateKey) serverConfig.getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getCertificateAlias(), serverConfig.getKeyStorePassword() == null ? null : serverConfig.getKeyStorePassword().toCharArray());
PublicKey publicKey = serverCertificate.getPublicKey(); PublicKey publicKey = serverCertificate.getPublicKey();
if (serverCertificate != null && if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) {
privateKey != null && privateKey.getEncoded().length > 0 &&
publicKey != null && publicKey.getEncoded().length > 0) {
builder.setPublicKey(serverCertificate.getPublicKey()); builder.setPublicKey(serverCertificate.getPublicKey());
builder.setPrivateKey(privateKey); builder.setPrivateKey(privateKey);
builder.setCertificateChain(new X509Certificate[]{serverCertificate}); builder.setCertificateChain(new X509Certificate[]{serverCertificate});
@ -200,10 +199,12 @@ public class LwM2MTransportBootstrapServerConfiguration {
} }
private void infoPramsUri(String mode) { private void infoPramsUri(String mode) {
log.info("Bootstrap Server uses [{}]: serverNoSecureURI : [{}], serverSecureURI : [{}]", log.info("Bootstrap Server uses [{}]: serverNoSecureURI : [{}:{}], serverSecureURI : [{}:{}]",
mode, mode,
this.contextBs.getCtxBootStrap().getBootstrapHost() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortNoSec(), this.contextBs.getCtxBootStrap().getHost(),
this.contextBs.getCtxBootStrap().getBootstrapHostSecurity() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortSecurity()); this.contextBs.getCtxBootStrap().getPort(),
this.contextBs.getCtxBootStrap().getSecureHost(),
this.contextBs.getCtxBootStrap().getSecurePort());
} }
@ -237,23 +238,25 @@ public class LwM2MTransportBootstrapServerConfiguration {
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1")); algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
if (this.contextBs.getCtxBootStrap().getBootstrapPublicX() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicX().isEmpty() && this.contextBs.getCtxBootStrap().getBootstrapPublicY() != null && !this.contextBs.getCtxBootStrap().getBootstrapPublicY().isEmpty()) { LwM2MTransportBootstrapConfig serverConfig = this.contextBs.getCtxBootStrap();
if (StringUtils.isNotEmpty(serverConfig.getPublicX()) && StringUtils.isNotEmpty(serverConfig.getPublicY())) {
/** Get point values */ /** Get point values */
byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray()); byte[] publicX = Hex.decodeHex(serverConfig.getPublicX().toCharArray());
byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray()); byte[] publicY = Hex.decodeHex(serverConfig.getPublicY().toCharArray());
/** Create key specs */ /** Create key specs */
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec); parameterSpec);
/** Get public key */ /** Get public key */
this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
} }
if (this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded() != null && !this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded().isEmpty()) { String privateEncodedKey = serverConfig.getPrivateEncoded();
if (StringUtils.isNotEmpty(privateEncodedKey)) {
/** Get private key */ /** Get private key */
byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded().toCharArray()); byte[] privateS = Hex.decodeHex(privateEncodedKey.toCharArray());
try { try {
this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS)); this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS));
} catch (InvalidKeySpecException ignore2) { } catch (InvalidKeySpecException ignore2) {
log.error("Invalid Bootstrap Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded()); log.error("Invalid Bootstrap Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", privateEncodedKey);
} }
} }
} }

12
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportContextBootstrap.java

@ -34,7 +34,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportContext; import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
@Slf4j @Slf4j
@ -42,13 +42,13 @@ import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstr
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith'") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith'")
public class LwM2MTransportContextBootstrap extends TransportContext { public class LwM2MTransportContextBootstrap extends TransportContext {
private final LwM2MTransportConfigBootstrap lwM2MTransportConfigBootstrap; private final LwM2MTransportBootstrapConfig lwM2MTransportBootstrapConfig;
public LwM2MTransportContextBootstrap(LwM2MTransportConfigBootstrap ctxBootStrap) { public LwM2MTransportContextBootstrap(LwM2MTransportBootstrapConfig ctxBootStrap) {
this.lwM2MTransportConfigBootstrap = ctxBootStrap; this.lwM2MTransportBootstrapConfig = ctxBootStrap;
} }
public LwM2MTransportConfigBootstrap getCtxBootStrap() { public LwM2MTransportBootstrapConfig getCtxBootStrap() {
return this.lwM2MTransportConfigBootstrap; return this.lwM2MTransportBootstrapConfig;
} }
} }

33
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java

@ -34,8 +34,9 @@ import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContextServer; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
@ -43,12 +44,12 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.BOOTSTRAP_SERVER; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.BOOTSTRAP_SERVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LWM2M_SERVER; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LWM2M_SERVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.SERVERS; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.SERVERS;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getBootstrapParametersFromThingsboard; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.getBootstrapParametersFromThingsboard;
@Slf4j @Slf4j
@Service("LwM2MBootstrapSecurityStore") @Service("LwM2MBootstrapSecurityStore")
@ -59,17 +60,19 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
private final LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator; private final LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator;
private final LwM2mTransportContextServer context; private final LwM2mTransportContext context;
private final LwM2mTransportServerHelper helper;
public LwM2MBootstrapSecurityStore(EditableBootstrapConfigStore bootstrapConfigStore, LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator, LwM2mTransportContextServer context) { public LwM2MBootstrapSecurityStore(EditableBootstrapConfigStore bootstrapConfigStore, LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator, LwM2mTransportContext context, LwM2mTransportServerHelper helper) {
this.bootstrapConfigStore = bootstrapConfigStore; this.bootstrapConfigStore = bootstrapConfigStore;
this.lwM2MCredentialsSecurityInfoValidator = lwM2MCredentialsSecurityInfoValidator; this.lwM2MCredentialsSecurityInfoValidator = lwM2MCredentialsSecurityInfoValidator;
this.context = context; this.context = context;
this.helper = helper;
} }
@Override @Override
public List<SecurityInfo> getAllByEndpoint(String endPoint) { public List<SecurityInfo> getAllByEndpoint(String endPoint) {
ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(endPoint, LwM2mTransportHandler.LwM2mTypeServer.BOOTSTRAP); ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(endPoint, LwM2mTransportHandlerUtil.LwM2mTypeServer.BOOTSTRAP);
if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
/** add value to store from BootstrapJson */ /** add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store); this.setBootstrapConfigScurityInfo(store);
@ -93,7 +96,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
@Override @Override
public SecurityInfo getByIdentity(String identity) { public SecurityInfo getByIdentity(String identity) {
ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(identity, LwM2mTransportHandler.LwM2mTypeServer.BOOTSTRAP); ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(identity, LwM2mTransportHandlerUtil.LwM2mTypeServer.BOOTSTRAP);
if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
/** add value to store from BootstrapJson */ /** add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store); this.setBootstrapConfigScurityInfo(store);
@ -158,19 +161,19 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class); LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class);
LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class); LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class);
UUID sessionUUiD = UUID.randomUUID(); UUID sessionUUiD = UUID.randomUUID();
TransportProtos.SessionInfoProto sessionInfo = context.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, sessionInfo)); context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, sessionInfo));
if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LW2M_INFO, store.getEndPoint()); String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LW2M_INFO, store.getEndPoint());
context.sendParametersOnThingsboardTelemetry(context.getKvLogyToThingsboard(logMsg), sessionInfo); helper.sendParametersOnThingsboardTelemetry(helper.getKvLogyToThingsboard(logMsg), sessionInfo);
return lwM2MBootstrapConfig; return lwM2MBootstrapConfig;
} else { } else {
log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint()); log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndPoint()); log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndPoint());
String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndPoint()); String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndPoint());
context.sendParametersOnThingsboardTelemetry(context.getKvLogyToThingsboard(logMsg), sessionInfo); helper.sendParametersOnThingsboardTelemetry(helper.getKvLogyToThingsboard(logMsg), sessionInfo);
return null; return null;
} }
} }

27
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/client/LwM2mSoftwareUpdate.java

@ -0,0 +1,27 @@
/**
* 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.lwm2m.client;
import lombok.Data;
import java.util.UUID;
@Data
public class LwM2mSoftwareUpdate {
private volatile String clientSwVersion;
private volatile String currentSwVersion;
private volatile UUID currentSwId;
}

38
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java

@ -0,0 +1,38 @@
/**
* 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.lwm2m.config;
public interface LwM2MSecureServerConfig {
Integer getId();
String getHost();
Integer getPort();
String getSecureHost();
Integer getSecurePort();
String getPublicX();
String getPublicY();
String getPrivateEncoded();
String getCertificateAlias();
}

47
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.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.common.transport.lwm2m; package org.thingsboard.server.transport.lwm2m.config;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -29,53 +29,42 @@ import java.util.Map;
@Slf4j @Slf4j
@Component @Component
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'")
public class LwM2MTransportConfigBootstrap { public class LwM2MTransportBootstrapConfig implements LwM2MSecureServerConfig {
@Getter
@Value("${transport.lwm2m.bootstrap.enable:}")
private Boolean bootstrapEnable;
@Getter @Getter
@Value("${transport.lwm2m.bootstrap.id:}") @Value("${transport.lwm2m.bootstrap.id:}")
private Integer bootstrapServerId; private Integer id;
@Getter @Getter
@Value("${transport.lwm2m.bootstrap.bind_address:}") @Value("${transport.lwm2m.bootstrap.bind_address:}")
private String bootstrapHost; private String host;
@Getter @Getter
@Value("${transport.lwm2m.bootstrap.bind_port_no_sec:}") @Value("${transport.lwm2m.bootstrap.bind_port:}")
private Integer bootstrapPortNoSec; private Integer port;
@Getter @Getter
@Value("${transport.lwm2m.bootstrap.secure.bind_address_security:}") @Value("${transport.lwm2m.bootstrap.security.bind_address:}")
private String bootstrapHostSecurity; private String secureHost;
@Getter @Getter
@Value("${transport.lwm2m.bootstrap.secure.bind_port_security:}") @Value("${transport.lwm2m.bootstrap.security.bind_port:}")
private Integer bootstrapPortSecurity; private Integer securePort;
@Getter @Getter
@Value("${transport.lwm2m.bootstrap.secure.public_x:}") @Value("${transport.lwm2m.bootstrap.security.public_x:}")
private String bootstrapPublicX; private String publicX;
@Getter @Getter
@Value("${transport.lwm2m.bootstrap.secure.public_y:}") @Value("${transport.lwm2m.bootstrap.security.public_y:}")
private String bootstrapPublicY; private String publicY;
@Getter @Getter
@Setter @Value("${transport.lwm2m.bootstrap.security.private_encoded:}")
private PublicKey bootstrapPublicKey; private String privateEncoded;
@Getter @Getter
@Value("${transport.lwm2m.bootstrap.secure.private_encoded:}") @Value("${transport.lwm2m.bootstrap.security.alias:}")
private String bootstrapPrivateEncoded; private String certificateAlias;
@Getter
@Value("${transport.lwm2m.bootstrap.secure.alias:}")
private String bootstrapAlias;
@Getter
@Setter
private Map<String /** clientEndPoint */, TransportProtos.ValidateDeviceCredentialsResponseMsg> sessions;
} }

162
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java

@ -0,0 +1,162 @@
/**
* 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.lwm2m.config;
import com.google.common.io.Resources;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
@Slf4j
@Component
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'")
public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig {
@Getter
@Setter
private LwM2mModelProvider modelProvider;
@Getter
@Value("${transport.lwm2m.timeout:}")
private Long timeout;
@Getter
@Value("${transport.sessions.report_timeout}")
private long sessionReportTimeout;
@Getter
@Value("${transport.lwm2m.recommended_ciphers:}")
private boolean recommendedCiphers;
@Getter
@Value("${transport.lwm2m.recommended_supported_groups:}")
private boolean recommendedSupportedGroups;
@Getter
@Value("${transport.lwm2m.response_pool_size:}")
private int responsePoolSize;
@Getter
@Value("${transport.lwm2m.registered_pool_size:}")
private int registeredPoolSize;
@Getter
@Value("${transport.lwm2m.update_registered_pool_size:}")
private int updateRegisteredPoolSize;
@Getter
@Value("${transport.lwm2m.un_registered_pool_size:}")
private int unRegisteredPoolSize;
@Getter
@Value("${transport.lwm2m.security.key_store_type:}")
private String keyStoreType;
@Getter
@Value("${transport.lwm2m.security.key_store:}")
private String keyStorePathFile;
@Getter
@Setter
private KeyStore keyStoreValue;
@Getter
@Value("${transport.lwm2m.security.key_store_password:}")
private String keyStorePassword;
@Getter
@Value("${transport.lwm2m.security.root_alias:}")
private String rootCertificateAlias;
@Getter
@Value("${transport.lwm2m.security.enable_gen_new_key_psk_rpk:}")
private Boolean enableGenNewKeyPskRpk;
@Getter
@Value("${transport.lwm2m.server.id:}")
private Integer id;
@Getter
@Value("${transport.lwm2m.server.bind_address:}")
private String host;
@Getter
@Value("${transport.lwm2m.server.bind_port:}")
private Integer port;
@Getter
@Value("${transport.lwm2m.server.security.bind_address:}")
private String secureHost;
@Getter
@Value("${transport.lwm2m.server.security.bind_port:}")
private Integer securePort;
@Getter
@Value("${transport.lwm2m.server.security.public_x:}")
private String publicX;
@Getter
@Value("${transport.lwm2m.server.security.public_y:}")
private String publicY;
@Getter
@Value("${transport.lwm2m.server.security.private_encoded:}")
private String privateEncoded;
@Getter
@Value("${transport.lwm2m.server.security.alias:}")
private String certificateAlias;
@Getter
@Value("${transport.lwm2m.log_max_length:}")
private int logMaxLength;
@PostConstruct
public void init() {
URI uri = null;
try {
uri = Resources.getResource(keyStorePathFile).toURI();
log.error("URI: {}", uri);
File keyStoreFile = new File(uri);
InputStream inKeyStore = new FileInputStream(keyStoreFile);
keyStoreValue = KeyStore.getInstance(keyStoreType);
keyStoreValue.load(inKeyStore, keyStorePassword == null ? null : keyStorePassword.toCharArray());
} catch (Exception e) {
log.warn("Unable to lookup LwM2M keystore. Reason: {}, {}" , uri, e.getMessage());
// Absence of the key store should not block user from using plain LwM2M
// throw new RuntimeException("Failed to lookup LwM2M keystore: " + (uri != null ? uri.toString() : ""), e);
}
}
}

29
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.lwm2m.secure; package org.thingsboard.server.transport.lwm2m.secure;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.core.util.SecurityUtil; import org.eclipse.leshan.core.util.SecurityUtil;
@ -26,8 +27,10 @@ import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContextServer; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
@ -44,13 +47,11 @@ import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X5
@Slf4j @Slf4j
@Component @Component
@TbLwM2mTransportComponent @TbLwM2mTransportComponent
@RequiredArgsConstructor
public class LwM2mCredentialsSecurityInfoValidator { public class LwM2mCredentialsSecurityInfoValidator {
private final LwM2mTransportContextServer contextS; private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config;
public LwM2mCredentialsSecurityInfoValidator(LwM2mTransportContextServer contextS) {
this.contextS = contextS;
}
/** /**
* Request to thingsboard Response from thingsboard ValidateDeviceLwM2MCredentials * Request to thingsboard Response from thingsboard ValidateDeviceLwM2MCredentials
@ -58,17 +59,17 @@ public class LwM2mCredentialsSecurityInfoValidator {
* @param keyValue - * @param keyValue -
* @return ValidateDeviceCredentialsResponseMsg and SecurityInfo * @return ValidateDeviceCredentialsResponseMsg and SecurityInfo
*/ */
public ReadResultSecurityStore createAndValidateCredentialsSecurityInfo(String endpoint, LwM2mTransportHandler.LwM2mTypeServer keyValue) { public ReadResultSecurityStore createAndValidateCredentialsSecurityInfo(String endpoint, LwM2mTransportHandlerUtil.LwM2mTypeServer keyValue) {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1]; final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1];
contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endpoint).build(), context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endpoint).build(),
new TransportServiceCallback<>() { new TransportServiceCallback<>() {
@Override @Override
public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) { public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
String credentialsBody = msg.getCredentialsBody(); String credentialsBody = msg.getCredentialsBody();
resultSecurityStore[0] = createSecurityInfo(endpoint, credentialsBody, keyValue); resultSecurityStore[0] = createSecurityInfo(endpoint, credentialsBody, keyValue);
resultSecurityStore[0].setMsg(msg); resultSecurityStore[0].setMsg(msg);
Optional<DeviceProfile> deviceProfileOpt = LwM2mTransportHandler.decode(msg.getProfileBody().toByteArray()); Optional<DeviceProfile> deviceProfileOpt = LwM2mTransportHandlerUtil.decode(msg.getProfileBody().toByteArray());
deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile)); deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile));
latch.countDown(); latch.countDown();
} }
@ -81,7 +82,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
} }
}); });
try { try {
latch.await(contextS.getLwM2MTransportConfigServer().getTimeout(), TimeUnit.MILLISECONDS); latch.await(config.getTimeout(), TimeUnit.MILLISECONDS);
} catch (InterruptedException e) { } catch (InterruptedException e) {
log.error("Failed to await credentials!", e); log.error("Failed to await credentials!", e);
} }
@ -95,9 +96,9 @@ public class LwM2mCredentialsSecurityInfoValidator {
* @param keyValue - * @param keyValue -
* @return SecurityInfo * @return SecurityInfo
*/ */
private ReadResultSecurityStore createSecurityInfo(String endPoint, String jsonStr, LwM2mTransportHandler.LwM2mTypeServer keyValue) { private ReadResultSecurityStore createSecurityInfo(String endPoint, String jsonStr, LwM2mTransportHandlerUtil.LwM2mTypeServer keyValue) {
ReadResultSecurityStore result = new ReadResultSecurityStore(); ReadResultSecurityStore result = new ReadResultSecurityStore();
JsonObject objectMsg = LwM2mTransportHandler.validateJson(jsonStr); JsonObject objectMsg = LwM2mTransportHandlerUtil.validateJson(jsonStr);
if (objectMsg != null && !objectMsg.isJsonNull()) { if (objectMsg != null && !objectMsg.isJsonNull()) {
JsonObject object = (objectMsg.has(keyValue.type) && !objectMsg.get(keyValue.type).isJsonNull()) ? objectMsg.get(keyValue.type).getAsJsonObject() : null; JsonObject object = (objectMsg.has(keyValue.type) && !objectMsg.get(keyValue.type).isJsonNull()) ? objectMsg.get(keyValue.type).getAsJsonObject() : null;
/** /**
@ -108,7 +109,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
&& objectMsg.get("client").getAsJsonObject().get("endpoint").isJsonPrimitive()) ? objectMsg.get("client").getAsJsonObject().get("endpoint").getAsString() : null; && objectMsg.get("client").getAsJsonObject().get("endpoint").isJsonPrimitive()) ? objectMsg.get("client").getAsJsonObject().get("endpoint").getAsString() : null;
endPoint = (endPointPsk == null || endPointPsk.isEmpty()) ? endPoint : endPointPsk; endPoint = (endPointPsk == null || endPointPsk.isEmpty()) ? endPoint : endPointPsk;
if (object != null && !object.isJsonNull()) { if (object != null && !object.isJsonNull()) {
if (keyValue.equals(LwM2mTransportHandler.LwM2mTypeServer.BOOTSTRAP)) { if (keyValue.equals(LwM2mTransportHandlerUtil.LwM2mTypeServer.BOOTSTRAP)) {
result.setBootstrapJsonCredential(object); result.setBootstrapJsonCredential(object);
result.setEndPoint(endPoint); result.setEndPoint(endPoint);
result.setSecurityMode(LwM2MSecurityMode.fromSecurityMode(object.get("bootstrapServer").getAsJsonObject().get("securityMode").getAsString().toLowerCase()).code); result.setSecurityMode(LwM2MSecurityMode.fromSecurityMode(object.get("bootstrapServer").getAsJsonObject().get("securityMode").getAsString().toLowerCase()).code);

395
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java

@ -38,10 +38,15 @@ import org.eclipse.leshan.server.registration.Registration;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cache.firmware.FirmwareDataCache;
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.firmware.FirmwareKey;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.firmware.FirmwareUtil;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
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;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
@ -49,6 +54,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotif
import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
@ -77,34 +83,37 @@ import java.util.stream.Collectors;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST;
import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.CLIENT_NOT_AUTHORIZED;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.DEVICE_ATTRIBUTES_REQUEST;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.FR_OBJECT_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.FR_PATH_RESOURCE_VER_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LWM2M_STRATEGY_2; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_VALUE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.DISCOVER; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LWM2M_STRATEGY_2;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.DISCOVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_CANCEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.EXECUTE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_READ_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.READ; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_CANCEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.WRITE_ATTRIBUTES; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.READ;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.WRITE_UPDATE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.WRITE_ATTRIBUTES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.SERVICE_CHANNEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.WRITE_REPLACE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertJsonArrayToSet; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.WRITE_UPDATE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromIdVerToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.SERVICE_CHANNEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertJsonArrayToSet;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getAckCallback; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.validateObjectVerFromKey; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromObjectIdToIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.getAckCallback;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.validateObjectVerFromKey;
@Slf4j @Slf4j
@Service @Service
@TbLwM2mTransportComponent @TbLwM2mTransportComponent
public class LwM2mTransportServiceImpl implements LwM2mTransportService { public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler {
private ExecutorService executorRegistered; private ExecutorService executorRegistered;
private ExecutorService executorUpdateRegistered; private ExecutorService executorUpdateRegistered;
@ -112,31 +121,35 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
private LwM2mValueConverterImpl converter; private LwM2mValueConverterImpl converter;
private final TransportService transportService; private final TransportService transportService;
private final LwM2mTransportContext context;
public final LwM2mTransportContextServer lwM2mTransportContextServer; private final LwM2MTransportServerConfig config;
private final FirmwareDataCache firmwareDataCache;
private final LwM2mTransportServerHelper helper;
private final LwM2mClientContext lwM2mClientContext; private final LwM2mClientContext lwM2mClientContext;
private final LeshanServer leshanServer;
private final LwM2mTransportRequest lwM2mTransportRequest; private final LwM2mTransportRequest lwM2mTransportRequest;
public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer, LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, @Lazy LwM2mTransportRequest lwM2mTransportRequest) { public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper,
LwM2mClientContext lwM2mClientContext,
@Lazy LwM2mTransportRequest lwM2mTransportRequest,
FirmwareDataCache firmwareDataCache,
LwM2mTransportContext context) {
this.transportService = transportService; this.transportService = transportService;
this.lwM2mTransportContextServer = lwM2mTransportContextServer; this.config = config;
this.helper = helper;
this.lwM2mClientContext = lwM2mClientContext; this.lwM2mClientContext = lwM2mClientContext;
this.leshanServer = leshanServer;
this.lwM2mTransportRequest = lwM2mTransportRequest; this.lwM2mTransportRequest = lwM2mTransportRequest;
this.firmwareDataCache = firmwareDataCache;
this.context = context;
} }
@PostConstruct @PostConstruct
public void init() { public void init() {
this.lwM2mTransportContextServer.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) lwM2mTransportContextServer.getLwM2MTransportConfigServer().getSessionReportTimeout()), lwM2mTransportContextServer.getLwM2MTransportConfigServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) config.getSessionReportTimeout()), config.getSessionReportTimeout(), TimeUnit.MILLISECONDS);
this.executorRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getRegisteredPoolSize(), this.executorRegistered = Executors.newFixedThreadPool(this.config.getRegisteredPoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL))); new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL)));
this.executorUpdateRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getUpdateRegisteredPoolSize(), this.executorUpdateRegistered = Executors.newFixedThreadPool(this.config.getUpdateRegisteredPoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL))); new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL)));
this.executorUnRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getUnRegisteredPoolSize(), this.executorUnRegistered = Executors.newFixedThreadPool(this.config.getUnRegisteredPoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL))); new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL)));
this.converter = LwM2mValueConverterImpl.getInstance(); this.converter = LwM2mValueConverterImpl.getInstance();
} }
@ -168,8 +181,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null); transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null);
this.getInfoFirmwareUpdate(lwM2MClient);
this.initLwM2mFromClientValue(registration, lwM2MClient); this.initLwM2mFromClientValue(registration, lwM2MClient);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration); this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId());
} else { } else {
log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
} }
@ -224,7 +238,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
executorUnRegistered.submit(() -> { executorUnRegistered.submit(() -> {
try { try {
this.setCancelObservations(registration); this.setCancelObservations(registration);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
this.closeClientSession(registration); this.closeClientSession(registration);
} catch (Throwable t) { } catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
@ -257,7 +271,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
@Override @Override
public void onSleepingDev(Registration registration) { public void onSleepingDev(Registration registration) {
log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration); this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration.getId());
//TODO: associate endpointId with device information. //TODO: associate endpointId with device information.
} }
@ -265,10 +279,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
@Override @Override
public void setCancelObservations(Registration registration) { public void setCancelObservations(Registration registration) {
if (registration != null) { if (registration != null) {
Set<Observation> observations = leshanServer.getObservationService().getObservations(registration); Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
observations.forEach(observation -> lwM2mTransportRequest.sendAllRequest(registration, observations.forEach(observation -> lwM2mTransportRequest.sendAllRequest(registration,
convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), OBSERVE_CANCEL, convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), OBSERVE_CANCEL,
null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); null, null, this.config.getTimeout(), null));
} }
} }
@ -280,7 +294,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* @param response - observe * @param response - observe
*/ */
@Override @Override
public void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) { public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) {
if (response.getContent() != null) { if (response.getContent() != null) {
if (response.getContent() instanceof LwM2mObject) { if (response.getContent() instanceof LwM2mObject) {
LwM2mObject lwM2mObject = (LwM2mObject) response.getContent(); LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
@ -304,22 +318,29 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
/** /**
* Update - send request in change value resources in Client * Update - send request in change value resources in Client
* Path to resources from profile equal keyName or from ModelObject equal name * 1. FirmwareUpdate:
* Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase) * - If msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0
* Delete - nothing * * 2. Shared Other AttributeUpdate
* -- Path to resources from profile equal keyName or from ModelObject equal name
* -- Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
* 3. Delete - nothing
* *
* @param msg - * @param msg -
*/ */
@Override @Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
if (msg.getSharedUpdatedCount() > 0) { if (msg.getSharedUpdatedCount() > 0) {
msg.getSharedUpdatedList().forEach(tsKvProto -> { msg.getSharedUpdatedList().forEach(tsKvProto -> {
String pathName = tsKvProto.getKv().getKey(); String pathName = tsKvProto.getKv().getKey();
String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); Object valueNew = this.helper.getValueFromKvProto(tsKvProto.getKv());
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); //TODO: react on change of the firmware name.
if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
this.getInfoFirmwareUpdate(lwM2MClient);
}
if (pathIdVer != null) { if (pathIdVer != null) {
ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config
.getModelProvider()); .getModelProvider());
if (resourceModel != null && resourceModel.operations.isWritable()) { if (resourceModel != null && resourceModel.operations.isWritable()) {
this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), valueNew, pathIdVer); this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), valueNew, pathIdVer);
@ -327,19 +348,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew);
String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
LOG_LW2M_ERROR, pathIdVer, valueNew); LOG_LW2M_ERROR, pathIdVer, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
} }
} else { } else {
log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew);
String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
LOG_LW2M_ERROR, pathName, valueNew); LOG_LW2M_ERROR, pathName, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
} }
}); });
} else if (msg.getSharedDeletedCount() > 0) { } else if (msg.getSharedDeletedCount() > 0) {
msg.getSharedUpdatedList().forEach(tsKvProto -> {
String pathName = tsKvProto.getKv().getKey();
Object valueNew = this.helper.getValueFromKvProto(tsKvProto.getKv());
if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) {
lwM2MClient.getFrUpdate().setCurrentFwVersion((String) valueNew);
}
});
log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
} }
} }
/** /**
@ -377,7 +405,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
@Override @Override
public void onResourceUpdate(Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) { public void onResourceUpdate(Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) {
String idVer = resourceUpdateMsgOpt.get().getResourceKey(); String idVer = resourceUpdateMsgOpt.get().getResourceKey();
lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.updateResourceModel(idVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider())); lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.updateResourceModel(idVer, this.config.getModelProvider()));
} }
/** /**
@ -386,7 +414,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
@Override @Override
public void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt) { public void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt) {
String pathIdVer = resourceDeleteMsgOpt.get().getResourceKey(); String pathIdVer = resourceDeleteMsgOpt.get().getResourceKey();
lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.deleteResources(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider())); lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.deleteResources(pathIdVer, this.config.getModelProvider()));
} }
@Override @Override
@ -402,7 +430,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
} else { } else {
lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), lwm2mClientRpcRequest.getContentFormatName(), lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), lwm2mClientRpcRequest.getContentFormatName(),
lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(),
this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), lwm2mClientRpcRequest); this.config.getTimeout(), lwm2mClientRpcRequest);
} }
} catch (Exception e) { } catch (Exception e) {
if (lwm2mClientRpcRequest == null) { if (lwm2mClientRpcRequest == null) {
@ -430,7 +458,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
lwm2mClientRpcRequest.setRequestId(toDeviceRequest.getRequestId()); lwm2mClientRpcRequest.setRequestId(toDeviceRequest.getRequestId());
lwm2mClientRpcRequest.setSessionInfo(sessionInfo); lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
lwm2mClientRpcRequest.setValidTypeOper(toDeviceRequest.getMethodName()); lwm2mClientRpcRequest.setValidTypeOper(toDeviceRequest.getMethodName());
JsonObject rpcRequest = LwM2mTransportHandler.validateJson(toDeviceRequest.getParams()); JsonObject rpcRequest = LwM2mTransportHandlerUtil.validateJson(toDeviceRequest.getParams());
if (rpcRequest != null) { if (rpcRequest != null) {
if (rpcRequest.has(lwm2mClientRpcRequest.keyNameKey)) { if (rpcRequest.has(lwm2mClientRpcRequest.keyNameKey)) {
String targetIdVer = this.getPresentPathIntoProfile(sessionInfo, String targetIdVer = this.getPresentPathIntoProfile(sessionInfo,
@ -455,23 +483,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
} }
if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) { if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) {
lwm2mClientRpcRequest.setParams(new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey) lwm2mClientRpcRequest.setParams(new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey)
.getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() { .getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {
}.getType())); }.getType()));
} }
lwm2mClientRpcRequest.setSessionInfo(sessionInfo); lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) { if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) {
lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " + lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " +
lwm2mClientRpcRequest.keyNameKey + " is null or bad format"); lwm2mClientRpcRequest.keyNameKey + " is null or bad format");
} } else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
|| WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper()) || WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper())
&& lwm2mClientRpcRequest.getTargetIdVer() !=null && lwm2mClientRpcRequest.getTargetIdVer() != null
&& !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource() && !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource()
|| new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) { || new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) {
lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey
+ ". Only Resource or ResourceInstance can be this operation"); + ". Only Resource or ResourceInstance can be this operation");
} } else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()) {
else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()){
lwm2mClientRpcRequest.setErrorMsg("Procedures In Development..."); lwm2mClientRpcRequest.setErrorMsg("Procedures In Development...");
} }
} else { } else {
@ -483,23 +509,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
return lwm2mClientRpcRequest; return lwm2mClientRpcRequest;
} }
public void sentRpcRequest (Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) {
rpcRequest.setResponseCode(requestCode); rpcRequest.setResponseCode(requestCode);
if (LOG_LW2M_ERROR.equals(typeMsg)) { if (LOG_LW2M_ERROR.equals(typeMsg)) {
rpcRequest.setInfoMsg(null); rpcRequest.setInfoMsg(null);
rpcRequest.setValueMsg(null); rpcRequest.setValueMsg(null);
if (rpcRequest.getErrorMsg() == null) { if (rpcRequest.getErrorMsg() == null) {
msg = msg.isEmpty() ? null : msg; msg = msg.isEmpty() ? null : msg;
rpcRequest.setErrorMsg(msg); rpcRequest.setErrorMsg(msg);
} }
} else if (LOG_LW2M_INFO.equals(typeMsg)) { } else if (LOG_LW2M_INFO.equals(typeMsg)) {
if (rpcRequest.getInfoMsg() == null) { if (rpcRequest.getInfoMsg() == null) {
rpcRequest.setInfoMsg(msg); rpcRequest.setInfoMsg(msg);
} }
} else if (LOG_LW2M_VALUE.equals(typeMsg)) { } else if (LOG_LW2M_VALUE.equals(typeMsg)) {
if (rpcRequest.getValueMsg() == null) { if (rpcRequest.getValueMsg() == null) {
rpcRequest.setValueMsg(msg); rpcRequest.setValueMsg(msg);
} }
} }
this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo()); this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo());
} }
@ -521,7 +547,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
@Override @Override
public void doTrigger(Registration registration, String path) { public void doTrigger(Registration registration, String path) {
lwM2mTransportRequest.sendAllRequest(registration, path, EXECUTE, lwM2mTransportRequest.sendAllRequest(registration, path, EXECUTE,
ContentFormat.TLV.getName(), null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); ContentFormat.TLV.getName(), null, this.config.getTimeout(), null);
} }
/** /**
@ -554,9 +580,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* *
* @param registration - * @param registration -
*/ */
protected void onAwakeDev(Registration registration) { @Override
public void onAwakeDev(Registration registration) {
log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration); this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId());
//TODO: associate endpointId with device information. //TODO: associate endpointId with device information.
} }
@ -583,12 +610,16 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
/** /**
* @param logMsg - text msg * @param logMsg - text msg
* @param registration - Id of Registration LwM2M Client * @param registrationId - Id of Registration LwM2M Client
*/ */
public void sendLogsToThingsboard(String logMsg, Registration registration) { @Override
SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); public void sendLogsToThingsboard(String logMsg, String registrationId) {
SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId);
if (logMsg != null && sessionInfo != null) { if (logMsg != null && sessionInfo != null) {
this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo); if(logMsg.length() > 1024){
logMsg = logMsg.substring(0, 1024);
}
this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvLogyToThingsboard(logMsg), sessionInfo);
} }
} }
@ -608,11 +639,11 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration); LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration);
Set<String> clientObjects = lwM2mClientContext.getSupportedIdVerInClient(registration); Set<String> clientObjects = lwM2mClientContext.getSupportedIdVerInClient(registration);
if (clientObjects != null && clientObjects.size() > 0) { if (clientObjects != null && clientObjects.size() > 0) {
if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { if (LWM2M_STRATEGY_2 == LwM2mTransportHandlerUtil.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
// #2 // #2
lwM2MClient.getPendingRequests().addAll(clientObjects); lwM2MClient.getPendingReadRequests().addAll(clientObjects);
clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(), clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(),
null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); null, this.config.getTimeout(), null));
} }
// #1 // #1
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, READ, clientObjects); this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, READ, clientObjects);
@ -652,6 +683,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* Sending observe value of resources to thingsboard * Sending observe value of resources to thingsboard
* #1 Return old Value Resource from LwM2MClient * #1 Return old Value Resource from LwM2MClient
* #2 Update new Resources (replace old Resource Value on new Resource Value) * #2 Update new Resources (replace old Resource Value on new Resource Value)
* #3 If fr_update -> UpdateFirmware
* #4 updateAttrTelemetry
* *
* @param registration - Registration LwM2M Client * @param registration - Registration LwM2M Client
* @param lwM2mResource - LwM2mSingleResource response.getContent() * @param lwM2mResource - LwM2mSingleResource response.getContent()
@ -659,8 +692,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
*/ */
private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) { private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config
.getModelProvider())) { .getModelProvider())) {
if (FR_PATH_RESOURCE_VER_ID.equals(convertPathFromIdVerToObjectId(path)) &&
lwM2MClient.getFrUpdate().getCurrentFwVersion() != null
&& !lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())
&& lwM2MClient.isUpdateFw()) {
/** version != null
* set setClient_fw_version = value
**/
lwM2MClient.setUpdateFw(false);
lwM2MClient.getFrUpdate().setClientFwVersion(lwM2mResource.getValue().toString());
log.warn("updateFirmwareClient3");
this.updateFirmwareClient(lwM2MClient);
}
Set<String> paths = new HashSet<>(); Set<String> paths = new HashSet<>();
paths.add(path); paths.add(path);
this.updateAttrTelemetry(registration, paths); this.updateAttrTelemetry(registration, paths);
@ -669,6 +715,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
} }
} }
/** /**
* send Attribute and Telemetry to Thingsboard * send Attribute and Telemetry to Thingsboard
* #1 - get AttrName/TelemetryName with value from LwM2MClient: * #1 - get AttrName/TelemetryName with value from LwM2MClient:
@ -684,10 +731,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
if (results != null && sessionInfo != null) { if (results != null && sessionInfo != null) {
if (results.getResultAttributes().size() > 0) { if (results.getResultAttributes().size() > 0) {
this.lwM2mTransportContextServer.sendParametersOnThingsboardAttribute(results.getResultAttributes(), sessionInfo); this.helper.sendParametersOnThingsboardAttribute(results.getResultAttributes(), sessionInfo);
} }
if (results.getResultTelemetries().size() > 0) { if (results.getResultTelemetries().size() > 0) {
this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(results.getResultTelemetries(), sessionInfo); this.helper.sendParametersOnThingsboardTelemetry(results.getResultTelemetries(), sessionInfo);
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -723,22 +770,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()); params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
result = params.keySet(); result = params.keySet();
} }
if (!result.isEmpty()) { if (result != null && !result.isEmpty()) {
// #1 // #1
Set<String> pathSend = result.stream().filter(target -> { Set<String> pathSend = result.stream().filter(target -> {
return target.split(LWM2M_SEPARATOR_PATH).length < 3 ? return target.split(LWM2M_SEPARATOR_PATH).length < 3 ?
clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) : clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) :
clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]); clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]);
} }
) ).collect(Collectors.toUnmodifiableSet());
.collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) { if (!pathSend.isEmpty()) {
lwM2MClient.getPendingRequests().addAll(pathSend); lwM2MClient.getPendingReadRequests().addAll(pathSend);
ConcurrentHashMap<String, Object> finalParams = params; ConcurrentHashMap<String, Object> finalParams = params;
pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(), pathSend.forEach(target -> {
finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),
finalParams != null ? finalParams.get(target) : null, this.config.getTimeout(), null);
});
if (OBSERVE.equals(typeOper)) { if (OBSERVE.equals(typeOper)) {
lwM2MClient.initValue(this, null); lwM2MClient.initReadValue(this, null);
} }
} }
} }
@ -820,7 +868,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
LwM2mResource resourceValue = lwM2MClient != null ? getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) : null; LwM2mResource resourceValue = lwM2MClient != null ? getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) : null;
if (resourceValue != null) { if (resourceValue != null) {
ResourceModel.Type currentType = resourceValue.getType(); ResourceModel.Type currentType = resourceValue.getType();
ResourceModel.Type expectedType = this.lwM2mTransportContextServer.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
Object valueKvProto = null; Object valueKvProto = null;
if (resourceValue.isMultiInstances()) { if (resourceValue.isMultiInstances()) {
valueKvProto = new JsonObject(); valueKvProto = new JsonObject();
@ -837,7 +885,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
valueKvProto = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, valueKvProto = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
} }
return valueKvProto != null ? this.lwM2mTransportContextServer.getKvAttrTelemetryToThingsboard(currentType, resourceName, valueKvProto, resourceValue.isMultiInstances()) : null; return valueKvProto != null ? this.helper.getKvAttrTelemetryToThingsboard(currentType, resourceName, valueKvProto, resourceValue.isMultiInstances()) : null;
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to add parameters.", e); log.error("Failed to add parameters.", e);
@ -856,7 +904,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) { private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) {
LwM2mResource resourceValue = this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); LwM2mResource resourceValue = this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer);
ResourceModel.Type currentType = resourceValue.getType(); ResourceModel.Type currentType = resourceValue.getType();
ResourceModel.Type expectedType = this.lwM2mTransportContextServer.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
} }
@ -969,7 +1017,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
// update value in Resources // update value in Resources
registrationIds.forEach(registrationId -> { registrationIds.forEach(registrationId -> {
Registration registration = lwM2mClientContext.getRegistration(registrationId); Registration registration = lwM2mClientContext.getRegistration(registrationId);
this.readResourceValueObserve(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ); this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
// send attr/telemetry to tingsboard for new path // send attr/telemetry to tingsboard for new path
this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd()); this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd());
}); });
@ -998,12 +1046,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
registrationIds.forEach(registrationId -> { registrationIds.forEach(registrationId -> {
Registration registration = lwM2mClientContext.getRegistration(registrationId); Registration registration = lwM2mClientContext.getRegistration(registrationId);
if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) { if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) {
this.readResourceValueObserve(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE); this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
} }
// 5.3 del // 5.3 del
// send Request cancel observe to Client // send Request cancel observe to Client
if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) { if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) {
this.cancelObserveIsValue(registration, postObserveAnalyzer.getPathPostParametersDel()); this.cancelObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersDel());
} }
}); });
} }
@ -1043,16 +1091,16 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* @param registration - Registration LwM2M Client * @param registration - Registration LwM2M Client
* @param targets - path Resources == [ "/2/0/0", "/2/0/1"] * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
*/ */
private void readResourceValueObserve(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) { private void readObserveFromProfile(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) {
targets.forEach(target -> { targets.forEach(target -> {
LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target)); LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target));
if (pathIds.isResource()) { if (pathIds.isResource()) {
if (READ.equals(typeOper)) { if (READ.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, lwM2mTransportRequest.sendAllRequest(registration, target, typeOper,
ContentFormat.TLV.getName(), null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); ContentFormat.TLV.getName(), null, this.config.getTimeout(), null);
} else if (OBSERVE.equals(typeOper)) { } else if (OBSERVE.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, lwM2mTransportRequest.sendAllRequest(registration, target, typeOper,
null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); null, null, this.config.getTimeout(), null);
} }
} }
}); });
@ -1108,7 +1156,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
if (!pathSend.isEmpty()) { if (!pathSend.isEmpty()) {
ConcurrentHashMap<String, Object> finalParams = lwm2mAttributesNew; ConcurrentHashMap<String, Object> finalParams = lwm2mAttributesNew;
pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(),
finalParams.get(target), this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); finalParams.get(target), this.config.getTimeout(), null));
} }
}); });
} }
@ -1125,7 +1173,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
params.clear(); params.clear();
params.put(OBJECT_VERSION, ""); params.put(OBJECT_VERSION, "");
lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(),
params, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); params, this.config.getTimeout(), null);
}); });
} }
}); });
@ -1133,12 +1181,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
} }
private void cancelObserveIsValue(Registration registration, Set<String> paramAnallyzer) { private void cancelObserveFromProfile(Registration registration, Set<String> paramAnallyzer) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
paramAnallyzer.forEach(pathIdVer -> { paramAnallyzer.forEach(pathIdVer -> {
if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) { if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) {
lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, OBSERVE_CANCEL, null, lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, OBSERVE_CANCEL, null,
null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); null, this.config.getTimeout(), null);
} }
} }
); );
@ -1148,12 +1196,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) { if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) {
lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, WRITE_REPLACE, lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, WRITE_REPLACE,
ContentFormat.TLV.getName(), valueNew, ContentFormat.TLV.getName(), valueNew,
this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); this.config.getTimeout(), null);
} else { } else {
log.error("Failed update resource [{}] [{}]", path, valueNew); log.error("Failed update resource [{}] [{}]", path, valueNew);
String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad", String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad",
LOG_LW2M_ERROR, path, valueNew); LOG_LW2M_ERROR, path, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
log.info("Failed update resource [{}] [{}]", path, valueNew); log.info("Failed update resource [{}] [{}]", path, valueNew);
} }
} }
@ -1182,8 +1230,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
} }
/** /**
* Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values * 1. FirmwareUpdate:
* #1 Get path resource by result attributesResponse * - msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0
* 2. Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
* - Get path resource by result attributesResponse
* *
* @param attributesResponse - * @param attributesResponse -
* @param sessionInfo - * @param sessionInfo -
@ -1191,6 +1241,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
try { try {
List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList(); List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList();
this.updateAttriuteFromThingsboard(tsKvProtos, sessionInfo); this.updateAttriuteFromThingsboard(tsKvProtos, sessionInfo);
} catch (Exception e) { } catch (Exception e) {
log.error(String.valueOf(e)); log.error(String.valueOf(e));
@ -1223,7 +1274,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
// #2.1 // #2.1
lwM2MClient.getDelayedRequests().forEach((pathIdVer, tsKvProto) -> { lwM2MClient.getDelayedRequests().forEach((pathIdVer, tsKvProto) -> {
this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer),
this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()), pathIdVer); this.helper.getValueFromKvProto(tsKvProto.getKv()), pathIdVer);
}); });
} }
@ -1240,7 +1291,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
return null; return null;
} else { } else {
return SessionInfoProto.newBuilder() return SessionInfoProto.newBuilder()
.setNodeId(this.lwM2mTransportContextServer.getNodeId()) .setNodeId(this.context.getNodeId())
.setSessionIdMSB(lwM2MClient.getSessionId().getMostSignificantBits()) .setSessionIdMSB(lwM2MClient.getSessionId().getMostSignificantBits())
.setSessionIdLSB(lwM2MClient.getSessionId().getLeastSignificantBits()) .setSessionIdLSB(lwM2MClient.getSessionId().getLeastSignificantBits())
.setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB()) .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
@ -1274,7 +1325,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
*/ */
private SessionInfoProto getValidateSessionInfo(String registrationId) { private SessionInfoProto getValidateSessionInfo(String registrationId) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(null, registrationId); LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(null, registrationId);
return getNewSessionInfoProto(lwM2MClient); return lwM2MClient != null ? this.getNewSessionInfoProto(lwM2MClient) : null;
} }
/** /**
@ -1293,28 +1344,88 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
} }
/** /**
* !!! sharedAttr === profileAttr !!! * #1. !!! sharedAttr === profileAttr !!!
* If there is a difference in values between the current resource values and the shared attribute values * - If there is a difference in values between the current resource values and the shared attribute values
* when the client connects to the server * - when the client connects to the server
* #1 get attributes name from profile include name resources in ModelObject if resource isWritable * #1.1 get attributes name from profile include name resources in ModelObject if resource isWritable
* #2.1 #1 size > 0 => send Request getAttributes to thingsboard * #1.2 #1 size > 0 => send Request getAttributes to thingsboard
* #2. FirmwareAttribute subscribe:
* *
* @param lwM2MClient - LwM2M Client * @param lwM2MClient - LwM2M Client
*/ */
public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) { public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) {
SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration()); SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
if (sessionInfo != null) { if (sessionInfo != null) {
//#1.1 + #1.2 //#1.1
List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient); ConcurrentMap<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient);
if (attrSharedNames.size() > 0) { if (keyNamesMap.values().size() > 0) {
//#2.1
try { try {
TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, attrSharedNames); //#1.2
TransportProtos.GetAttributeRequestMsg getAttributeMsg = helper.getAdaptor().convertToGetAttributes(null, keyNamesMap.values());
transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
} catch (AdaptorException e) { } catch (AdaptorException e) {
log.warn("Failed to decode get attributes request", e); log.warn("Failed to decode get attributes request", e);
} }
} }
}
}
public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) {
SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
if (sessionInfo != null) {
TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setType(FirmwareType.FIRMWARE.name())
.build();
transportService.process(sessionInfo, getFirmwareRequestMsg,
new TransportServiceCallback<>() {
@Override
public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) {
if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
lwM2MClient.getFrUpdate().setCurrentFwVersion(response.getVersion());
lwM2MClient.getFrUpdate().setCurrentFwId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
lwM2MClient.setUpdateFw(true);
readRequestToClientFirmwareVer(lwM2MClient.getRegistration());
} else {
log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
}
}
@Override
public void onError(Throwable e) {
log.trace("Failed to process credentials ", e);
}
});
}
}
/**
* @param registration
*/
public void readRequestToClientFirmwareVer(Registration registration) {
String pathIdVer = convertPathFromObjectIdToIdVer(FR_PATH_RESOURCE_VER_ID, registration);
lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, READ, ContentFormat.TLV.getName(),
null, config.getTimeout(), null);
}
/**
*
* @param lwM2MClient -
*/
public void updateFirmwareClient(LwM2mClient lwM2MClient) {
if (!lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())) {
int chunkSize = 0;
int chunk = 0;
byte[] firmwareChunk = firmwareDataCache.get(lwM2MClient.getFrUpdate().getCurrentFwId().toString(), chunkSize, chunk);
String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(FR_OBJECT_ID);
String targetIdVer = LWM2M_SEPARATOR_PATH + FR_OBJECT_ID + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0;
lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(),
firmwareChunk, config.getTimeout(), null);
log.warn("updateFirmwareClient [{}] [{}]", lwM2MClient.getFrUpdate().getCurrentFwVersion(), lwM2MClient.getFrUpdate().getClientFwVersion());
} }
} }
@ -1326,27 +1437,16 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* @param lwM2MClient - * @param lwM2MClient -
* @return ArrayList keyNames from profile profileAttr && IsWritable * @return ArrayList keyNames from profile profileAttr && IsWritable
*/ */
private List<String> getNamesAttrFromProfileIsWritable(LwM2mClient lwM2MClient) { private ConcurrentMap<String, String> getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) {
LwM2mClientProfile profile = lwM2mClientContext.getProfile(lwM2MClient.getProfileId()); LwM2mClientProfile profile = lwM2mClientContext.getProfile(lwM2MClient.getProfileId());
Set<String> attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), return new Gson().fromJson(profile.getPostKeyNameProfile().toString(),
new TypeToken<HashSet<String>>() {
}.getType());
ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(),
new TypeToken<ConcurrentHashMap<String, String>>() { new TypeToken<ConcurrentHashMap<String, String>>() {
}.getType()); }.getType());
ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()
.stream()
.filter(e -> (attrSet.contains(e.getKey()) && validateResourceInModel(lwM2MClient, e.getKey(), true)))
.collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();
namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));
return new ArrayList<>(namesIsWritable);
} }
private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) {
ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config
.getModelProvider()); .getModelProvider());
Integer objectId = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)).getObjectId(); Integer objectId = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)).getObjectId();
String objectVer = validateObjectVerFromKey(pathIdVer); String objectVer = validateObjectVerFromKey(pathIdVer);
@ -1355,9 +1455,4 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
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";
}
} }

160
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java

@ -15,7 +15,10 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server; package org.thingsboard.server.transport.lwm2m.server;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.core.network.stack.BlockwiseLayer;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
@ -27,12 +30,16 @@ import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.eclipse.leshan.server.security.DefaultAuthorizer; import org.eclipse.leshan.server.security.DefaultAuthorizer;
import org.eclipse.leshan.server.security.EditableSecurityStore; import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.SecurityChecker; import org.eclipse.leshan.server.security.SecurityChecker;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.AlgorithmParameters; import java.security.AlgorithmParameters;
import java.security.KeyFactory; import java.security.KeyFactory;
@ -57,61 +64,86 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig; import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
@Slf4j @Slf4j
@Component @Component
@TbLwM2mTransportComponent @TbLwM2mTransportComponent
public class LwM2mTransportServerConfiguration { @RequiredArgsConstructor
public class DefaultLwM2mTransportService implements LwM2MTransportService {
private PublicKey publicKey; private PublicKey publicKey;
private PrivateKey privateKey; private PrivateKey privateKey;
private boolean pskMode = false; private boolean pskMode = false;
private final LwM2mTransportContextServer context;
private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config;
private final LwM2mTransportServerHelper helper;
private final LwM2mTransportMsgHandler handler;
private final CaliforniumRegistrationStore registrationStore; private final CaliforniumRegistrationStore registrationStore;
private final EditableSecurityStore securityStore; private final EditableSecurityStore securityStore;
private final LwM2mClientContext lwM2mClientContext; private final LwM2mClientContext lwM2mClientContext;
public LwM2mTransportServerConfiguration(LwM2mTransportContextServer context, CaliforniumRegistrationStore registrationStore, EditableSecurityStore securityStore, LwM2mClientContext lwM2mClientContext) { private LeshanServer server;
this.context = context;
this.registrationStore = registrationStore; @PostConstruct
this.securityStore = securityStore; public void init() {
this.lwM2mClientContext = lwM2mClientContext; if (config.getEnableGenNewKeyPskRpk()) {
new LWM2MGenerationPSkRPkECC();
}
this.server = getLhServer(config.getPort(), config.getSecurePort());
this.startLhServer();
this.context.setServer(server);
}
private void startLhServer() {
log.info("Starting LwM2M transport Server...");
this.server.start();
LwM2mServerListener lhServerCertListener = new LwM2mServerListener(handler);
this.server.getRegistrationService().addListener(lhServerCertListener.registrationListener);
this.server.getPresenceService().addListener(lhServerCertListener.presenceListener);
this.server.getObservationService().addListener(lhServerCertListener.observationListener);
} }
@Bean @PreDestroy
public LeshanServer getLeshanServer() { public void shutdown() {
log.info("Starting LwM2M transport Server... PostConstruct"); log.info("Stopping LwM2M transport Server!");
return this.getLhServer(this.context.getLwM2MTransportConfigServer().getServerPortNoSec(), this.context.getLwM2MTransportConfigServer().getServerPortSecurity()); server.destroy();
log.info("LwM2M transport Server stopped!");
} }
private LeshanServer getLhServer(Integer serverPortNoSec, Integer serverSecurePort) { private LeshanServer getLhServer(Integer serverPortNoSec, Integer serverSecurePort) {
LeshanServerBuilder builder = new LeshanServerBuilder(); LeshanServerBuilder builder = new LeshanServerBuilder();
builder.setLocalAddress(this.context.getLwM2MTransportConfigServer().getServerHost(), serverPortNoSec); builder.setLocalAddress(config.getHost(), serverPortNoSec);
builder.setLocalSecureAddress(this.context.getLwM2MTransportConfigServer().getServerHostSecurity(), serverSecurePort); builder.setLocalSecureAddress(config.getSecureHost(), serverSecurePort);
builder.setDecoder(new DefaultLwM2mNodeDecoder()); builder.setDecoder(new DefaultLwM2mNodeDecoder());
/** Use a magic converter to support bad type send by the UI. */ /* Use a magic converter to support bad type send by the UI. */
builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
/** Create CoAP Config */
/* Create CoAP Config */
NetworkConfig networkConfig = getCoapConfig(serverPortNoSec, serverSecurePort);
BlockwiseLayer blockwiseLayer = new BlockwiseLayer(networkConfig);
builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort)); builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort));
/** Define model provider (Create Models )*/ /* Define model provider (Create Models )*/
LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.context); LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.helper, this.context);
this.context.getLwM2MTransportConfigServer().setModelProvider(modelProvider); config.setModelProvider(modelProvider);
builder.setObjectModelProvider(modelProvider); builder.setObjectModelProvider(modelProvider);
/** Create credentials */ /* Create credentials */
this.setServerWithCredentials(builder); this.setServerWithCredentials(builder);
/** Set securityStore with new registrationStore */ /* Set securityStore with new registrationStore */
builder.setSecurityStore(securityStore); builder.setSecurityStore(securityStore);
builder.setRegistrationStore(registrationStore); builder.setRegistrationStore(registrationStore);
/** Create DTLS Config */ /* Create DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups()); dtlsConfig.setServerOnly(true);
dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers()); dtlsConfig.setRecommendedSupportedGroupsOnly(config.isRecommendedSupportedGroups());
dtlsConfig.setRecommendedCipherSuitesOnly(config.isRecommendedCiphers());
if (this.pskMode) { if (this.pskMode) {
dtlsConfig.setSupportedCipherSuites( dtlsConfig.setSupportedCipherSuites(
TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CCM_8,
@ -124,27 +156,27 @@ public class LwM2mTransportServerConfiguration {
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256); TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256);
} }
/** Set DTLS Config */ /* Set DTLS Config */
builder.setDtlsConfig(dtlsConfig); builder.setDtlsConfig(dtlsConfig);
/** Create LWM2M server */ /* Create LWM2M server */
return builder.build(); return builder.build();
} }
private void setServerWithCredentials(LeshanServerBuilder builder) { private void setServerWithCredentials(LeshanServerBuilder builder) {
try { try {
if (this.context.getLwM2MTransportConfigServer().getKeyStoreValue() != null) { if (config.getKeyStoreValue() != null) {
if (this.setBuilderX509(builder)) { if (this.setBuilderX509(builder)) {
X509Certificate rootCAX509Cert = (X509Certificate) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportConfigServer().getRootAlias()); X509Certificate rootCAX509Cert = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getRootCertificateAlias());
if (rootCAX509Cert != null) { if (rootCAX509Cert != null) {
X509Certificate[] trustedCertificates = new X509Certificate[1]; X509Certificate[] trustedCertificates = new X509Certificate[1];
trustedCertificates[0] = rootCAX509Cert; trustedCertificates[0] = rootCAX509Cert;
builder.setTrustedCertificates(trustedCertificates); builder.setTrustedCertificates(trustedCertificates);
} else { } else {
/** by default trust all */ /* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]); builder.setTrustedCertificates(new X509Certificate[0]);
} }
/** Set securityStore with registrationStore*/ /* Set securityStore with registrationStore*/
builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() { builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() {
@Override @Override
protected boolean matchX509Identity(String endpoint, String receivedX509CommonName, protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,
@ -157,7 +189,7 @@ public class LwM2mTransportServerConfiguration {
this.infoPramsUri("RPK"); this.infoPramsUri("RPK");
this.infoParamsServerKey(this.publicKey, this.privateKey); this.infoParamsServerKey(this.publicKey, this.privateKey);
} else { } else {
/** by default trust all */ /* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]); builder.setTrustedCertificates(new X509Certificate[0]);
log.info("Unable to load X509 files for LWM2MServer"); log.info("Unable to load X509 files for LWM2MServer");
this.pskMode = true; this.pskMode = true;
@ -169,17 +201,11 @@ public class LwM2mTransportServerConfiguration {
} }
private boolean setBuilderX509(LeshanServerBuilder builder) { private boolean setBuilderX509(LeshanServerBuilder builder) {
/**
* For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
* For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
*/
try { try {
X509Certificate serverCertificate = (X509Certificate) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportConfigServer().getServerAlias()); X509Certificate serverCertificate = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getCertificateAlias());
PrivateKey privateKey = (PrivateKey) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getKey(this.context.getLwM2MTransportConfigServer().getServerAlias(), this.context.getLwM2MTransportConfigServer().getKeyStorePasswordServer() == null ? null : this.context.getLwM2MTransportConfigServer().getKeyStorePasswordServer().toCharArray()); PrivateKey privateKey = (PrivateKey) config.getKeyStoreValue().getKey(config.getCertificateAlias(), config.getKeyStorePassword() == null ? null : config.getKeyStorePassword().toCharArray());
PublicKey publicKey = serverCertificate.getPublicKey(); PublicKey publicKey = serverCertificate.getPublicKey();
if (serverCertificate != null && if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) {
privateKey != null && privateKey.getEncoded().length > 0 &&
publicKey != null && publicKey.getEncoded().length > 0) {
builder.setPublicKey(serverCertificate.getPublicKey()); builder.setPublicKey(serverCertificate.getPublicKey());
builder.setPrivateKey(privateKey); builder.setPrivateKey(privateKey);
builder.setCertificateChain(new X509Certificate[]{serverCertificate}); builder.setCertificateChain(new X509Certificate[]{serverCertificate});
@ -206,10 +232,12 @@ public class LwM2mTransportServerConfiguration {
} }
private void infoPramsUri(String mode) { private void infoPramsUri(String mode) {
log.info("Server uses [{}]: serverNoSecureURI : [{}], serverSecureURI : [{}]", LwM2MTransportServerConfig lwM2MTransportServerConfig = config;
mode, log.info("Server uses [{}]: serverNoSecureURI : [{}:{}], serverSecureURI : [{}:{}]", mode,
this.context.getLwM2MTransportConfigServer().getServerHost() + ":" + this.context.getLwM2MTransportConfigServer().getServerPortNoSec(), lwM2MTransportServerConfig.getHost(),
this.context.getLwM2MTransportConfigServer().getServerHostSecurity() + ":" + this.context.getLwM2MTransportConfigServer().getServerPortSecurity()); lwM2MTransportServerConfig.getPort(),
lwM2MTransportServerConfig.getSecureHost(),
lwM2MTransportServerConfig.getSecurePort());
} }
private boolean setServerRPK(LeshanServerBuilder builder) { private boolean setServerRPK(LeshanServerBuilder builder) {
@ -227,55 +255,42 @@ public class LwM2mTransportServerConfiguration {
return false; return false;
} }
/**
* From yml: server
* public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}"
* public_y: "${LWM2M_SERVER_PUBLIC_Y:30c9237e946a3a1692c1cafaa01a238a077f632c99371348337512363f28212b}"
* private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:274671fe40ce937b8a6352cf0a418e8a39e4bf0bb9bf74c910db953c20c73802}"
*/
private void generateKeyForRPK() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException { private void generateKeyForRPK() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException {
/** Get Elliptic Curve Parameter spec for secp256r1 */ /* Get Elliptic Curve Parameter spec for secp256r1 */
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1")); algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
if (this.context.getLwM2MTransportConfigServer().getServerPublicX() != null && LwM2MTransportServerConfig serverConfig = config;
!this.context.getLwM2MTransportConfigServer().getServerPublicX().isEmpty() && if (StringUtils.isNotEmpty(serverConfig.getPublicX()) && StringUtils.isNotEmpty(serverConfig.getPublicY())) {
this.context.getLwM2MTransportConfigServer().getServerPublicY() != null && byte[] publicX = Hex.decodeHex(serverConfig.getPublicX().toCharArray());
!this.context.getLwM2MTransportConfigServer().getServerPublicY().isEmpty()) { byte[] publicY = Hex.decodeHex(serverConfig.getPublicY().toCharArray());
/** Get point values */
byte[] publicX = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPublicX().toCharArray());
byte[] publicY = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPublicY().toCharArray());
/** Create key specs */
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec); parameterSpec);
/** Get keys */
this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
} }
if (this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded() != null && String privateEncodedKey = serverConfig.getPrivateEncoded();
!this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded().isEmpty()) { if (StringUtils.isNotEmpty(privateEncodedKey)) {
/** Get private key */ byte[] privateS = Hex.decodeHex(privateEncodedKey.toCharArray());
byte[] privateS = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded().toCharArray());
try { try {
this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS)); this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS));
} catch (InvalidKeySpecException ignore2) { } catch (InvalidKeySpecException ignore2) {
log.error("Invalid Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded()); log.error("Invalid Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", privateEncodedKey);
} }
} }
} }
private void infoParamsServerKey(PublicKey publicKey, PrivateKey privateKey) { private void infoParamsServerKey(PublicKey publicKey, PrivateKey privateKey) {
/** Get x coordinate */ /* Get x coordinate */
byte[] x = ((ECPublicKey) publicKey).getW().getAffineX().toByteArray(); byte[] x = ((ECPublicKey) publicKey).getW().getAffineX().toByteArray();
if (x[0] == 0) if (x[0] == 0)
x = Arrays.copyOfRange(x, 1, x.length); x = Arrays.copyOfRange(x, 1, x.length);
/** Get Y coordinate */ /* Get Y coordinate */
byte[] y = ((ECPublicKey) publicKey).getW().getAffineY().toByteArray(); byte[] y = ((ECPublicKey) publicKey).getW().getAffineY().toByteArray();
if (y[0] == 0) if (y[0] == 0)
y = Arrays.copyOfRange(y, 1, y.length); y = Arrays.copyOfRange(y, 1, y.length);
/** Get Curves params */ /* Get Curves params */
String params = ((ECPublicKey) publicKey).getParams().toString(); String params = ((ECPublicKey) publicKey).getParams().toString();
String privHex = Hex.encodeHexString(privateKey.getEncoded()); String privHex = Hex.encodeHexString(privateKey.getEncoded());
log.info(" \n- Public Key (Hex): [{}] \n" + log.info(" \n- Public Key (Hex): [{}] \n" +
@ -292,4 +307,9 @@ public class LwM2mTransportServerConfiguration {
params); params);
} }
@Override
public String getName() {
return "LWM2M";
}
} }

22
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java

@ -0,0 +1,22 @@
/**
* 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.lwm2m.server;
import org.thingsboard.server.common.data.TbTransportService;
public interface LwM2MTransportService extends TbTransportService {
}

99
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java

@ -0,0 +1,99 @@
/**
* 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.lwm2m.server;
import org.eclipse.californium.core.network.config.NetworkConfig;
public class LwM2mNetworkConfig {
public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) {
NetworkConfig coapConfig = new NetworkConfig();
coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec);
coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort);
/*
Example:Property for large packet:
#NetworkConfig config = new NetworkConfig();
#config.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE,32);
#config.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE,32);
#config.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE,2048);
#config.setInt(NetworkConfig.Keys.MAX_RETRANSMIT,3);
#config.setInt(NetworkConfig.Keys.MAX_TRANSMIT_WAIT,120000);
*/
/*
Property to indicate if the response should always include the Block2 option \
when client request early blockwise negociation but the response can be sent on one packet.
- value of false indicate that the server will respond without block2 option if no further blocks are required.
- value of true indicate that the server will response with block2 option event if no further blocks are required.
CoAP client will try to use block mode
or adapt the block size when receiving a 4.13 Entity too large response code
*/
coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
/*
Property to indicate if the response should always include the Block2 option \
when client request early blockwise negociation but the response can be sent on one packet.
- value of false indicate that the server will respond without block2 option if no further blocks are required.
- value of true indicate that the server will response with block2 option event if no further blocks are required.
*/
coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
coapConfig.setInt(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, 300000);
/*
!!! REQUEST_ENTITY_TOO_LARGE CODE=4.13
The maximum size of a resource body (in bytes) that will be accepted
as the payload of a POST/PUT or the response to a GET request in a
transparent> blockwise transfer.
This option serves as a safeguard against excessive memory
consumption when many resources contain large bodies that cannot be
transferred in a single CoAP message. This option has no impact on
*manually* managed blockwise transfers in which the blocks are handled individually.
Note that this option does not prevent local clients or resource
implementations from sending large bodies as part of a request or response to a peer.
The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192
A value of {@code 0} turns off transparent handling of blockwise transfers altogether.
*/
coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024);
/*
The default DTLS response matcher.
Supported values are STRICT, RELAXED, or PRINCIPAL.
The default value is STRICT.
Create new instance of udp endpoint context matcher.
Params:
checkAddress
– true with address check, (STRICT, UDP)
- false, without
*/
coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT");
/*
https://tools.ietf.org/html/rfc7959#section-2.9.3
The block size (number of bytes) to use when doing a blockwise transfer. \
This value serves as the upper limit for block size in blockwise transfers
*/
coapConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024);
/*
The maximum payload size (in bytes) that can be transferred in a
single message, i.e. without requiring a blockwise transfer.
NB: this value MUST be adapted to the maximum message size supported by the transport layer.
In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol
DEFAULT_VALUE = 1024
*/
coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024);
coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4);
return coapConfig;
}
}

20
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java

@ -26,14 +26,15 @@ import org.eclipse.leshan.server.registration.RegistrationUpdate;
import java.util.Collection; import java.util.Collection;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromObjectIdToIdVer;
@Slf4j @Slf4j
public class LwM2mServerListener { public class LwM2mServerListener {
private final LwM2mTransportServiceImpl service; private final LwM2mTransportMsgHandler service;
public LwM2mServerListener(LwM2mTransportServiceImpl service) { public LwM2mServerListener(LwM2mTransportMsgHandler service) {
this.service = service; this.service = service;
} }
@ -85,17 +86,19 @@ public class LwM2mServerListener {
@Override @Override
public void cancelled(Observation observation) { public void cancelled(Observation observation) {
log.info("Received notification cancelled from [{}] ", observation.getPath()); String msg = String.format("%s: Cancel Observation %s.", LOG_LW2M_INFO, observation.getPath());
service.sendLogsToThingsboard(msg, observation.getRegistrationId());
log.trace(msg);
} }
@Override @Override
public void onResponse(Observation observation, Registration registration, ObserveResponse response) { public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
if (registration != null) { if (registration != null) {
try { try {
service.onObservationResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
registration), response, null); registration), response, null);
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] onResponse", e.toString()); log.error("Observation/Read onResponse", e);
} }
} }
@ -108,7 +111,10 @@ public class LwM2mServerListener {
@Override @Override
public void newObservation(Observation observation, Registration registration) { public void newObservation(Observation observation, Registration registration) {
log.info("Received newObservation from [{}] endpoint [{}] ", observation.getPath(), registration.getEndpoint()); String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO,
observation.getPath());
service.sendLogsToThingsboard(msg, registration.getId());
log.trace(msg);
} }
}; };

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java

@ -35,10 +35,10 @@ import java.util.Optional;
@Slf4j @Slf4j
public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? super Void>>, SessionMsgListener { public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
private LwM2mTransportServiceImpl service; private DefaultLwM2MTransportMsgHandler service;
private TransportProtos.SessionInfoProto sessionInfo; private TransportProtos.SessionInfoProto sessionInfo;
public LwM2mSessionMsgListener(LwM2mTransportServiceImpl service, TransportProtos.SessionInfoProto sessionInfo) { public LwM2mSessionMsgListener(DefaultLwM2MTransportMsgHandler service, TransportProtos.SessionInfoProto sessionInfo) {
this.service = service; this.service = service;
this.sessionInfo = sessionInfo; this.sessionInfo = sessionInfo;
} }

32
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.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.transport.lwm2m.server;
import lombok.Getter;
import lombok.Setter;
import org.eclipse.leshan.server.californium.LeshanServer;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
@Component
@TbLwM2mTransportComponent
public class LwM2mTransportContext extends TransportContext {
@Getter @Setter
private LeshanServer server;
}

37
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandler.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandlerUtil.java

@ -25,7 +25,6 @@ import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.Attribute;
import org.eclipse.leshan.core.attributes.AttributeSet; import org.eclipse.leshan.core.attributes.AttributeSet;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
@ -40,7 +39,6 @@ import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.request.DownlinkRequest; import org.eclipse.leshan.core.request.DownlinkRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.californium.LeshanServerBuilder;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.nustaq.serialization.FSTConfiguration; import org.nustaq.serialization.FSTConfiguration;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
@ -50,7 +48,6 @@ import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -70,9 +67,8 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
@Slf4j @Slf4j
public class LwM2mTransportHandler { public class LwM2mTransportHandlerUtil {
// public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me";
public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0"; public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0";
public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings"; public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";
public static final String BOOTSTRAP = "bootstrap"; public static final String BOOTSTRAP = "bootstrap";
@ -85,19 +81,12 @@ public class LwM2mTransportHandler {
public static final String KEY_NAME = "keyName"; public static final String KEY_NAME = "keyName";
public static final String OBSERVE_LWM2M = "observe"; public static final String OBSERVE_LWM2M = "observe";
public static final String ATTRIBUTE_LWM2M = "attributeLwm2m"; public static final String ATTRIBUTE_LWM2M = "attributeLwm2m";
// public static final String RESOURCE_VALUE = "resValue";
// public static final String RESOURCE_TYPE = "resType";
private static final String REQUEST = "/request"; private static final String REQUEST = "/request";
// private static final String RESPONSE = "/response";
private static final String ATTRIBUTES = "/" + ATTRIBUTE; private static final String ATTRIBUTES = "/" + ATTRIBUTE;
public static final String TELEMETRIES = "/" + TELEMETRY; public static final String TELEMETRIES = "/" + TELEMETRY;
// public static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE;
public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST; public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST;
// public static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/";
public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/"; public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/";
// public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES;
// public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRIES;
public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
@ -112,6 +101,11 @@ public class LwM2mTransportHandler {
public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized"; public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized";
public static final Integer FR_OBJECT_ID = 5;
public static final Integer FR_RESOURCE_VER_ID = 7;
public static final String FR_PATH_RESOURCE_VER_ID = LWM2M_SEPARATOR_PATH + FR_OBJECT_ID + LWM2M_SEPARATOR_PATH
+ "0" + LWM2M_SEPARATOR_PATH + FR_RESOURCE_VER_ID;
public enum LwM2mTypeServer { public enum LwM2mTypeServer {
BOOTSTRAP(0, "bootstrap"), BOOTSTRAP(0, "bootstrap"),
CLIENT(1, "client"); CLIENT(1, "client");
@ -168,6 +162,8 @@ public class LwM2mTransportHandler {
WRITE_ATTRIBUTES(8, "WriteAttributes"), WRITE_ATTRIBUTES(8, "WriteAttributes"),
DELETE(9, "Delete"); DELETE(9, "Delete");
// READ_INFO_FW(10, "ReadInfoFirmware");
public int code; public int code;
public String type; public String type;
@ -190,21 +186,6 @@ public class LwM2mTransportHandler {
public static final String SERVICE_CHANNEL = "SERVICE"; public static final String SERVICE_CHANNEL = "SERVICE";
public static final String RESPONSE_CHANNEL = "RESP"; public static final String RESPONSE_CHANNEL = "RESP";
public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) {
NetworkConfig coapConfig;
File configFile = new File(NetworkConfig.DEFAULT_FILE_NAME);
if (configFile.isFile()) {
coapConfig = new NetworkConfig();
coapConfig.load(configFile);
} else {
coapConfig = LeshanServerBuilder.createDefaultNetworkConfig();
coapConfig.store(configFile);
}
coapConfig.setString("COAP_PORT", Integer.toString(serverPortNoSec));
coapConfig.setString("COAP_SECURE_PORT", Integer.toString(serverSecurePort));
return coapConfig;
}
public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException { public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException {
switch (type) { switch (type) {
case BOOLEAN: case BOOLEAN:
@ -275,7 +256,7 @@ public class LwM2mTransportHandler {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String profileStr = mapper.writeValueAsString(profile); String profileStr = mapper.writeValueAsString(profile);
JsonObject profileJson = (profileStr != null) ? validateJson(profileStr) : null; JsonObject profileJson = (profileStr != null) ? validateJson(profileStr) : null;
return getValidateCredentialsBodyFromThingsboard(profileJson) ? LwM2mTransportHandler.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null; return getValidateCredentialsBodyFromThingsboard(profileJson) ? LwM2mTransportHandlerUtil.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null;
} catch (IOException e) { } catch (IOException e) {
log.error("", e); log.error("", e);
} }

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java

@ -27,7 +27,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcReques
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
public interface LwM2mTransportService extends TbTransportService { public interface LwM2mTransportMsgHandler {
void onRegistered(Registration registration, Collection<Observation> previousObsersations); void onRegistered(Registration registration, Collection<Observation> previousObsersations);
@ -39,7 +39,7 @@ public interface LwM2mTransportService extends TbTransportService {
void setCancelObservations(Registration registration); void setCancelObservations(Registration registration);
void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest); void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest);
void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo); void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo);
@ -61,5 +61,7 @@ public interface LwM2mTransportService extends TbTransportService {
void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
void onAwakeDev(Registration registration);
void sendLogsToThingsboard(String msg, String registrationId);
} }

205
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server; package org.thingsboard.server.transport.lwm2m.server;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.CoAP;
@ -50,6 +51,7 @@ import org.eclipse.leshan.server.registration.Registration;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
@ -66,50 +68,38 @@ import java.util.stream.Collectors;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST;
import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND; import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.DEFAULT_TIMEOUT;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.FR_PATH_RESOURCE_VER_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_VALUE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_CANCEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_READ_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_CANCEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.RESPONSE_CHANNEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromIdVerToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.RESPONSE_CHANNEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.createWriteAttributeRequest; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromObjectIdToIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.createWriteAttributeRequest;
@Slf4j @Slf4j
@Service @Service
@TbLwM2mTransportComponent @TbLwM2mTransportComponent
@RequiredArgsConstructor
public class LwM2mTransportRequest { public class LwM2mTransportRequest {
private ExecutorService executorResponse; private ExecutorService executorResponse;
public LwM2mValueConverterImpl converter; public LwM2mValueConverterImpl converter;
private final LwM2mTransportContextServer lwM2mTransportContextServer; private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config;
private final LwM2mTransportServerHelper lwM2MTransportServerHelper;
private final LwM2mClientContext lwM2mClientContext; private final LwM2mClientContext lwM2mClientContext;
private final DefaultLwM2MTransportMsgHandler serviceImpl;
private final LeshanServer leshanServer;
private final LwM2mTransportServiceImpl serviceImpl;
private final TransportService transportService;
public LwM2mTransportRequest(LwM2mTransportContextServer lwM2mTransportContextServer,
LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer,
LwM2mTransportServiceImpl serviceImpl, TransportService transportService) {
this.lwM2mTransportContextServer = lwM2mTransportContextServer;
this.lwM2mClientContext = lwM2mClientContext;
this.leshanServer = leshanServer;
this.serviceImpl = serviceImpl;
this.transportService = transportService;
}
@PostConstruct @PostConstruct
public void init() { public void init() {
this.converter = LwM2mValueConverterImpl.getInstance(); this.converter = LwM2mValueConverterImpl.getInstance();
executorResponse = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getResponsePoolSize(), executorResponse = Executors.newFixedThreadPool(this.config.getResponsePoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL))); new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL)));
} }
@ -125,7 +115,6 @@ public class LwM2mTransportRequest {
public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper, public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
try { try {
String target = convertPathFromIdVerToObjectId(targetIdVer); String target = convertPathFromIdVerToObjectId(targetIdVer);
DownlinkRequest request = null; DownlinkRequest request = null;
ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT; ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT;
@ -145,11 +134,11 @@ public class LwM2mTransportRequest {
break; break;
case OBSERVE: case OBSERVE:
if (resultIds.isResource()) { if (resultIds.isResource()) {
request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
} else if (resultIds.isObjectInstance()) { } else if (resultIds.isObjectInstance()) {
request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId()); request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId());
} else if (resultIds.getObjectId() >= 0) { } else if (resultIds.getObjectId() >= 0) {
request = new ObserveRequest(resultIds.getObjectId()); request = new ObserveRequest(contentFormat, resultIds.getObjectId());
} }
break; break;
case OBSERVE_CANCEL: case OBSERVE_CANCEL:
@ -158,10 +147,10 @@ public class LwM2mTransportRequest {
* At server side this will not remove the observation from the observation store, to do it you need to use * At server side this will not remove the observation from the observation store, to do it you need to use
* {@code ObservationService#cancelObservation()} * {@code ObservationService#cancelObservation()}
*/ */
leshanServer.getObservationService().cancelObservations(registration, target); context.getServer().getObservationService().cancelObservations(registration, target);
break; break;
case EXECUTE: case EXECUTE:
resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config
.getModelProvider()); .getModelProvider());
if (params != null && !resourceModel.multiple) { if (params != null && !resourceModel.multiple) {
request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModel.type, ResourceModel.Type.STRING, resultIds)); request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModel.type, ResourceModel.Type.STRING, resultIds));
@ -171,9 +160,7 @@ public class LwM2mTransportRequest {
break; break;
case WRITE_REPLACE: case WRITE_REPLACE:
// Request to write a <b>String Single-Instance Resource</b> using the TLV content format. // Request to write a <b>String Single-Instance Resource</b> using the TLV content format.
// resource = lwM2MClient.getResourceModel(targetIdVer); resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config
// if (contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {
resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer()
.getModelProvider()); .getModelProvider());
if (contentFormat.equals(ContentFormat.TLV)) { if (contentFormat.equals(ContentFormat.TLV)) {
request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(),
@ -181,7 +168,6 @@ public class LwM2mTransportRequest {
registration, rpcRequest); registration, rpcRequest);
} }
// Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON) // Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON)
// else if (!contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {
else if (!contentFormat.equals(ContentFormat.TLV)) { else if (!contentFormat.equals(ContentFormat.TLV)) {
request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type, resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type,
@ -215,13 +201,16 @@ public class LwM2mTransportRequest {
long finalTimeoutInMs = timeoutInMs; long finalTimeoutInMs = timeoutInMs;
lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest)); lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest));
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper, e); log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e);
}
} else if (OBSERVE_CANCEL == typeOper) {
log.trace("[{}], [{}] - [{}] SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
if (rpcRequest != null) {
rpcRequest.setInfoMsg(null);
serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null);
} }
} else if (OBSERVE_CANCEL == typeOper && rpcRequest != null) {
rpcRequest.setInfoMsg(null);
serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null);
} else { } else {
log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper, targetIdVer); log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
if (rpcRequest != null) { if (rpcRequest != null) {
String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
@ -232,12 +221,12 @@ public class LwM2mTransportRequest {
serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
} }
} else if (OBSERVE_READ_ALL.name().equals(typeOper.name())) { } else if (OBSERVE_READ_ALL.name().equals(typeOper.name())) {
Set<Observation> observations = leshanServer.getObservationService().getObservations(registration); Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
Set<String> observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); Set<String> observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO, String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO,
OBSERVE_READ_ALL.type, observationPaths); OBSERVE_READ_ALL.type, observationPaths);
serviceImpl.sendLogsToThingsboard(msg, registration); serviceImpl.sendLogsToThingsboard(msg, registration.getId());
log.info("[{}], [{}]", registration.getEndpoint(), msg); log.trace("[{}] [{}], [{}]", typeOper.name(), registration.getEndpoint(), msg);
if (rpcRequest != null) { if (rpcRequest != null) {
String valueMsg = String.format("Observation paths - %s", observationPaths); String valueMsg = String.format("Observation paths - %s", observationPaths);
serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
@ -246,7 +235,7 @@ public class LwM2mTransportRequest {
} catch (Exception e) { } catch (Exception e) {
String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
typeOper.name(), e.getMessage()); typeOper.name(), e.getMessage());
serviceImpl.sendLogsToThingsboard(msg, registration); serviceImpl.sendLogsToThingsboard(msg, registration.getId());
throw new Exception(e); throw new Exception(e);
} }
} }
@ -259,47 +248,55 @@ public class LwM2mTransportRequest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
leshanServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> { context.getServer().send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
if (!lwM2MClient.isInit()) { if (!lwM2MClient.isInit()) {
lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
} }
if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest); this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest);
if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) {
LwM2mNode node = ((WriteRequest) request).getNode();
Object value = this.converter.convertValue(((LwM2mSingleResource) node).getValue(),
((LwM2mSingleResource) node).getType(), ResourceModel.Type.STRING, request.getPath());
String msg = String.format("%s: sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client",
LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(),
response.getCode().getName(), request.getPath().toString(), value);
serviceImpl.sendLogsToThingsboard(msg, registration);
log.info("[{}] [{}] - [{}] [{}] Update SendRequest[{}]", registration.getEndpoint(),
((Response) response.getCoapResponse()).getCode(), response.getCode(),
request.getPath().toString(), value);
serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
}
} else { } else {
String msg = String.format("%s: sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", LOG_LW2M_ERROR, String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(),
((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
serviceImpl.sendLogsToThingsboard(msg, registration); serviceImpl.sendLogsToThingsboard(msg, registration.getId());
log.error("[{}], [{}] - [{}] [{}] error SendRequest", registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(),
((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
if (!lwM2MClient.isInit()) {
lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
}
if (rpcRequest != null) { if (rpcRequest != null) {
serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR); serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
} }
/** Not Found
* set setClient_fw_version = empty
**/
if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
lwM2MClient.setUpdateFw(false);
lwM2MClient.getFrUpdate().setClientFwVersion("");
log.warn("updateFirmwareClient1");
serviceImpl.updateFirmwareClient(lwM2MClient);
}
} }
}, e -> { }, e -> {
/** version == null
* set setClient_fw_version = empty
**/
if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
lwM2MClient.setUpdateFw(false);
lwM2MClient.getFrUpdate().setClientFwVersion("");
log.warn("updateFirmwareClient2");
serviceImpl.updateFirmwareClient(lwM2MClient);
}
if (!lwM2MClient.isInit()) { if (!lwM2MClient.isInit()) {
lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
} }
String msg = String.format("%s: sendRequest: Resource path - %s msg error - %s SendRequest to Client", String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s",
LOG_LW2M_ERROR, request.getPath().toString(), e.getMessage()); LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage());
serviceImpl.sendLogsToThingsboard(msg, registration); serviceImpl.sendLogsToThingsboard(msg, registration.getId());
log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString()); log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
if (rpcRequest != null) { if (rpcRequest != null) {
serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
} }
}); });
} }
private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId,
@ -323,7 +320,9 @@ public class LwM2mTransportRequest {
Date date = new Date(Long.decode(value.toString())); Date date = new Date(Long.decode(value.toString()));
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date); return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date);
case OPAQUE: // byte[] value, base64 case OPAQUE: // byte[] value, base64
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())); byte[] valueRequest = value instanceof byte[] ? (byte[]) value : Hex.decodeHex(value.toString().toCharArray());
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueRequest) :
new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueRequest);
default: default:
} }
} }
@ -337,7 +336,7 @@ public class LwM2mTransportRequest {
String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
patn, type, value, e.toString()); patn, type, value, e.toString());
serviceImpl.sendLogsToThingsboard(msg, registration); serviceImpl.sendLogsToThingsboard(msg, registration.getId());
log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
if (rpcRequest != null) { if (rpcRequest != null) {
String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
@ -369,7 +368,7 @@ public class LwM2mTransportRequest {
DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
if (response instanceof ReadResponse) { if (response instanceof ReadResponse) {
serviceImpl.onObservationResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); serviceImpl.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
} else if (response instanceof CancelObservationResponse) { } else if (response instanceof CancelObservationResponse) {
log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response); log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response);
@ -389,14 +388,56 @@ public class LwM2mTransportRequest {
} else if (response instanceof WriteAttributesResponse) { } else if (response instanceof WriteAttributesResponse) {
log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response); log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response);
} else if (response instanceof WriteResponse) { } else if (response instanceof WriteResponse) {
log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", pathIdVer, response); log.info("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response);
this.infoWriteResponse(registration, response, request);
serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
} }
if (rpcRequest != null && (response instanceof ExecuteResponse if (rpcRequest != null) {
|| response instanceof WriteAttributesResponse if (response instanceof ExecuteResponse
|| response instanceof DeleteResponse)) { || response instanceof WriteAttributesResponse
rpcRequest.setInfoMsg(null); || response instanceof DeleteResponse) {
serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null); rpcRequest.setInfoMsg(null);
serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null);
} else if (response instanceof WriteResponse) {
serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
}
}
}
private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request) {
try {
LwM2mNode node = ((WriteRequest) request).getNode();
String msg;
Object value;
LwM2mSingleResource singleResource = (LwM2mSingleResource) node;
if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) {
int valueLength;
if (singleResource.getType() == ResourceModel.Type.STRING) {
valueLength = ((String) singleResource.getValue()).length();
value = ((String) singleResource.getValue())
.substring(Math.min(valueLength, config.getLogMaxLength()));
} else {
valueLength = ((byte[]) singleResource.getValue()).length;
value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()),
Math.min(valueLength, config.getLogMaxLength())));
}
value = valueLength > config.getLogMaxLength() ? value + "..." : value;
msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path - %s length - %s value - %s",
LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value);
} else {
value = this.converter.convertValue(singleResource.getValue(),
singleResource.getType(), ResourceModel.Type.STRING, request.getPath());
msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path - %s value - %s",
LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value);
}
if (msg != null) {
serviceImpl.sendLogsToThingsboard(msg, registration.getId());
log.warn("[{}] [{}] [{}] - [{}] [{}] Update finished successfully: [{}]", request.getClass().getName(), registration.getEndpoint(),
((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(), value);
}
} catch (Exception e) {
log.trace("Fail convert value from request to string. ", e);
} }
} }
} }

85
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java

@ -31,6 +31,7 @@ package org.thingsboard.server.transport.lwm2m.server;
*/ */
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.model.DDFFileParser; import org.eclipse.leshan.core.model.DDFFileParser;
import org.eclipse.leshan.core.model.DefaultDDFFileValidator; import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
@ -39,11 +40,8 @@ import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.node.codec.CodecException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.TransportResourceCache;
import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
@ -57,39 +55,21 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.BOOLEAN_V; import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.BOOLEAN_V;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_TELEMETRY;
@Slf4j @Slf4j
@Component @Component
@TbLwM2mTransportComponent @TbLwM2mTransportComponent
public class LwM2mTransportContextServer extends TransportContext { @RequiredArgsConstructor
public class LwM2mTransportServerHelper {
private final LwM2mTransportContext context;
private final LwM2MTransportConfigServer lwM2MTransportConfigServer;
private final TransportService transportService; private final TransportService transportService;
private final TransportResourceCache transportResourceCache;
@Getter @Getter
private final LwM2MJsonAdaptor adaptor; private final LwM2MJsonAdaptor adaptor;
public LwM2mTransportContextServer(LwM2MTransportConfigServer lwM2MTransportConfigServer, TransportService transportService, TransportResourceCache transportResourceCache, LwM2MJsonAdaptor adaptor) {
this.lwM2MTransportConfigServer = lwM2MTransportConfigServer;
this.transportService = transportService;
this.transportResourceCache = transportResourceCache;
this.adaptor = adaptor;
}
public LwM2MTransportConfigServer getLwM2MTransportConfigServer() {
return this.lwM2MTransportConfigServer;
}
public TransportResourceCache getTransportResourceCache() {
return this.transportResourceCache;
}
/** /**
* send to Thingsboard Attribute || Telemetry * send to Thingsboard Attribute || Telemetry
* *
@ -134,7 +114,7 @@ public class LwM2mTransportContextServer extends TransportContext {
*/ */
public SessionInfoProto getValidateSessionInfo(TransportProtos.ValidateDeviceCredentialsResponseMsg msg, long mostSignificantBits, long leastSignificantBits) { public SessionInfoProto getValidateSessionInfo(TransportProtos.ValidateDeviceCredentialsResponseMsg msg, long mostSignificantBits, long leastSignificantBits) {
return SessionInfoProto.newBuilder() return SessionInfoProto.newBuilder()
.setNodeId(this.getNodeId()) .setNodeId(context.getNodeId())
.setSessionIdMSB(mostSignificantBits) .setSessionIdMSB(mostSignificantBits)
.setSessionIdLSB(leastSignificantBits) .setSessionIdLSB(leastSignificantBits)
.setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB()) .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
@ -165,8 +145,8 @@ public class LwM2mTransportContextServer extends TransportContext {
* @param logMsg - info about Logs * @param logMsg - info about Logs
* @return- KeyValueProto for telemetry (Logs) * @return- KeyValueProto for telemetry (Logs)
*/ */
public List <TransportProtos.KeyValueProto> getKvLogyToThingsboard(String logMsg) { public List<TransportProtos.KeyValueProto> getKvLogyToThingsboard(String logMsg) {
List <TransportProtos.KeyValueProto> result = new ArrayList<>(); List<TransportProtos.KeyValueProto> result = new ArrayList<>();
result.add(TransportProtos.KeyValueProto.newBuilder() result.add(TransportProtos.KeyValueProto.newBuilder()
.setKey(LOG_LW2M_TELEMETRY) .setKey(LOG_LW2M_TELEMETRY)
.setType(TransportProtos.KeyValueType.STRING_V) .setType(TransportProtos.KeyValueType.STRING_V)
@ -179,32 +159,31 @@ public class LwM2mTransportContextServer extends TransportContext {
* @throws CodecException - * @throws CodecException -
*/ */
public TransportProtos.KeyValueProto getKvAttrTelemetryToThingsboard(ResourceModel.Type resourceType, String resourceName, Object value, boolean isMultiInstances) { public TransportProtos.KeyValueProto getKvAttrTelemetryToThingsboard(ResourceModel.Type resourceType, String resourceName, Object value, boolean isMultiInstances) {
TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(resourceName); TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(resourceName);
if (isMultiInstances) { if (isMultiInstances) {
kvProto.setType(TransportProtos.KeyValueType.JSON_V) kvProto.setType(TransportProtos.KeyValueType.JSON_V)
.setJsonV((String) value); .setJsonV((String) value);
} else {
switch (resourceType) {
case BOOLEAN:
kvProto.setType(BOOLEAN_V).setBoolV((Boolean) value).build();
break;
case STRING:
case TIME:
case OPAQUE:
case OBJLNK:
kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV((String) value);
break;
case INTEGER:
kvProto.setType(TransportProtos.KeyValueType.LONG_V).setLongV((Long) value);
break;
case FLOAT:
kvProto.setType(TransportProtos.KeyValueType.DOUBLE_V).setDoubleV((Double) value);
} }
else {
switch (resourceType) {
case BOOLEAN:
kvProto.setType(BOOLEAN_V).setBoolV((Boolean) value).build();
break;
case STRING:
case TIME:
case OPAQUE:
case OBJLNK:
kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV((String) value);
break;
case INTEGER:
kvProto.setType(TransportProtos.KeyValueType.LONG_V).setLongV((Long) value);
break;
case FLOAT:
kvProto.setType(TransportProtos.KeyValueType.DOUBLE_V).setDoubleV((Double) value);
}
}
return kvProto.build();
} }
return kvProto.build();
}
/** /**
* *
@ -230,7 +209,7 @@ public class LwM2mTransportContextServer extends TransportContext {
throw new CodecException("Invalid ResourceModel_Type for resource %s, got %s", resourcePath, currentType); throw new CodecException("Invalid ResourceModel_Type for resource %s, got %s", resourcePath, currentType);
} }
public Object getValueFromKvProto (TransportProtos.KeyValueProto kv) { public Object getValueFromKvProto(TransportProtos.KeyValueProto kv) {
switch (kv.getType()) { switch (kv.getType()) {
case BOOLEAN_V: case BOOLEAN_V:
return kv.getBoolV(); return kv.getBoolV();

31
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java

@ -30,35 +30,4 @@ import javax.annotation.PreDestroy;
@TbLwM2mTransportComponent @TbLwM2mTransportComponent
public class LwM2mTransportServerInitializer { public class LwM2mTransportServerInitializer {
@Autowired
private LwM2mTransportServiceImpl service;
@Autowired
private LeshanServer leshanServer;
@Autowired
private LwM2mTransportContextServer context;
@PostConstruct
public void init() {
if (this.context.getLwM2MTransportConfigServer().getEnableGenNewKeyPskRpk()) {
new LWM2MGenerationPSkRPkECC();
}
this.startLhServer();
}
private void startLhServer() {
this.leshanServer.start();
LwM2mServerListener lhServerCertListener = new LwM2mServerListener(service);
this.leshanServer.getRegistrationService().addListener(lhServerCertListener.registrationListener);
this.leshanServer.getPresenceService().addListener(lhServerCertListener.presenceListener);
this.leshanServer.getObservationService().addListener(lhServerCertListener.observationListener);
}
@PreDestroy
public void shutdown() {
log.info("Stopping LwM2M transport Server!");
leshanServer.destroy();
log.info("LwM2M transport Server stopped!");
}
} }

16
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server; package org.thingsboard.server.transport.lwm2m.server;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.model.DefaultDDFFileValidator; import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
import org.eclipse.leshan.core.model.LwM2mModel; import org.eclipse.leshan.core.model.LwM2mModel;
@ -37,6 +38,7 @@ import static org.thingsboard.server.common.data.ResourceType.LWM2M_MODEL;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
@Slf4j @Slf4j
@RequiredArgsConstructor
public class LwM2mVersionedModelProvider implements LwM2mModelProvider { public class LwM2mVersionedModelProvider implements LwM2mModelProvider {
/** /**
@ -46,12 +48,8 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider {
* Value = TenantId * Value = TenantId
*/ */
private final LwM2mClientContext lwM2mClientContext; private final LwM2mClientContext lwM2mClientContext;
private final LwM2mTransportContextServer lwM2mTransportContextServer; private final LwM2mTransportServerHelper helper;
private final LwM2mTransportContext context;
public LwM2mVersionedModelProvider(LwM2mClientContext lwM2mClientContext, LwM2mTransportContextServer lwM2mTransportContextServer) {
this.lwM2mClientContext = lwM2mClientContext;
this.lwM2mTransportContextServer = lwM2mTransportContextServer;
}
private String getKeyIdVer(Integer objectId, String version) { private String getKeyIdVer(Integer objectId, String version) {
return objectId != null ? objectId + LWM2M_SEPARATOR_KEY + ((version == null || version.isEmpty()) ? ObjectModel.DEFAULT_VERSION : version) : null; return objectId != null ? objectId + LWM2M_SEPARATOR_KEY + ((version == null || version.isEmpty()) ? ObjectModel.DEFAULT_VERSION : version) : null;
@ -120,11 +118,9 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider {
private ObjectModel getObjectModelDynamic(Integer objectId, String version) { private ObjectModel getObjectModelDynamic(Integer objectId, String version) {
String key = getKeyIdVer(objectId, version); String key = getKeyIdVer(objectId, version);
Optional<TbResource> tbResource = lwM2mTransportContextServer Optional<TbResource> tbResource = context.getTransportResourceCache().get(this.tenantId, LWM2M_MODEL, key);
.getTransportResourceCache()
.get(this.tenantId, LWM2M_MODEL, key);
return tbResource.map(resource -> lwM2mTransportContextServer.parseFromXmlToObjectModel( return tbResource.map(resource -> helper.parseFromXmlToObjectModel(
Base64.getDecoder().decode(resource.getData()), Base64.getDecoder().decode(resource.getData()),
key + ".xml", key + ".xml",
new DefaultDDFFileValidator())).orElse(null); new DefaultDDFFileValidator())).orElse(null);

21
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java

@ -24,11 +24,8 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.Arrays; import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.Set;
@Slf4j @Slf4j
@Component("LwM2MJsonAdaptor") @Component("LwM2MJsonAdaptor")
@ -54,11 +51,7 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
} }
@Override @Override
public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException { public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection<String> clientKeys, Collection<String> sharedKeys) throws AdaptorException {
return processGetAttributeRequestMsg(clientKeys, sharedKeys);
}
protected TransportProtos.GetAttributeRequestMsg processGetAttributeRequestMsg(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException {
try { try {
TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
Random random = new Random(); Random random = new Random();
@ -75,14 +68,4 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor {
throw new AdaptorException(e); throw new AdaptorException(e);
} }
} }
private Set<String> toStringSet(JsonElement requestBody, String name) {
JsonElement element = requestBody.getAsJsonObject().get(name);
if (element != null) {
return new HashSet<>(Arrays.asList(element.getAsString().split(",")));
} else {
return null;
}
}
} }

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java

@ -19,7 +19,7 @@ import com.google.gson.JsonElement;
import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.List; import java.util.Collection;
public interface LwM2MTransportAdaptor { public interface LwM2MTransportAdaptor {
@ -27,5 +27,5 @@ public interface LwM2MTransportAdaptor {
TransportProtos.PostAttributeMsg convertToPostAttributes(JsonElement jsonElement) throws AdaptorException; TransportProtos.PostAttributeMsg convertToPostAttributes(JsonElement jsonElement) throws AdaptorException;
TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException; TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection<String> clientKeys, Collection<String> sharedKeys) throws AdaptorException;
} }

26
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java

@ -27,7 +27,7 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServiceImpl; import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import java.util.Collection; import java.util.Collection;
@ -42,9 +42,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.TRANSPORT_DEFAULT_LWM2M_VERSION; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.TRANSPORT_DEFAULT_LWM2M_VERSION;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromIdVerToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getVerFromPathIdVerOrId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.getVerFromPathIdVerOrId;
@Slf4j @Slf4j
@Data @Data
@ -57,13 +57,15 @@ public class LwM2mClient implements Cloneable {
private UUID deviceId; private UUID deviceId;
private UUID sessionId; private UUID sessionId;
private UUID profileId; private UUID profileId;
private volatile LwM2mFirmwareUpdate frUpdate;
private Registration registration; private Registration registration;
private ValidateDeviceCredentialsResponseMsg credentialsResponse; private ValidateDeviceCredentialsResponseMsg credentialsResponse;
private final Map<String, ResourceValue> resources; private final Map<String, ResourceValue> resources;
private final Map<String, TransportProtos.TsKvProto> delayedRequests; private final Map<String, TransportProtos.TsKvProto> delayedRequests;
private final List<String> pendingRequests; private final List<String> pendingReadRequests;
private final Queue<LwM2mQueuedRequest> queuedRequests; private final Queue<LwM2mQueuedRequest> queuedRequests;
private boolean init; private boolean init;
private volatile boolean updateFw;
public Object clone() throws CloneNotSupportedException { public Object clone() throws CloneNotSupportedException {
return super.clone(); return super.clone();
@ -75,12 +77,14 @@ public class LwM2mClient implements Cloneable {
this.securityInfo = securityInfo; this.securityInfo = securityInfo;
this.credentialsResponse = credentialsResponse; this.credentialsResponse = credentialsResponse;
this.delayedRequests = new ConcurrentHashMap<>(); this.delayedRequests = new ConcurrentHashMap<>();
this.pendingRequests = new CopyOnWriteArrayList<>(); this.pendingReadRequests = new CopyOnWriteArrayList<>();
this.resources = new ConcurrentHashMap<>(); this.resources = new ConcurrentHashMap<>();
this.profileId = profileId; this.profileId = profileId;
this.sessionId = sessionId; this.sessionId = sessionId;
this.init = false; this.init = false;
this.updateFw = false;
this.queuedRequests = new ConcurrentLinkedQueue<>(); this.queuedRequests = new ConcurrentLinkedQueue<>();
this.frUpdate = new LwM2mFirmwareUpdate();
} }
public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) { public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) {
@ -103,15 +107,13 @@ public class LwM2mClient implements Cloneable {
LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez)); LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez));
String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
String verRez = getVerFromPathIdVerOrId(pathRez); String verRez = getVerFromPathIdVerOrId(pathRez);
return (verRez == null || verSupportedObject.equals(verRez)) ? modelProvider.getObjectModel(registration) return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration)
.getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null; .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null;
} }
public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, LwM2mModelProvider modelProvider, public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, LwM2mModelProvider modelProvider,
LwM2mValueConverterImpl converter) { LwM2mValueConverterImpl converter) {
LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer)); LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer));
String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
String verRez = getVerFromPathIdVerOrId(pathRezIdVer);
Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet(); Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet();
Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration) Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration)
.getObjectModel(pathIds.getObjectId()).resources; .getObjectModel(pathIds.getObjectId()).resources;
@ -170,11 +172,11 @@ public class LwM2mClient implements Cloneable {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
public void initValue(LwM2mTransportServiceImpl serviceImpl, String path) { public void initReadValue(DefaultLwM2MTransportMsgHandler serviceImpl, String path) {
if (path != null) { if (path != null) {
this.pendingRequests.remove(path); this.pendingReadRequests.remove(path);
} }
if (this.pendingRequests.size() == 0) { if (this.pendingReadRequests.size() == 0) {
this.init = true; this.init = true;
serviceImpl.putDelayedUpdateResourcesThingsboard(this); serviceImpl.putDelayedUpdateResourcesThingsboard(this);
} }

18
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java

@ -25,7 +25,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
@ -34,7 +34,7 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromObjectIdToIdVer;
@Service @Service
@TbLwM2mTransportComponent @TbLwM2mTransportComponent
@ -82,12 +82,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
@Override @Override
public LwM2mClient getLwM2mClientWithReg(Registration registration, String registrationId) { public LwM2mClient getLwM2mClientWithReg(Registration registration, String registrationId) {
LwM2mClient client = registrationId != null ? LwM2mClient client = registrationId != null && this.lwM2mClients.containsKey(registrationId) ?
this.lwM2mClients.get(registrationId) : this.lwM2mClients.get(registrationId) :
this.lwM2mClients.containsKey(registration.getId()) ? registration !=null && this.lwM2mClients.containsKey(registration.getId()) ?
this.lwM2mClients.get(registration.getId()) : this.lwM2mClients.get(registration.getId()) : registration !=null && this.lwM2mClients.containsKey(registration) ?
this.lwM2mClients.get(registration.getEndpoint()); this.lwM2mClients.get(registration.getEndpoint()) : null;
return client != null ? client : updateInSessionsLwM2MClient(registration); return client != null ? client : registration!= null ? updateInSessionsLwM2MClient(registration) : null;
} }
@Override @Override
@ -118,7 +118,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
*/ */
@Override @Override
public LwM2mClient addLwM2mClientToSession(String identity) { public LwM2mClient addLwM2mClientToSession(String identity) {
ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(identity, LwM2mTransportHandler.LwM2mTypeServer.CLIENT); ReadResultSecurityStore store = lwM2MCredentialsSecurityInfoValidator.createAndValidateCredentialsSecurityInfo(identity, LwM2mTransportHandlerUtil.LwM2mTypeServer.CLIENT);
if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) { if (store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null; UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null;
LwM2mClient client; LwM2mClient client;
@ -165,7 +165,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
@Override @Override
public boolean addUpdateProfileParameters(DeviceProfile deviceProfile) { public boolean addUpdateProfileParameters(DeviceProfile deviceProfile) {
LwM2mClientProfile lwM2MClientProfile = LwM2mTransportHandler.getLwM2MClientProfileFromThingsboard(deviceProfile); LwM2mClientProfile lwM2MClientProfile = LwM2mTransportHandlerUtil.getLwM2MClientProfileFromThingsboard(deviceProfile);
if (lwM2MClientProfile != null) { if (lwM2MClientProfile != null) {
profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile); profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile);
return true; return true;

27
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFirmwareUpdate.java

@ -0,0 +1,27 @@
/**
* 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.lwm2m.server.client;
import lombok.Data;
import java.util.UUID;
@Data
public class LwM2mFirmwareUpdate {
private volatile String clientFwVersion;
private volatile String currentFwVersion;
private volatile UUID currentFwId;
}

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java

@ -21,11 +21,11 @@ import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.validPathIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.validPathIdVer;
@Data @Data
public class Lwm2mClientRpcRequest { public class Lwm2mClientRpcRequest {

0
common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh

0
common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks → common/transport/lwm2m/src/main/resources/lwm2mserver.jks

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

@ -47,6 +47,7 @@ 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.TransportPayloadType; import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.MqttTopics; import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
@ -59,6 +60,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.common.transport.service.DefaultTransportService;
import org.thingsboard.server.common.transport.service.SessionMetaData; import org.thingsboard.server.common.transport.service.SessionMetaData;
import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
@ -68,7 +70,6 @@ import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler; import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler;
import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher; import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher;
import org.thingsboard.server.common.transport.util.SslUtil;
import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLPeerUnverifiedException;
import java.io.IOException; import java.io.IOException;
@ -97,6 +98,8 @@ import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK;
import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE;
import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE;
import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka
@ -104,7 +107,9 @@ import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE;
@Slf4j @Slf4j
public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener<Future<? super Void>>, SessionMsgListener { public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
private static final Pattern FW_PATTERN = Pattern.compile("v2/fw/request/(?<requestId>\\d+)/chunk/(?<chunk>\\d+)"); private static final Pattern FW_REQUEST_PATTERN = Pattern.compile(DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN);
private static final Pattern SW_REQUEST_PATTERN = Pattern.compile(DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN);
private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE"; private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE";
@ -314,38 +319,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
} else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) { } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) {
TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg);
transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg));
} else if ((fwMatcher = FW_PATTERN.matcher(topicName)).find()) { } else if ((fwMatcher = FW_REQUEST_PATTERN.matcher(topicName)).find()) {
String payload = mqttMsg.content().toString(UTF8); getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.FIRMWARE);
int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0; } else if ((fwMatcher = SW_REQUEST_PATTERN.matcher(topicName)).find()) {
String requestId = fwMatcher.group("requestId"); getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.SOFTWARE);
int chunk = Integer.parseInt(fwMatcher.group("chunk"));
if (chunkSize > 0) {
this.fwChunkSizes.put(requestId, chunkSize);
} else {
chunkSize = fwChunkSizes.getOrDefault(requestId, 0);
}
if (chunkSize > context.getMaxPayloadSize()) {
sendFirmwareError(ctx, PAYLOAD_TOO_LARGE);
return;
}
String firmwareId = fwSessions.get(requestId);
if (firmwareId != null) {
sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk);
} else {
TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo();
TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.build();
transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg,
new FirmwareCallback(ctx, mqttMsg.variableHeader().packetId(), getFirmwareRequestMsg, requestId, chunkSize, chunk));
}
} else { } else {
transportService.reportActivity(deviceSessionCtx.getSessionInfo()); transportService.reportActivity(deviceSessionCtx.getSessionInfo());
ack(ctx, msgId); ack(ctx, msgId);
@ -357,6 +334,41 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
} }
} }
private void getFirmwareCallback(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, int msgId, Matcher fwMatcher, FirmwareType type) {
String payload = mqttMsg.content().toString(UTF8);
int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0;
String requestId = fwMatcher.group("requestId");
int chunk = Integer.parseInt(fwMatcher.group("chunk"));
if (chunkSize > 0) {
this.fwChunkSizes.put(requestId, chunkSize);
} else {
chunkSize = fwChunkSizes.getOrDefault(requestId, 0);
}
if (chunkSize > context.getMaxPayloadSize()) {
sendFirmwareError(ctx, PAYLOAD_TOO_LARGE);
return;
}
String firmwareId = fwSessions.get(requestId);
if (firmwareId != null) {
sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk, type);
} else {
TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo();
TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setType(type.name())
.build();
transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg,
new FirmwareCallback(ctx, msgId, getFirmwareRequestMsg, requestId, chunkSize, chunk));
}
}
private void ack(ChannelHandlerContext ctx, int msgId) { private void ack(ChannelHandlerContext ctx, int msgId) {
if (msgId > 0) { if (msgId > 0) {
ctx.writeAndFlush(createMqttPubAckMsg(msgId)); ctx.writeAndFlush(createMqttPubAckMsg(msgId));
@ -435,7 +447,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())); FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB()));
fwSessions.put(requestId, firmwareId.toString()); fwSessions.put(requestId, firmwareId.toString());
sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk); sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk, FirmwareType.valueOf(response.getType()));
} else { } else {
sendFirmwareError(ctx, response.getResponseStatus().toString()); sendFirmwareError(ctx, response.getResponseStatus().toString());
} }
@ -448,13 +460,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
} }
} }
private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk) { private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, FirmwareType type) {
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.getFirmwareDataCache().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, type)
.ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
if (firmwareChunk != null && chunkSize != firmwareChunk.length) { if (firmwareChunk != null && chunkSize != firmwareChunk.length) {
scheduler.schedule(() -> processDisconnect(ctx), 60, TimeUnit.SECONDS); scheduler.schedule(() -> processDisconnect(ctx), 60, TimeUnit.SECONDS);
@ -504,6 +516,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC: case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC:
case MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC: case MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC:
case MqttTopics.DEVICE_FIRMWARE_ERROR_TOPIC: case MqttTopics.DEVICE_FIRMWARE_ERROR_TOPIC:
case MqttTopics.DEVICE_SOFTWARE_RESPONSES_TOPIC:
case MqttTopics.DEVICE_SOFTWARE_ERROR_TOPIC:
registerSubQoS(topic, grantedQoSList, reqQoS); registerSubQoS(topic, grantedQoSList, reqQoS);
break; break;
default: default:

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

@ -30,6 +30,7 @@ import lombok.extern.slf4j.Slf4j;
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.device.profile.MqttTopics; import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
@ -43,6 +44,9 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka
*/ */
@ -151,8 +155,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
} }
@Override @Override
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) { public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) {
return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/chunk/" + chunk, firmwareChunk)); return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk));
} }
public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {

3
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java

@ -23,6 +23,7 @@ import io.netty.handler.codec.mqtt.MqttMessage;
import io.netty.handler.codec.mqtt.MqttMessageType; import io.netty.handler.codec.mqtt.MqttMessageType;
import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttPublishMessage;
import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
@ -77,7 +78,7 @@ public interface MqttTransportAdaptor {
Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException; Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException;
Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException; Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException;
default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) { default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) {
MqttFixedHeader mqttFixedHeader = MqttFixedHeader mqttFixedHeader =

7
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java

@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
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.device.profile.MqttTopics; import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.common.transport.adaptor.ProtoConverter; import org.thingsboard.server.common.transport.adaptor.ProtoConverter;
@ -38,6 +39,8 @@ import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionConte
import java.util.Optional; import java.util.Optional;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT;
@Component @Component
@Slf4j @Slf4j
public class ProtoMqttAdaptor implements MqttTransportAdaptor { public class ProtoMqttAdaptor implements MqttTransportAdaptor {
@ -165,8 +168,8 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
} }
@Override @Override
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException { public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException {
return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/" + chunk, firmwareChunk)); return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk));
} }
@Override @Override

4
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java

@ -51,11 +51,13 @@ public abstract class TransportContext {
@Getter @Getter
private ExecutorService executor; private ExecutorService executor;
@Getter @Getter
@Autowired @Autowired
private FirmwareDataCache firmwareDataCache; private FirmwareDataCache firmwareDataCache;
@Autowired
private TransportResourceCache transportResourceCache;
@PostConstruct @PostConstruct
public void init() { public void init() {
executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass()); executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass());

217
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java

@ -1,217 +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.common.transport.lwm2m;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
@Slf4j
@Component
@ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'")
public class LwM2MTransportConfigServer {
@Getter
private String KEY_STORE_DEFAULT_RESOURCE_PATH = "credentials";
@Getter
private String KEY_STORE_DEFAULT_FILE = "serverKeyStore.jks";
@Getter
private String APP_DIR = "common";
@Getter
private String TRANSPORT_DIR = "transport";
@Getter
private String LWM2M_DIR = "lwm2m";
@Getter
private String SRC_DIR = "src";
@Getter
private String MAIN_DIR = "main";
@Getter
private String RESOURCES_DIR = "resources";
@Getter
private String BASE_DIR_PATH = System.getProperty("user.dir");
@Getter
// private String PATH_DATA_MICROSERVICE = "/usr/share/tb-lwm2m-transport/data$";
private String PATH_DATA = "data";
@Getter
@Setter
private LwM2mModelProvider modelProvider;
@Getter
@Value("${transport.lwm2m.timeout:}")
private Long timeout;
@Getter
@Value("${transport.sessions.report_timeout}")
private long sessionReportTimeout;
@Getter
@Value("${transport.lwm2m.recommended_ciphers:}")
private boolean recommendedCiphers;
@Getter
@Value("${transport.lwm2m.recommended_supported_groups:}")
private boolean recommendedSupportedGroups;
@Getter
@Value("${transport.lwm2m.response_pool_size:}")
private int responsePoolSize;
@Getter
@Value("${transport.lwm2m.registered_pool_size:}")
private int registeredPoolSize;
@Getter
@Value("${transport.lwm2m.update_registered_pool_size:}")
private int updateRegisteredPoolSize;
@Getter
@Value("${transport.lwm2m.un_registered_pool_size:}")
private int unRegisteredPoolSize;
@Getter
@Value("${transport.lwm2m.secure.key_store_type:}")
private String keyStoreType;
@Getter
@Value("${transport.lwm2m.secure.key_store_path_file:}")
private String keyStorePathFile;
@Getter
@Setter
private KeyStore keyStoreValue;
@Getter
@Value("${transport.lwm2m.secure.key_store_password:}")
private String keyStorePasswordServer;
@Getter
@Value("${transport.lwm2m.secure.root_alias:}")
private String rootAlias;
@Getter
@Value("${transport.lwm2m.secure.enable_gen_new_key_psk_rpk:}")
private Boolean enableGenNewKeyPskRpk;
@Getter
@Value("${transport.lwm2m.server.id:}")
private Integer serverId;
@Getter
@Value("${transport.lwm2m.server.bind_address:}")
private String serverHost;
@Getter
@Value("${transport.lwm2m.server.secure.bind_address_security:}")
private String serverHostSecurity;
@Getter
@Value("${transport.lwm2m.server.bind_port_no_sec:}")
private Integer serverPortNoSec;
@Getter
@Value("${transport.lwm2m.server.secure.bind_port_security:}")
private Integer serverPortSecurity;
@Getter
@Value("${transport.lwm2m.server.secure.public_x:}")
private String serverPublicX;
@Getter
@Value("${transport.lwm2m.server.secure.public_y:}")
private String serverPublicY;
@Getter
@Value("${transport.lwm2m.server.secure.private_encoded:}")
private String serverPrivateEncoded;
@Getter
@Value("${transport.lwm2m.server.secure.alias:}")
private String serverAlias;
@PostConstruct
public void init() {
this.getInKeyStore();
}
private KeyStore getInKeyStore() {
try {
if (keyStoreValue != null && keyStoreValue.size() > 0)
return keyStoreValue;
} catch (KeyStoreException e) {
log.error("Uninitialized keystore [{}]", keyStoreValue.toString());
}
Path keyStorePath = (keyStorePathFile != null && !keyStorePathFile.isEmpty()) ? Paths.get(keyStorePathFile) :
(new File(Paths.get(getBaseDirPath(), PATH_DATA, KEY_STORE_DEFAULT_RESOURCE_PATH, KEY_STORE_DEFAULT_FILE).toUri()).isFile()) ?
Paths.get(getBaseDirPath(), PATH_DATA, KEY_STORE_DEFAULT_RESOURCE_PATH, KEY_STORE_DEFAULT_FILE) :
Paths.get(getBaseDirPath(), APP_DIR, TRANSPORT_DIR, LWM2M_DIR, SRC_DIR, MAIN_DIR, RESOURCES_DIR, KEY_STORE_DEFAULT_RESOURCE_PATH, KEY_STORE_DEFAULT_FILE);
File keyStoreFile = new File(keyStorePath.toUri());
if (keyStoreFile.isFile()) {
try {
InputStream inKeyStore = new FileInputStream(keyStoreFile);
keyStoreValue = KeyStore.getInstance(keyStoreType);
keyStoreValue.load(inKeyStore, keyStorePasswordServer == null ? null : keyStorePasswordServer.toCharArray());
} catch (CertificateException | NoSuchAlgorithmException | IOException | KeyStoreException e) {
log.error("[{}] Unable to load KeyStore files server, folder is not a directory", e.getMessage());
keyStoreValue = null;
}
log.info("[{}] Load KeyStore files server, folder is a directory", keyStoreFile.getAbsoluteFile());
} else {
log.error("[{}] Unable to load KeyStore files server, is not a file", keyStoreFile.getAbsoluteFile());
keyStoreValue = null;
}
return keyStoreValue;
}
private String getBaseDirPath() {
Path FULL_FILE_PATH;
if (BASE_DIR_PATH.endsWith("bin")) {
FULL_FILE_PATH = Paths.get(BASE_DIR_PATH.replaceAll("bin$", ""));
} else if (BASE_DIR_PATH.endsWith("conf")) {
FULL_FILE_PATH = Paths.get(BASE_DIR_PATH.replaceAll("conf$", ""));
} else if (BASE_DIR_PATH.endsWith("application")) {
FULL_FILE_PATH = Paths.get(BASE_DIR_PATH.substring(0, BASE_DIR_PATH.length() - "application".length()));
} else {
FULL_FILE_PATH = Paths.get(BASE_DIR_PATH);
}
return FULL_FILE_PATH.toUri().getPath();
}
}

2
dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java

@ -83,6 +83,8 @@ public interface DeviceDao extends Dao<Device>, TenantEntityDao {
PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink); PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink);
PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink);
/** /**
* Find device infos by tenantId, type and page link. * Find device infos by tenantId, type and page link.
* *

23
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfilePr
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
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.PageData;
@ -406,9 +407,31 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
if (firmware == null) { if (firmware == null) {
throw new DataValidationException("Can't assign non-existent firmware!"); throw new DataValidationException("Can't assign non-existent firmware!");
} }
if (!firmware.getType().equals(FirmwareType.FIRMWARE)) {
throw new DataValidationException("Can't assign firmware with type: " + firmware.getType());
}
if (firmware.getData() == null) { if (firmware.getData() == null) {
throw new DataValidationException("Can't assign firmware with empty data!"); throw new DataValidationException("Can't assign firmware with empty data!");
} }
if (!firmware.getDeviceProfileId().equals(deviceProfile.getId())) {
throw new DataValidationException("Can't assign firmware with different deviceProfile!");
}
}
if (deviceProfile.getSoftwareId() != null) {
Firmware software = firmwareService.findFirmwareById(tenantId, deviceProfile.getSoftwareId());
if (software == null) {
throw new DataValidationException("Can't assign non-existent software!");
}
if (!software.getType().equals(FirmwareType.SOFTWARE)) {
throw new DataValidationException("Can't assign software with type: " + software.getType());
}
if (software.getData() == null) {
throw new DataValidationException("Can't assign software with empty data!");
}
if (!software.getDeviceProfileId().equals(deviceProfile.getId())) {
throw new DataValidationException("Can't assign firmware with different deviceProfile!");
}
} }
} }

34
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfig
import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
@ -361,13 +362,22 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Override @Override
public PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink) { public PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); log.trace("Executing findDevicesByTenantIdAndTypeAndEmptyFirmware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type); validateString(type, "Incorrect type " + type);
validatePageLink(pageLink); validatePageLink(pageLink);
return deviceDao.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId.getId(), type, pageLink); return deviceDao.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId.getId(), type, pageLink);
} }
@Override
public PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndTypeAndEmptySoftware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink);
return deviceDao.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId.getId(), type, pageLink);
}
@Override @Override
public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
@ -696,9 +706,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
if (firmware == null) { if (firmware == null) {
throw new DataValidationException("Can't assign non-existent firmware!"); throw new DataValidationException("Can't assign non-existent firmware!");
} }
if (!firmware.getType().equals(FirmwareType.FIRMWARE)) {
throw new DataValidationException("Can't assign firmware with type: " + firmware.getType());
}
if (firmware.getData() == null) { if (firmware.getData() == null) {
throw new DataValidationException("Can't assign firmware with empty data!"); throw new DataValidationException("Can't assign firmware with empty data!");
} }
if (!firmware.getDeviceProfileId().equals(device.getDeviceProfileId())) {
throw new DataValidationException("Can't assign firmware with different deviceProfile!");
}
}
if (device.getSoftwareId() != null) {
Firmware software = firmwareService.findFirmwareById(tenantId, device.getSoftwareId());
if (software == null) {
throw new DataValidationException("Can't assign non-existent software!");
}
if (!software.getType().equals(FirmwareType.SOFTWARE)) {
throw new DataValidationException("Can't assign software with type: " + software.getType());
}
if (software.getData() == null) {
throw new DataValidationException("Can't assign software with empty data!");
}
if (!software.getDeviceProfileId().equals(device.getDeviceProfileId())) {
throw new DataValidationException("Can't assign firmware with different deviceProfile!");
}
} }
} }
}; };

96
dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java

@ -27,13 +27,17 @@ import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.firmware.FirmwareDataCache; import org.thingsboard.server.cache.firmware.FirmwareDataCache;
import org.thingsboard.server.common.data.DeviceProfile;
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.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.firmware.FirmwareType;
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.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.device.DeviceProfileDao;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.PaginatedRemover;
@ -56,6 +60,7 @@ public class BaseFirmwareService implements FirmwareService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
private final TenantDao tenantDao; private final TenantDao tenantDao;
private final DeviceProfileDao deviceProfileDao;
private final FirmwareDao firmwareDao; private final FirmwareDao firmwareDao;
private final FirmwareInfoDao firmwareInfoDao; private final FirmwareInfoDao firmwareInfoDao;
private final CacheManager cacheManager; private final CacheManager cacheManager;
@ -124,7 +129,8 @@ public class BaseFirmwareService implements FirmwareService {
public ListenableFuture<FirmwareInfo> findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId) { public ListenableFuture<FirmwareInfo> findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId) {
log.trace("Executing findFirmwareInfoByIdAsync [{}]", firmwareId); log.trace("Executing findFirmwareInfoByIdAsync [{}]", firmwareId);
validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId);
return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId()); } return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId());
}
@Override @Override
public PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) { public PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) {
@ -135,11 +141,11 @@ public class BaseFirmwareService implements FirmwareService {
} }
@Override @Override
public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) { public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) {
log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validatePageLink(pageLink); validatePageLink(pageLink);
return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink); return firmwareInfoDao.findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, firmwareType, hasData, pageLink);
} }
@Override @Override
@ -157,6 +163,10 @@ public class BaseFirmwareService implements FirmwareService {
throw new DataValidationException("The firmware referenced by the devices cannot be deleted!"); throw new DataValidationException("The firmware referenced by the devices cannot be deleted!");
} else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) { } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) {
throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!"); throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!");
} else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device")) {
throw new DataValidationException("The software referenced by the devices cannot be deleted!");
} else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device_profile")) {
throw new DataValidationException("The software referenced by the device profile cannot be deleted!");
} else { } else {
throw t; throw t;
} }
@ -173,29 +183,15 @@ public class BaseFirmwareService implements FirmwareService {
private DataValidator<FirmwareInfo> firmwareInfoValidator = new DataValidator<>() { private DataValidator<FirmwareInfo> firmwareInfoValidator = new DataValidator<>() {
@Override @Override
protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmware) { protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmwareInfo) {
if (firmware.getTenantId() == null) { validateImpl(firmwareInfo);
throw new DataValidationException("Firmware should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Firmware is referencing to non-existent tenant!");
}
}
if (StringUtils.isEmpty(firmware.getTitle())) {
throw new DataValidationException("Firmware title should be specified!");
}
if (StringUtils.isEmpty(firmware.getVersion())) {
throw new DataValidationException("Firmware version should be specified!");
}
} }
@Override @Override
protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) { protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) {
FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId()); FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId());
validateUpdateDeviceProfile(firmware, firmwareOld);
BaseFirmwareService.validateUpdate(firmware, firmwareOld); BaseFirmwareService.validateUpdate(firmware, firmwareOld);
} }
}; };
@ -204,22 +200,7 @@ public class BaseFirmwareService implements FirmwareService {
@Override @Override
protected void validateDataImpl(TenantId tenantId, Firmware firmware) { protected void validateDataImpl(TenantId tenantId, Firmware firmware) {
if (firmware.getTenantId() == null) { validateImpl(firmware);
throw new DataValidationException("Firmware should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Firmware is referencing to non-existent tenant!");
}
}
if (StringUtils.isEmpty(firmware.getTitle())) {
throw new DataValidationException("Firmware title should be specified!");
}
if (StringUtils.isEmpty(firmware.getVersion())) {
throw new DataValidationException("Firmware version should be specified!");
}
if (StringUtils.isEmpty(firmware.getFileName())) { if (StringUtils.isEmpty(firmware.getFileName())) {
throw new DataValidationException("Firmware file name should be specified!"); throw new DataValidationException("Firmware file name should be specified!");
@ -267,6 +248,7 @@ public class BaseFirmwareService implements FirmwareService {
protected void validateUpdate(TenantId tenantId, Firmware firmware) { protected void validateUpdate(TenantId tenantId, Firmware firmware) {
Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId()); Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId());
validateUpdateDeviceProfile(firmware, firmwareOld);
BaseFirmwareService.validateUpdate(firmware, firmwareOld); BaseFirmwareService.validateUpdate(firmware, firmwareOld);
if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) { if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) {
@ -275,7 +257,19 @@ public class BaseFirmwareService implements FirmwareService {
} }
}; };
private void validateUpdateDeviceProfile(FirmwareInfo firmware, FirmwareInfo firmwareOld) {
if (firmwareOld.getDeviceProfileId() != null && !firmwareOld.getDeviceProfileId().equals(firmware.getDeviceProfileId())) {
if (firmwareInfoDao.isFirmwareUsed(firmwareOld.getId(), firmware.getType(), firmwareOld.getDeviceProfileId())) {
throw new DataValidationException("Can`t update deviceProfileId because firmware is already in use!");
}
}
}
private static void validateUpdate(FirmwareInfo firmware, FirmwareInfo firmwareOld) { private static void validateUpdate(FirmwareInfo firmware, FirmwareInfo firmwareOld) {
if (!firmwareOld.getType().equals(firmware.getType())) {
throw new DataValidationException("Updating type is prohibited!");
}
if (!firmwareOld.getTitle().equals(firmware.getTitle())) { if (!firmwareOld.getTitle().equals(firmware.getTitle())) {
throw new DataValidationException("Updating firmware title is prohibited!"); throw new DataValidationException("Updating firmware title is prohibited!");
} }
@ -305,6 +299,36 @@ public class BaseFirmwareService implements FirmwareService {
} }
} }
private void validateImpl(FirmwareInfo firmwareInfo) {
if (firmwareInfo.getTenantId() == null) {
throw new DataValidationException("Firmware should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Firmware is referencing to non-existent tenant!");
}
}
if (firmwareInfo.getDeviceProfileId() != null) {
DeviceProfile deviceProfile = deviceProfileDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getDeviceProfileId().getId());
if (deviceProfile == null) {
throw new DataValidationException("Firmware is referencing to non-existent device profile!");
}
}
if (firmwareInfo.getType() == null) {
throw new DataValidationException("Type should be specified!");
}
if (StringUtils.isEmpty(firmwareInfo.getTitle())) {
throw new DataValidationException("Firmware title should be specified!");
}
if (StringUtils.isEmpty(firmwareInfo.getVersion())) {
throw new DataValidationException("Firmware version should be specified!");
}
}
private PaginatedRemover<TenantId, FirmwareInfo> tenantFirmwareRemover = private PaginatedRemover<TenantId, FirmwareInfo> tenantFirmwareRemover =
new PaginatedRemover<>() { new PaginatedRemover<>() {

7
dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java

@ -16,6 +16,9 @@
package org.thingsboard.server.dao.firmware; package org.thingsboard.server.dao.firmware;
import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
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.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
@ -27,6 +30,8 @@ public interface FirmwareInfoDao extends Dao<FirmwareInfo> {
PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink); PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink);
PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink); PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink);
boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId);
} }

4
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -154,6 +154,7 @@ public class ModelConstants {
public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id"; public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id";
public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data"; public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data";
public static final String DEVICE_FIRMWARE_ID_PROPERTY = "firmware_id"; public static final String DEVICE_FIRMWARE_ID_PROPERTY = "firmware_id";
public static final String DEVICE_SOFTWARE_ID_PROPERTY = "software_id";
public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text"; public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text"; public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
@ -178,6 +179,7 @@ public class ModelConstants {
public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name"; public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name";
public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key"; public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key";
public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id"; public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id";
public static final String DEVICE_PROFILE_SOFTWARE_ID_PROPERTY = "software_id";
/** /**
* Cassandra entityView constants. * Cassandra entityView constants.
@ -476,6 +478,8 @@ public class ModelConstants {
*/ */
public static final String FIRMWARE_TABLE_NAME = "firmware"; public static final String FIRMWARE_TABLE_NAME = "firmware";
public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN; public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN;
public static final String FIRMWARE_DEVICE_PROFILE_ID_COLUMN = "device_profile_id";
public static final String FIRMWARE_TYPE_COLUMN = "type";
public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY; public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY;
public static final String FIRMWARE_VERSION_COLUMN = "version"; public static final String FIRMWARE_VERSION_COLUMN = "version";
public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name"; public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name";

16
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java

@ -22,6 +22,7 @@ import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs; import org.hibernate.annotations.TypeDefs;
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.device.data.DeviceData; import org.thingsboard.server.common.data.device.data.DeviceData;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
@ -32,7 +33,6 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.dao.util.mapping.JsonBinaryType; import org.thingsboard.server.dao.util.mapping.JsonBinaryType;
import org.thingsboard.server.dao.util.mapping.JsonStringType; import org.thingsboard.server.dao.util.mapping.JsonStringType;
@ -43,8 +43,8 @@ import java.util.UUID;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@TypeDefs({ @TypeDefs({
@TypeDef(name = "json", typeClass = JsonStringType.class), @TypeDef(name = "json", typeClass = JsonStringType.class),
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
}) })
@MappedSuperclass @MappedSuperclass
public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEntity<T> implements SearchTextEntity<T> { public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEntity<T> implements SearchTextEntity<T> {
@ -77,6 +77,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
@Column(name = ModelConstants.DEVICE_FIRMWARE_ID_PROPERTY, columnDefinition = "uuid") @Column(name = ModelConstants.DEVICE_FIRMWARE_ID_PROPERTY, columnDefinition = "uuid")
private UUID firmwareId; private UUID firmwareId;
@Column(name = ModelConstants.DEVICE_SOFTWARE_ID_PROPERTY, columnDefinition = "uuid")
private UUID softwareId;
@Type(type = "jsonb") @Type(type = "jsonb")
@Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb") @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb")
private JsonNode deviceData; private JsonNode deviceData;
@ -102,6 +105,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
if (device.getFirmwareId() != null) { if (device.getFirmwareId() != null) {
this.firmwareId = device.getFirmwareId().getId(); this.firmwareId = device.getFirmwareId().getId();
} }
if (device.getSoftwareId() != null) {
this.softwareId = device.getSoftwareId().getId();
}
this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class); this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class);
this.name = device.getName(); this.name = device.getName();
this.type = device.getType(); this.type = device.getType();
@ -122,6 +128,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
this.searchText = deviceEntity.getSearchText(); this.searchText = deviceEntity.getSearchText();
this.additionalInfo = deviceEntity.getAdditionalInfo(); this.additionalInfo = deviceEntity.getAdditionalInfo();
this.firmwareId = deviceEntity.getFirmwareId(); this.firmwareId = deviceEntity.getFirmwareId();
this.softwareId = deviceEntity.getSoftwareId();
} }
@Override @Override
@ -149,6 +156,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
if (firmwareId != null) { if (firmwareId != null) {
device.setFirmwareId(new FirmwareId(firmwareId)); device.setFirmwareId(new FirmwareId(firmwareId));
} }
if (softwareId != null) {
device.setSoftwareId(new FirmwareId(softwareId));
}
device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class)); device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class));
device.setName(name); device.setName(name);
device.setType(type); device.setType(type);

18
dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java

@ -21,9 +21,10 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
@ -33,7 +34,6 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.dao.util.mapping.JsonBinaryType; import org.thingsboard.server.dao.util.mapping.JsonBinaryType;
import javax.persistence.Column; import javax.persistence.Column;
@ -87,12 +87,15 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
@Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb") @Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb")
private JsonNode profileData; private JsonNode profileData;
@Column(name=ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY) @Column(name = ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY)
private String provisionDeviceKey; private String provisionDeviceKey;
@Column(name=ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY) @Column(name = ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY)
private UUID firmwareId; private UUID firmwareId;
@Column(name = ModelConstants.DEVICE_PROFILE_SOFTWARE_ID_PROPERTY)
private UUID softwareId;
public DeviceProfileEntity() { public DeviceProfileEntity() {
super(); super();
} }
@ -120,6 +123,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
if (deviceProfile.getFirmwareId() != null) { if (deviceProfile.getFirmwareId() != null) {
this.firmwareId = deviceProfile.getFirmwareId().getId(); this.firmwareId = deviceProfile.getFirmwareId().getId();
} }
if (deviceProfile.getSoftwareId() != null) {
this.firmwareId = deviceProfile.getSoftwareId().getId();
}
} }
@Override @Override
@ -160,6 +166,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
deviceProfile.setFirmwareId(new FirmwareId(firmwareId)); deviceProfile.setFirmwareId(new FirmwareId(firmwareId));
} }
if (softwareId != null) {
deviceProfile.setSoftwareId(new FirmwareId(softwareId));
}
return deviceProfile; return deviceProfile;
} }
} }

21
dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java

@ -21,6 +21,8 @@ import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.firmware.FirmwareType;
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.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.BaseSqlEntity;
@ -30,6 +32,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Lob; import javax.persistence.Lob;
import javax.persistence.Table; import javax.persistence.Table;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -40,10 +44,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
@ -57,6 +63,13 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
@Column(name = FIRMWARE_TENANT_ID_COLUMN) @Column(name = FIRMWARE_TENANT_ID_COLUMN)
private UUID tenantId; private UUID tenantId;
@Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN)
private UUID deviceProfileId;
@Enumerated(EnumType.STRING)
@Column(name = FIRMWARE_TYPE_COLUMN)
private FirmwareType type;
@Column(name = FIRMWARE_TITLE_COLUMN) @Column(name = FIRMWARE_TITLE_COLUMN)
private String title; private String title;
@ -97,6 +110,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
this.createdTime = firmware.getCreatedTime(); this.createdTime = firmware.getCreatedTime();
this.setUuid(firmware.getUuidId()); this.setUuid(firmware.getUuidId());
this.tenantId = firmware.getTenantId().getId(); this.tenantId = firmware.getTenantId().getId();
if (firmware.getDeviceProfileId() != null) {
this.deviceProfileId = firmware.getDeviceProfileId().getId();
}
this.type = firmware.getType();
this.title = firmware.getTitle(); this.title = firmware.getTitle();
this.version = firmware.getVersion(); this.version = firmware.getVersion();
this.fileName = firmware.getFileName(); this.fileName = firmware.getFileName();
@ -123,6 +140,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex
Firmware firmware = new Firmware(new FirmwareId(id)); Firmware firmware = new Firmware(new FirmwareId(id));
firmware.setCreatedTime(createdTime); firmware.setCreatedTime(createdTime);
firmware.setTenantId(new TenantId(tenantId)); firmware.setTenantId(new TenantId(tenantId));
if (deviceProfileId != null) {
firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
}
firmware.setType(type);
firmware.setTitle(title); firmware.setTitle(title);
firmware.setVersion(version); firmware.setVersion(version);
firmware.setFileName(fileName); firmware.setFileName(fileName);

27
dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java

@ -22,6 +22,8 @@ import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.firmware.FirmwareType;
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.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.BaseSqlEntity;
@ -31,6 +33,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Transient; import javax.persistence.Transient;
import java.util.UUID; import java.util.UUID;
@ -38,13 +42,13 @@ import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_HAS_DATA_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
@ -58,6 +62,13 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
@Column(name = FIRMWARE_TENANT_ID_COLUMN) @Column(name = FIRMWARE_TENANT_ID_COLUMN)
private UUID tenantId; private UUID tenantId;
@Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN)
private UUID deviceProfileId;
@Enumerated(EnumType.STRING)
@Column(name = FIRMWARE_TYPE_COLUMN)
private FirmwareType type;
@Column(name = FIRMWARE_TITLE_COLUMN) @Column(name = FIRMWARE_TITLE_COLUMN)
private String title; private String title;
@ -97,6 +108,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
this.createdTime = firmware.getCreatedTime(); this.createdTime = firmware.getCreatedTime();
this.setUuid(firmware.getUuidId()); this.setUuid(firmware.getUuidId());
this.tenantId = firmware.getTenantId().getId(); this.tenantId = firmware.getTenantId().getId();
this.type = firmware.getType();
if (firmware.getDeviceProfileId() != null) {
this.deviceProfileId = firmware.getDeviceProfileId().getId();
}
this.title = firmware.getTitle(); this.title = firmware.getTitle();
this.version = firmware.getVersion(); this.version = firmware.getVersion();
this.fileName = firmware.getFileName(); this.fileName = firmware.getFileName();
@ -107,12 +122,14 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
this.additionalInfo = firmware.getAdditionalInfo(); this.additionalInfo = firmware.getAdditionalInfo();
} }
public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, String title, String version, public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, FirmwareType type, String title, String version,
String fileName, String contentType, String checksumAlgorithm, String checksum, Long dataSize, String fileName, String contentType, String checksumAlgorithm, String checksum, Long dataSize,
Object additionalInfo, boolean hasData) { Object additionalInfo, boolean hasData) {
this.id = id; this.id = id;
this.createdTime = createdTime; this.createdTime = createdTime;
this.tenantId = tenantId; this.tenantId = tenantId;
this.deviceProfileId = deviceProfileId;
this.type = type;
this.title = title; this.title = title;
this.version = version; this.version = version;
this.fileName = fileName; this.fileName = fileName;
@ -139,6 +156,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S
FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id)); FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id));
firmware.setCreatedTime(createdTime); firmware.setCreatedTime(createdTime);
firmware.setTenantId(new TenantId(tenantId)); firmware.setTenantId(new TenantId(tenantId));
if (deviceProfileId != null) {
firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId));
}
firmware.setType(type);
firmware.setTitle(title); firmware.setTitle(title);
firmware.setVersion(version); firmware.setVersion(version);
firmware.setFileName(fileName); firmware.setFileName(fileName);

9
dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java

@ -104,6 +104,15 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
@Param("textSearch") String textSearch, @Param("textSearch") String textSearch,
Pageable pageable); Pageable pageable);
@Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " +
"AND d.type = :type " +
"AND d.softwareId = null " +
"AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
Page<DeviceEntity> findByTenantIdAndTypeAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId,
@Param("type") String type,
@Param("textSearch") String textSearch,
Pageable pageable);
@Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
"FROM DeviceEntity d " + "FROM DeviceEntity d " +
"LEFT JOIN CustomerEntity c on c.id = d.customerId " + "LEFT JOIN CustomerEntity c on c.id = d.customerId " +

10
dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java

@ -164,6 +164,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
DaoUtil.toPageable(pageLink))); DaoUtil.toPageable(pageLink)));
} }
@Override
public PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink) {
return DaoUtil.toPageData(
deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull(
tenantId,
type,
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
@Override @Override
public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) { public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) {
return DaoUtil.toPageData( return DaoUtil.toPageData(

28
dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java

@ -20,27 +20,41 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity; import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity;
import java.util.UUID; import java.util.UUID;
public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> { public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> {
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
"f.tenantId = :tenantId " + "f.tenantId = :tenantId " +
"AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId, Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId,
@Param("searchText") String searchText, @Param("searchText") String searchText,
Pageable pageable); Pageable pageable);
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " +
"f.tenantId = :tenantId " + "f.tenantId = :tenantId " +
"AND f.deviceProfileId = :deviceProfileId " +
"AND f.type = :type " +
"AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" +
"AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
Page<FirmwareInfoEntity> findAllByTenantIdAndHasData(@Param("tenantId") UUID tenantId, Page<FirmwareInfoEntity> findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId,
@Param("hasData") boolean hasData, @Param("deviceProfileId") UUID deviceProfileId,
@Param("searchText") String searchText, @Param("type") FirmwareType type,
Pageable pageable); @Param("hasData") boolean hasData,
@Param("searchText") String searchText,
Pageable pageable);
@Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id") @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id")
FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id); FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id);
@Query(value = "SELECT exists(SELECT * " +
"FROM device_profile AS dp " +
"LEFT JOIN device AS d ON dp.id = d.device_profile_id " +
"WHERE dp.id = :deviceProfileId AND " +
"(('FIRMWARE' = :type AND (dp.firmware_id = :firmwareId OR d.firmware_id = :firmwareId)) " +
"OR ('SOFTWARE' = :type AND (dp.software_id = :firmwareId or d.software_id = :firmwareId))))", nativeQuery = true)
boolean isFirmwareUsed(@Param("firmwareId") UUID firmwareId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") String type);
} }

14
dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java

@ -20,6 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
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.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
@ -73,12 +76,19 @@ public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao<FirmwareInfoEnt
} }
@Override @Override
public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) { public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) {
return DaoUtil.toPageData(firmwareInfoRepository return DaoUtil.toPageData(firmwareInfoRepository
.findAllByTenantIdAndHasData( .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(
tenantId.getId(), tenantId.getId(),
deviceProfileId.getId(),
firmwareType,
hasData, hasData,
Objects.toString(pageLink.getTextSearch(), ""), Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink))); DaoUtil.toPageable(pageLink)));
} }
@Override
public boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId) {
return firmwareInfoRepository.isFirmwareUsed(firmwareId.getId(), deviceProfileId.getId(), type.name());
}
} }

10
dao/src/main/resources/sql/schema-entities-hsql.sql

@ -162,6 +162,8 @@ CREATE TABLE IF NOT EXISTS firmware (
id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
created_time bigint NOT NULL, created_time bigint NOT NULL,
tenant_id uuid NOT NULL, tenant_id uuid NOT NULL,
device_profile_id uuid ,
type varchar(32) NOT NULL,
title varchar(255) NOT NULL, title varchar(255) NOT NULL,
version varchar(255) NOT NULL, version varchar(255) NOT NULL,
file_name varchar(255), file_name varchar(255),
@ -188,13 +190,15 @@ CREATE TABLE IF NOT EXISTS device_profile (
is_default boolean, is_default boolean,
tenant_id uuid, tenant_id uuid,
firmware_id uuid, firmware_id uuid,
software_id uuid,
default_rule_chain_id uuid, default_rule_chain_id uuid,
default_queue_name varchar(255), default_queue_name varchar(255),
provision_device_key varchar, provision_device_key varchar,
CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id) CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id),
CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id)
); );
CREATE TABLE IF NOT EXISTS device ( CREATE TABLE IF NOT EXISTS device (
@ -210,9 +214,11 @@ CREATE TABLE IF NOT EXISTS device (
search_text varchar(255), search_text varchar(255),
tenant_id uuid, tenant_id uuid,
firmware_id uuid, firmware_id uuid,
software_id uuid,
CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id) CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id),
CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id)
); );
CREATE TABLE IF NOT EXISTS device_credentials ( CREATE TABLE IF NOT EXISTS device_credentials (

1
dao/src/main/resources/sql/schema-entities-idx.sql

@ -45,3 +45,4 @@ CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type);
CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc); CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);
CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time); CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time);

20
dao/src/main/resources/sql/schema-entities.sql

@ -180,6 +180,8 @@ CREATE TABLE IF NOT EXISTS firmware (
id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
created_time bigint NOT NULL, created_time bigint NOT NULL,
tenant_id uuid NOT NULL, tenant_id uuid NOT NULL,
device_profile_id uuid ,
type varchar(32) NOT NULL,
title varchar(255) NOT NULL, title varchar(255) NOT NULL,
version varchar(255) NOT NULL, version varchar(255) NOT NULL,
file_name varchar(255), file_name varchar(255),
@ -191,6 +193,7 @@ CREATE TABLE IF NOT EXISTS firmware (
additional_info varchar, additional_info varchar,
search_text varchar(255), search_text varchar(255),
CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
-- CONSTRAINT fk_device_profile_firmware FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE
); );
CREATE TABLE IF NOT EXISTS device_profile ( CREATE TABLE IF NOT EXISTS device_profile (
@ -206,15 +209,26 @@ CREATE TABLE IF NOT EXISTS device_profile (
is_default boolean, is_default boolean,
tenant_id uuid, tenant_id uuid,
firmware_id uuid, firmware_id uuid,
software_id uuid,
default_rule_chain_id uuid, default_rule_chain_id uuid,
default_queue_name varchar(255), default_queue_name varchar(255),
provision_device_key varchar, provision_device_key varchar,
CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id) CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id),
CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id)
); );
-- We will use one-to-many relation in the first release and extend this feature in case of user requests
-- CREATE TABLE IF NOT EXISTS device_profile_firmware (
-- device_profile_id uuid NOT NULL,
-- firmware_id uuid NOT NULL,
-- CONSTRAINT device_profile_firmware_unq_key UNIQUE (device_profile_id, firmware_id),
-- CONSTRAINT fk_device_profile_firmware_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE,
-- CONSTRAINT fk_device_profile_firmware_firmware FOREIGN KEY (firmware_id) REFERENCES firmware(id) ON DELETE CASCADE,
-- );
CREATE TABLE IF NOT EXISTS device ( CREATE TABLE IF NOT EXISTS device (
id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
created_time bigint NOT NULL, created_time bigint NOT NULL,
@ -228,9 +242,11 @@ CREATE TABLE IF NOT EXISTS device (
search_text varchar(255), search_text varchar(255),
tenant_id uuid, tenant_id uuid,
firmware_id uuid, firmware_id uuid,
software_id uuid,
CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id) CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id),
CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id)
); );
CREATE TABLE IF NOT EXISTS device_credentials ( CREATE TABLE IF NOT EXISTS device_credentials (

5
dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java

@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.firmware.FirmwareType;
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.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
@ -43,6 +44,8 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
public class BaseDeviceProfileServiceTest extends AbstractServiceTest { public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
private IdComparator<DeviceProfile> idComparator = new IdComparator<>(); private IdComparator<DeviceProfile> idComparator = new IdComparator<>();
@ -97,6 +100,8 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(savedDeviceProfile.getId());
firmware.setType(FIRMWARE);
firmware.setTitle("my firmware"); firmware.setTitle("my firmware");
firmware.setVersion("v1.0"); firmware.setVersion("v1.0");
firmware.setFileName("test.txt"); firmware.setFileName("test.txt");

43
dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java

@ -20,10 +20,13 @@ import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.thingsboard.server.common.data.Customer; 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.DeviceInfo; import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
@ -42,6 +45,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
public abstract class BaseDeviceServiceTest extends AbstractServiceTest { public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
@ -66,6 +70,9 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
tenantProfileService.deleteTenantProfiles(anotherTenantId); tenantProfileService.deleteTenantProfiles(anotherTenantId);
} }
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test @Test
public void testSaveDevicesWithoutMaxDeviceLimit() { public void testSaveDevicesWithoutMaxDeviceLimit() {
Device device = this.saveDevice(tenantId, "My device"); Device device = this.saveDevice(tenantId, "My device");
@ -183,6 +190,8 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(device.getDeviceProfileId());
firmware.setType(FIRMWARE);
firmware.setTitle("my firmware"); firmware.setTitle("my firmware");
firmware.setVersion("v1.0"); firmware.setVersion("v1.0");
firmware.setFileName("test.txt"); firmware.setFileName("test.txt");
@ -198,6 +207,40 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
Device foundDevice = deviceService.findDeviceById(tenantId, savedDevice.getId()); Device foundDevice = deviceService.findDeviceById(tenantId, savedDevice.getId());
Assert.assertEquals(foundDevice.getName(), savedDevice.getName()); Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
} }
@Test
public void testAssignFirmwareToDeviceWithDifferentDeviceProfile() {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device");
device.setType("default");
Device savedDevice = deviceService.saveDevice(device);
Assert.assertNotNull(savedDevice);
DeviceProfile deviceProfile = createDeviceProfile(tenantId, "New device Profile");
DeviceProfile savedProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
Assert.assertNotNull(savedProfile);
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(savedProfile.getId());
firmware.setType(FIRMWARE);
firmware.setTitle("my firmware");
firmware.setVersion("v1.0");
firmware.setFileName("test.txt");
firmware.setContentType("text/plain");
firmware.setChecksumAlgorithm("sha256");
firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a");
firmware.setData(ByteBuffer.wrap(new byte[]{1}));
Firmware savedFirmware = firmwareService.saveFirmware(firmware);
savedDevice.setFirmwareId(savedFirmware.getId());
thrown.expect(DataValidationException.class);
thrown.expectMessage("Can't assign firmware with different deviceProfile!");
deviceService.saveDevice(savedDevice);
}
@Test(expected = DataValidationException.class) @Test(expected = DataValidationException.class)
public void testSaveDeviceWithEmptyName() { public void testSaveDeviceWithEmptyName() {

232
dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java

@ -19,13 +19,16 @@ import com.datastax.oss.driver.api.core.uuid.Uuids;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
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.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.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.DeviceProfileId;
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.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
@ -36,6 +39,8 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public static final String TITLE = "My firmware"; public static final String TITLE = "My firmware";
@ -50,6 +55,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
private TenantId tenantId; private TenantId tenantId;
private DeviceProfileId deviceProfileId;
@Before @Before
public void before() { public void before() {
Tenant tenant = new Tenant(); Tenant tenant = new Tenant();
@ -57,8 +64,16 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
Tenant savedTenant = tenantService.saveTenant(tenant); Tenant savedTenant = tenantService.saveTenant(tenant);
Assert.assertNotNull(savedTenant); Assert.assertNotNull(savedTenant);
tenantId = savedTenant.getId(); tenantId = savedTenant.getId();
DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile");
DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
Assert.assertNotNull(savedDeviceProfile);
deviceProfileId = savedDeviceProfile.getId();
} }
@Rule
public ExpectedException thrown = ExpectedException.none();
@After @After
public void after() { public void after() {
tenantService.deleteTenant(tenantId); tenantService.deleteTenant(tenantId);
@ -68,6 +83,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public void testSaveFirmware() { public void testSaveFirmware() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
@ -99,6 +116,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public void testSaveFirmwareInfoAndUpdateWithData() { public void testSaveFirmwareInfoAndUpdateWithData() {
FirmwareInfo firmwareInfo = new FirmwareInfo(); FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setTenantId(tenantId); firmwareInfo.setTenantId(tenantId);
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION); firmwareInfo.setVersion(VERSION);
FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo); FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo);
@ -112,6 +131,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
Firmware firmware = new Firmware(savedFirmwareInfo.getId()); Firmware firmware = new Firmware(savedFirmwareInfo.getId());
firmware.setCreatedTime(firmwareInfo.getCreatedTime()); firmware.setCreatedTime(firmwareInfo.getCreatedTime());
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
@ -135,9 +156,11 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId()); firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId());
} }
@Test(expected = DataValidationException.class) @Test
public void testSaveFirmwareWithEmptyTenant() { public void testSaveFirmwareWithEmptyTenant() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
@ -145,65 +168,108 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM); firmware.setChecksum(CHECKSUM);
firmware.setData(DATA); firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware should be assigned to tenant!");
firmwareService.saveFirmware(firmware); firmwareService.saveFirmware(firmware);
} }
@Test(expected = DataValidationException.class) @Test
public void testSaveFirmwareWithEmptyType() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Type should be specified!");
firmwareService.saveFirmware(firmware);
}
@Test
public void testSaveFirmwareWithEmptyTitle() { public void testSaveFirmwareWithEmptyTitle() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE); firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM); firmware.setChecksum(CHECKSUM);
firmware.setData(DATA); firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware title should be specified!");
firmwareService.saveFirmware(firmware); firmwareService.saveFirmware(firmware);
} }
@Test(expected = DataValidationException.class) @Test
public void testSaveFirmwareWithEmptyFileName() { public void testSaveFirmwareWithEmptyFileName() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setContentType(CONTENT_TYPE); firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM); firmware.setChecksum(CHECKSUM);
firmware.setData(DATA); firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware file name should be specified!");
firmwareService.saveFirmware(firmware); firmwareService.saveFirmware(firmware);
} }
@Test(expected = DataValidationException.class) @Test
public void testSaveFirmwareWithEmptyContentType() { public void testSaveFirmwareWithEmptyContentType() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM); firmware.setChecksum(CHECKSUM);
firmware.setData(DATA); firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware content type should be specified!");
firmwareService.saveFirmware(firmware); firmwareService.saveFirmware(firmware);
} }
@Test(expected = DataValidationException.class) @Test
public void testSaveFirmwareWithEmptyData() { public void testSaveFirmwareWithEmptyData() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE); firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM); firmware.setChecksum(CHECKSUM);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware data should be specified!");
firmwareService.saveFirmware(firmware); firmwareService.saveFirmware(firmware);
} }
@Test(expected = DataValidationException.class) @Test
public void testSaveFirmwareWithInvalidTenant() { public void testSaveFirmwareWithInvalidTenant() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(new TenantId(Uuids.timeBased())); firmware.setTenantId(new TenantId(Uuids.timeBased()));
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
@ -211,41 +277,77 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM); firmware.setChecksum(CHECKSUM);
firmware.setData(DATA); firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware is referencing to non-existent tenant!");
firmwareService.saveFirmware(firmware); firmwareService.saveFirmware(firmware);
} }
@Test(expected = DataValidationException.class) @Test
public void testSaveFirmwareWithInvalidDeviceProfileId() {
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(new DeviceProfileId(Uuids.timeBased()));
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware is referencing to non-existent device profile!");
firmwareService.saveFirmware(firmware);
}
@Test
public void testSaveFirmwareWithEmptyChecksum() { public void testSaveFirmwareWithEmptyChecksum() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(new TenantId(Uuids.timeBased())); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE); firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setData(DATA); firmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware checksum should be specified!");
firmwareService.saveFirmware(firmware); firmwareService.saveFirmware(firmware);
} }
@Test(expected = DataValidationException.class) @Test
public void testSaveFirmwareInfoWithExistingTitleAndVersion() { public void testSaveFirmwareInfoWithExistingTitleAndVersion() {
FirmwareInfo firmwareInfo = new FirmwareInfo(); FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setTenantId(tenantId); firmwareInfo.setTenantId(tenantId);
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION); firmwareInfo.setVersion(VERSION);
firmwareService.saveFirmwareInfo(firmwareInfo); firmwareService.saveFirmwareInfo(firmwareInfo);
FirmwareInfo newFirmwareInfo = new FirmwareInfo(); FirmwareInfo newFirmwareInfo = new FirmwareInfo();
newFirmwareInfo.setTenantId(tenantId); newFirmwareInfo.setTenantId(tenantId);
newFirmwareInfo.setDeviceProfileId(deviceProfileId);
newFirmwareInfo.setType(FIRMWARE);
newFirmwareInfo.setTitle(TITLE); newFirmwareInfo.setTitle(TITLE);
newFirmwareInfo.setVersion(VERSION); newFirmwareInfo.setVersion(VERSION);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware with such title and version already exists!");
firmwareService.saveFirmwareInfo(newFirmwareInfo); firmwareService.saveFirmwareInfo(newFirmwareInfo);
} }
@Test(expected = DataValidationException.class) @Test
public void testSaveFirmwareWithExistingTitleAndVersion() { public void testSaveFirmwareWithExistingTitleAndVersion() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
@ -257,18 +359,27 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
Firmware newFirmware = new Firmware(); Firmware newFirmware = new Firmware();
newFirmware.setTenantId(tenantId); newFirmware.setTenantId(tenantId);
newFirmware.setDeviceProfileId(deviceProfileId);
newFirmware.setType(FIRMWARE);
newFirmware.setTitle(TITLE); newFirmware.setTitle(TITLE);
newFirmware.setVersion(VERSION); newFirmware.setVersion(VERSION);
newFirmware.setFileName(FILE_NAME); newFirmware.setFileName(FILE_NAME);
newFirmware.setContentType(CONTENT_TYPE); newFirmware.setContentType(CONTENT_TYPE);
newFirmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
newFirmware.setChecksum(CHECKSUM);
newFirmware.setData(DATA); newFirmware.setData(DATA);
thrown.expect(DataValidationException.class);
thrown.expectMessage("Firmware with such title and version already exists!");
firmwareService.saveFirmware(newFirmware); firmwareService.saveFirmware(newFirmware);
} }
@Test(expected = DataValidationException.class) @Test
public void testDeleteFirmwareWithReferenceByDevice() { public void testDeleteFirmwareWithReferenceByDevice() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
@ -281,11 +392,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
Device device = new Device(); Device device = new Device();
device.setTenantId(tenantId); device.setTenantId(tenantId);
device.setName("My device"); device.setName("My device");
device.setType("default"); device.setDeviceProfileId(deviceProfileId);
device.setFirmwareId(savedFirmware.getId()); device.setFirmwareId(savedFirmware.getId());
Device savedDevice = deviceService.saveDevice(device); Device savedDevice = deviceService.saveDevice(device);
try { try {
thrown.expect(DataValidationException.class);
thrown.expectMessage("The firmware referenced by the devices cannot be deleted!");
firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
} finally { } finally {
deviceService.deleteDevice(tenantId, savedDevice.getId()); deviceService.deleteDevice(tenantId, savedDevice.getId());
@ -293,10 +406,12 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
} }
} }
@Test(expected = DataValidationException.class) @Test
public void testDeleteFirmwareWithReferenceByDeviceProfile() { public void testUpdateDeviceProfileIdWithReferenceByDevice() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
@ -306,12 +421,81 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
firmware.setData(DATA); firmware.setData(DATA);
Firmware savedFirmware = firmwareService.saveFirmware(firmware); Firmware savedFirmware = firmwareService.saveFirmware(firmware);
DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile"); Device device = new Device();
deviceProfile.setFirmwareId(savedFirmware.getId()); device.setTenantId(tenantId);
device.setName("My device");
device.setDeviceProfileId(deviceProfileId);
device.setFirmwareId(savedFirmware.getId());
Device savedDevice = deviceService.saveDevice(device);
try {
thrown.expect(DataValidationException.class);
thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!");
savedFirmware.setDeviceProfileId(null);
firmwareService.saveFirmware(savedFirmware);
} finally {
deviceService.deleteDevice(tenantId, savedDevice.getId());
firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
}
}
@Test
public void testDeleteFirmwareWithReferenceByDeviceProfile() {
DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile");
DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(savedDeviceProfile.getId());
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
Firmware savedFirmware = firmwareService.saveFirmware(firmware);
savedDeviceProfile.setFirmwareId(savedFirmware.getId());
deviceProfileService.saveDeviceProfile(savedDeviceProfile);
try { try {
thrown.expect(DataValidationException.class);
thrown.expectMessage("The firmware referenced by the device profile cannot be deleted!");
firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
} finally {
deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
}
}
@Test
public void testUpdateDeviceProfileIdWithReferenceByDeviceProfile() {
DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile");
DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
Firmware firmware = new Firmware();
firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(savedDeviceProfile.getId());
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE);
firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME);
firmware.setContentType(CONTENT_TYPE);
firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
firmware.setChecksum(CHECKSUM);
firmware.setData(DATA);
Firmware savedFirmware = firmwareService.saveFirmware(firmware);
savedDeviceProfile.setFirmwareId(savedFirmware.getId());
deviceProfileService.saveDeviceProfile(savedDeviceProfile);
try {
thrown.expect(DataValidationException.class);
thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!");
savedFirmware.setDeviceProfileId(null);
firmwareService.saveFirmware(savedFirmware);
} finally { } finally {
deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); firmwareService.deleteFirmware(tenantId, savedFirmware.getId());
@ -322,6 +506,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public void testFindFirmwareById() { public void testFindFirmwareById() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
@ -341,6 +527,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public void testFindFirmwareInfoById() { public void testFindFirmwareInfoById() {
FirmwareInfo firmware = new FirmwareInfo(); FirmwareInfo firmware = new FirmwareInfo();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware); FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware);
@ -355,6 +543,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
public void testDeleteFirmware() { public void testDeleteFirmware() {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION); firmware.setVersion(VERSION);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
@ -377,6 +567,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
for (int i = 0; i < 165; i++) { for (int i = 0; i < 165; i++) {
Firmware firmware = new Firmware(); Firmware firmware = new Firmware();
firmware.setTenantId(tenantId); firmware.setTenantId(tenantId);
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(TITLE); firmware.setTitle(TITLE);
firmware.setVersion(VERSION + i); firmware.setVersion(VERSION + i);
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
@ -420,6 +612,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
for (int i = 0; i < 165; i++) { for (int i = 0; i < 165; i++) {
FirmwareInfo firmwareInfo = new FirmwareInfo(); FirmwareInfo firmwareInfo = new FirmwareInfo();
firmwareInfo.setTenantId(tenantId); firmwareInfo.setTenantId(tenantId);
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE); firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION + i); firmwareInfo.setVersion(VERSION + i);
firmwareInfo.setFileName(FILE_NAME); firmwareInfo.setFileName(FILE_NAME);
@ -434,7 +628,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
PageLink pageLink = new PageLink(16); PageLink pageLink = new PageLink(16);
PageData<FirmwareInfo> pageData; PageData<FirmwareInfo> pageData;
do { do {
pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, false, pageLink); pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink);
loadedFirmwares.addAll(pageData.getData()); loadedFirmwares.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink(); pageLink = pageLink.nextPageLink();
@ -450,6 +644,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
Firmware firmware = new Firmware(f.getId()); Firmware firmware = new Firmware(f.getId());
firmware.setCreatedTime(f.getCreatedTime()); firmware.setCreatedTime(f.getCreatedTime());
firmware.setTenantId(f.getTenantId()); firmware.setTenantId(f.getTenantId());
firmware.setDeviceProfileId(deviceProfileId);
firmware.setType(FIRMWARE);
firmware.setTitle(f.getTitle()); firmware.setTitle(f.getTitle());
firmware.setVersion(f.getVersion()); firmware.setVersion(f.getVersion());
firmware.setFileName(FILE_NAME); firmware.setFileName(FILE_NAME);
@ -465,7 +661,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest {
loadedFirmwares = new ArrayList<>(); loadedFirmwares = new ArrayList<>();
pageLink = new PageLink(16); pageLink = new PageLink(16);
do { do {
pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, true, pageLink); pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink);
loadedFirmwares.addAll(pageData.getData()); loadedFirmwares.addAll(pageData.getData());
if (pageData.hasNext()) { if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink(); pageLink = pageLink.nextPageLink();

0
transport/lwm2m/src/main/data/credentials/serverKeyStore.jks → transport/lwm2m/src/main/data/lwm2mserver.jks

2
ui-ngx/src/app/core/http/entity.service.ts

@ -361,7 +361,7 @@ export class EntityService {
break; break;
case EntityType.FIRMWARE: case EntityType.FIRMWARE:
pageLink.sortOrder.property = 'title'; pageLink.sortOrder.property = 'title';
entitiesObservable = this.firmwareService.getFirmwares(pageLink, true, config); entitiesObservable = this.firmwareService.getFirmwares(pageLink, config);
break; break;
} }
return entitiesObservable; return entitiesObservable;

15
ui-ngx/src/app/core/http/firmware.service.ts

@ -20,7 +20,7 @@ import { PageLink } from '@shared/models/page/page-link';
import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils'; import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { PageData } from '@shared/models/page/page-data'; import { PageData } from '@shared/models/page/page-data';
import { Firmware, FirmwareInfo } from '@shared/models/firmware.models'; import { Firmware, FirmwareInfo, FirmwareType } from '@shared/models/firmware.models';
import { catchError, map, mergeMap } from 'rxjs/operators'; import { catchError, map, mergeMap } from 'rxjs/operators';
import { deepClone, isDefinedAndNotNull } from '@core/utils'; import { deepClone, isDefinedAndNotNull } from '@core/utils';
@ -34,12 +34,13 @@ export class FirmwareService {
} }
public getFirmwares(pageLink: PageLink, hasData?: boolean, config?: RequestConfig): Observable<PageData<FirmwareInfo>> { public getFirmwares(pageLink: PageLink, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
let url = `/api/firmwares`; return this.http.get<PageData<FirmwareInfo>>(`/api/firmwares${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config));
if (isDefinedAndNotNull(hasData)) { }
url += `/${hasData}`;
} public getFirmwaresInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: FirmwareType,
url += `${pageLink.toQuery()}`; hasData = true, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
const url = `/api/firmwares/${deviceProfileId}/${type}/${hasData}${pageLink.toQuery()}`;
return this.http.get<PageData<FirmwareInfo>>(url, defaultHttpOptionsFromConfig(config)); return this.http.get<PageData<FirmwareInfo>>(url, defaultHttpOptionsFromConfig(config));
} }

94
ui-ngx/src/app/core/services/menu.service.ts

@ -103,19 +103,12 @@ export class MenuService {
path: '/widgets-bundles', path: '/widgets-bundles',
icon: 'now_widgets' icon: 'now_widgets'
}, },
{
id: guid(),
name: 'resource.resources-library',
type: 'link',
path: '/resources-library',
icon: 'folder'
},
{ {
id: guid(), id: guid(),
name: 'admin.system-settings', name: 'admin.system-settings',
type: 'toggle', type: 'toggle',
path: '/settings', path: '/settings',
height: '200px', height: '240px',
icon: 'settings', icon: 'settings',
pages: [ pages: [
{ {
@ -152,6 +145,13 @@ export class MenuService {
type: 'link', type: 'link',
path: '/settings/oauth2', path: '/settings/oauth2',
icon: 'security' icon: 'security'
},
{
id: guid(),
name: 'resource.resources-library',
type: 'link',
path: '/settings/resources-library',
icon: 'folder'
} }
] ]
} }
@ -188,16 +188,6 @@ export class MenuService {
} }
] ]
}, },
{
name: 'resource.management',
places: [
{
name: 'resource.resources-library',
icon: 'folder',
path: '/resources-library'
}
]
},
{ {
name: 'admin.system-settings', name: 'admin.system-settings',
places: [ places: [
@ -225,6 +215,11 @@ export class MenuService {
name: 'admin.oauth2.oauth2', name: 'admin.oauth2.oauth2',
icon: 'security', icon: 'security',
path: '/settings/oauth2' path: '/settings/oauth2'
},
{
name: 'resource.resources-library',
icon: 'folder',
path: '/resources-library'
} }
] ]
} }
@ -337,20 +332,6 @@ export class MenuService {
path: '/dashboards', path: '/dashboards',
icon: 'dashboards' icon: 'dashboards'
}, },
{
id: guid(),
name: 'resource.resources-library',
type: 'link',
path: '/resources-library',
icon: 'folder'
},
{
id: guid(),
name: 'admin.home-settings',
type: 'link',
path: '/settings/home',
icon: 'settings_applications'
},
{ {
id: guid(), id: guid(),
name: 'audit-log.audit-logs', name: 'audit-log.audit-logs',
@ -365,6 +346,30 @@ export class MenuService {
path: '/usage', path: '/usage',
icon: 'insert_chart', icon: 'insert_chart',
notExact: true notExact: true
},
{
id: guid(),
name: 'admin.system-settings',
type: 'toggle',
path: '/settings',
height: '80px',
icon: 'settings',
pages: [
{
id: guid(),
name: 'admin.home-settings',
type: 'link',
path: '/settings/home',
icon: 'settings_applications'
},
{
id: guid(),
name: 'resource.resources-library',
type: 'link',
path: '/settings/resources-library',
icon: 'folder'
}
]
} }
); );
return sections; return sections;
@ -455,16 +460,6 @@ export class MenuService {
); );
} }
homeSections.push( homeSections.push(
{
name: 'resource.management',
places: [
{
name: 'resource.resources-library',
icon: 'folder',
path: '/resources-library'
}
]
},
{ {
name: 'dashboard.management', name: 'dashboard.management',
places: [ places: [
@ -494,6 +489,21 @@ export class MenuService {
path: '/usage' path: '/usage'
} }
] ]
},
{
name: 'admin.system-settings',
places: [
{
name: 'admin.home-settings',
icon: 'settings_applications',
path: '/settings/home'
},
{
name: 'resource.resources-library',
icon: 'folder',
path: '/settings/resources-library'
}
]
} }
); );
return homeSections; return homeSections;

4
ui-ngx/src/app/modules/home/components/device/device-credentials.component.html

@ -20,7 +20,7 @@
<mat-label translate>device.credentials-type</mat-label> <mat-label translate>device.credentials-type</mat-label>
<mat-select formControlName="credentialsType"> <mat-select formControlName="credentialsType">
<mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType"> <mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType">
{{ credentialTypeNamesMap.get(deviceCredentialsType[credentialsType]) }} {{ credentialTypeNamesMap.get(credentialsType) }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
@ -85,7 +85,7 @@
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-value</mat-label> <mat-label translate>device.lwm2m-value</mat-label>
<textarea matInput formControlName="credentialsValue" rows="10" required <textarea matInput formControlName="credentialsValue" rows="10" required
[matTooltip]="lwm2mCredentialsValueTip(deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError'))" [matTooltip]="lwm2mCredentialsValueTooltip(deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError'))"
matTooltipPosition="above" matTooltipPosition="above"
></textarea> ></textarea>
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')"> <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')">

75
ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component, forwardRef, Input, OnDestroy, OnInit} from '@angular/core'; import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
FormBuilder, FormBuilder,
@ -33,9 +33,9 @@ import {
DeviceCredentials, DeviceCredentials,
DeviceCredentialsType DeviceCredentialsType
} from '@shared/models/device.models'; } from '@shared/models/device.models';
import {Subscription} from 'rxjs'; import { Subject } from 'rxjs';
import {distinctUntilChanged} from 'rxjs/operators'; import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
import {SecurityConfigComponent} from '@home/pages/device/lwm2m/security-config.component'; import { SecurityConfigLwm2mComponent } from '@home/components/device/security-config-lwm2m.component';
import { import {
ClientSecurityConfig, ClientSecurityConfig,
DEFAULT_END_POINT, DEFAULT_END_POINT,
@ -44,10 +44,10 @@ import {
getDefaultSecurityConfig, getDefaultSecurityConfig,
JSON_ALL_CONFIG, JSON_ALL_CONFIG,
validateSecurityConfig validateSecurityConfig
} from '@home/pages/device/lwm2m/security-config.models'; } from '@shared/models/lwm2m-security-config.models';
import {TranslateService} from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import {MatDialog} from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import {isDefinedAndNotNull} from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
@Component({ @Component({
selector: 'tb-device-credentials', selector: 'tb-device-credentials',
@ -67,20 +67,16 @@ import {isDefinedAndNotNull} from '@core/utils';
}) })
export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy { export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy {
deviceCredentialsFormGroup: FormGroup;
subscriptions: Subscription[] = [];
@Input() @Input()
disabled: boolean; disabled: boolean;
deviceCredentials: DeviceCredentials = null; private destroy$ = new Subject();
submitted = false; deviceCredentialsFormGroup: FormGroup;
deviceCredentialsType = DeviceCredentialsType; deviceCredentialsType = DeviceCredentialsType;
credentialsTypes = Object.keys(DeviceCredentialsType); credentialsTypes = Object.values(DeviceCredentialsType);
credentialTypeNamesMap = credentialTypeNames; credentialTypeNamesMap = credentialTypeNames;
@ -102,16 +98,17 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
}, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])}) }, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])})
}); });
this.deviceCredentialsFormGroup.get('credentialsBasic').disable(); this.deviceCredentialsFormGroup.get('credentialsBasic').disable();
this.subscriptions.push( this.deviceCredentialsFormGroup.valueChanges.pipe(
this.deviceCredentialsFormGroup.valueChanges.pipe(distinctUntilChanged()).subscribe(() => { distinctUntilChanged(),
this.updateView(); takeUntil(this.destroy$)
}) ).subscribe(() => {
); this.updateView();
this.subscriptions.push( });
this.deviceCredentialsFormGroup.get('credentialsType').valueChanges.subscribe(() => { this.deviceCredentialsFormGroup.get('credentialsType').valueChanges.pipe(
this.credentialsTypeChanged(); takeUntil(this.destroy$)
}) ).subscribe((type) => {
); this.credentialsTypeChanged(type);
});
} }
ngOnInit(): void { ngOnInit(): void {
@ -121,12 +118,12 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
} }
ngOnDestroy() { ngOnDestroy() {
this.subscriptions.forEach(s => s.unsubscribe()); this.destroy$.next();
this.destroy$.complete();
} }
writeValue(value: DeviceCredentials | null): void { writeValue(value: DeviceCredentials | null): void {
if (isDefinedAndNotNull(value)) { if (isDefinedAndNotNull(value)) {
this.deviceCredentials = value;
let credentialsBasic = {clientId: null, userName: null, password: null}; let credentialsBasic = {clientId: null, userName: null, password: null};
let credentialsValue = null; let credentialsValue = null;
if (value.credentialsType === DeviceCredentialsType.MQTT_BASIC) { if (value.credentialsType === DeviceCredentialsType.MQTT_BASIC) {
@ -179,10 +176,11 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
}; };
} }
credentialsTypeChanged(): void { credentialsTypeChanged(credentialsType: DeviceCredentialsType): void {
const credentialsValue = credentialsType === DeviceCredentialsType.LWM2M_CREDENTIALS ? this.lwm2mDefaultConfig : null;
this.deviceCredentialsFormGroup.patchValue({ this.deviceCredentialsFormGroup.patchValue({
credentialsId: null, credentialsId: null,
credentialsValue: JSON.stringify(getDefaultSecurityConfig(), null, 2), credentialsValue,
credentialsBasic: {clientId: '', userName: '', password: ''} credentialsBasic: {clientId: '', userName: '', password: ''}
}); });
this.updateValidators(); this.updateValidators();
@ -264,7 +262,7 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
} }
} }
const credentialsId = this.deviceCredentialsFormGroup.get('credentialsId').value || DEFAULT_END_POINT; const credentialsId = this.deviceCredentialsFormGroup.get('credentialsId').value || DEFAULT_END_POINT;
this.dialog.open<SecurityConfigComponent, DeviceCredentialsDialogLwm2mData, object>(SecurityConfigComponent, { this.dialog.open<SecurityConfigLwm2mComponent, DeviceCredentialsDialogLwm2mData, object>(SecurityConfigLwm2mComponent, {
disableClose: true, disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: { data: {
@ -275,8 +273,8 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
(res) => { (res) => {
if (res) { if (res) {
this.deviceCredentialsFormGroup.patchValue({ this.deviceCredentialsFormGroup.patchValue({
credentialsValue: this.isDefautLw2mResponse(res[JSON_ALL_CONFIG]) ? null : JSON.stringify(res[JSON_ALL_CONFIG]), credentialsValue: this.isDefaultLw2mResponse(res[JSON_ALL_CONFIG]) ? null : JSON.stringify(res[JSON_ALL_CONFIG]),
credentialsId: this.isDefautLw2mResponse(res[END_POINT]) ? null : JSON.stringify(res[END_POINT]).split('\"').join('') credentialsId: this.isDefaultLw2mResponse(res[END_POINT]) ? null : JSON.stringify(res[END_POINT]).split('\"').join('')
}); });
this.deviceCredentialsFormGroup.get('credentialsValue').markAsDirty(); this.deviceCredentialsFormGroup.get('credentialsValue').markAsDirty();
} }
@ -284,16 +282,19 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
); );
} }
private isDefautLw2mResponse(response: object): boolean { private isDefaultLw2mResponse(response: object): boolean {
return Object.keys(response).length === 0 || JSON.stringify(response) === '[{}]'; return Object.keys(response).length === 0 || JSON.stringify(response) === '[{}]';
} }
private lwm2mConfigJsonValidator(control: FormControl) { private lwm2mConfigJsonValidator(control: FormControl) {
return validateSecurityConfig(control.value) ? null: {jsonError: {parsedJson: "error"}}; return validateSecurityConfig(control.value) ? null : {jsonError: {parsedJson: 'error'}};
}
private get lwm2mDefaultConfig(): string {
return JSON.stringify(getDefaultSecurityConfig(), null, 2);
} }
lwm2mCredentialsValueTip (flag: boolean): string { lwm2mCredentialsValueTooltip(flag: boolean): string {
let jsonConfigDef = JSON.stringify(getDefaultSecurityConfig(), null, 2); return !flag ? '' : 'Example (mode=\"NoSec\"):\n\r ' + this.lwm2mDefaultConfig;
return !flag ? "" : 'Example (mode=\"NoSec\"):\n\r ' + jsonConfigDef;
} }
} }

79
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.html

@ -0,0 +1,79 @@
<!--
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.
-->
<section [formGroup]="serverFormGroup" fxLayout="column">
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.mode</mat-label>
<mat-select formControlName="securityMode">
<mat-option *ngFor="let securityType of securityConfigLwM2MTypes" [value]="securityType">
{{ lwm2mSecurityTypeTranslationMap.get(securityType) }}
</mat-option>
</mat-select>
</mat-form-field>
<div [fxShow]="serverFormGroup.get('securityMode').value !== securityConfigLwM2MType.NO_SEC">
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-publicKey-or-id' | translate }}</mat-label>
<textarea matInput
#clientPublicKeyOrId
[maxlength]="lenMaxClientPublicKeyOrId"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="clientPublicKeyOrId"
required>
</textarea>
<mat-hint align="end">{{clientPublicKeyOrId.value?.length || 0}}/{{lenMaxClientPublicKeyOrId}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('required')">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-pattern' | translate }}
</mat-error>
<mat-error *ngIf="(serverFormGroup.get('clientPublicKeyOrId').hasError('maxlength') ||
serverFormGroup.get('clientPublicKeyOrId').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-length' | translate: {
count: lenMaxClientPublicKeyOrId
} }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-secret-key' | translate }}</mat-label>
<textarea matInput
#clientSecretKey
[maxlength]="lengthClientSecretKey"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="clientSecretKey"
required>
</textarea>
<mat-hint align="end">{{clientSecretKey.value?.length || 0}}/{{lengthClientSecretKey}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('required')">
{{ 'device.lwm2m-security-config.client-secret-key-required' | translate }}
</mat-error>
<mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-secret-key-pattern' | translate }}
</mat-error>
<mat-error *ngIf="(serverFormGroup.get('clientSecretKey').hasError('maxlength') ||
serverFormGroup.get('clientSecretKey').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-secret-key-length' | translate: {
count: lengthClientSecretKey
} }}
</mat-error>
</mat-form-field>
</div>
</section>

20
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss

@ -0,0 +1,20 @@
/**
* 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.
*/
:host ::ng-deep {
textarea.mat-input-element.cdk-textarea-autosize {
box-sizing: content-box;
}
}

176
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts

@ -0,0 +1,176 @@
///
/// 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.
///
import { Component, forwardRef, OnDestroy } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
Validators
} from '@angular/forms';
import {
KEY_REGEXP_HEX_DEC,
LEN_MAX_PRIVATE_KEY,
LEN_MAX_PSK,
LEN_MAX_PUBLIC_KEY_RPK,
LEN_MAX_PUBLIC_KEY_X509,
Lwm2mSecurityType,
Lwm2mSecurityTypeTranslationMap,
ServerSecurityConfig
} from '@shared/models/lwm2m-security-config.models';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
@Component({
selector: 'tb-security-config-lwm2m-server',
templateUrl: './security-config-lwm2m-server.component.html',
styleUrls: ['./security-config-lwm2m-server.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent),
multi: true
}
]
})
export class SecurityConfigLwm2mServerComponent implements OnDestroy, ControlValueAccessor, Validator {
serverFormGroup: FormGroup;
securityConfigLwM2MType = Lwm2mSecurityType;
securityConfigLwM2MTypes = Object.values(Lwm2mSecurityType);
lwm2mSecurityTypeTranslationMap = Lwm2mSecurityTypeTranslationMap;
lenMinClientPublicKeyOrId = 0;
lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
private destroy$ = new Subject();
private propagateChange = (v: any) => {};
constructor(private fb: FormBuilder) {
this.serverFormGroup = this.fb.group({
securityMode: [Lwm2mSecurityType.NO_SEC],
clientPublicKeyOrId: [''],
clientSecretKey: ['']
});
this.serverFormGroup.get('securityMode').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((securityMode) => {
this.updateValidate(securityMode);
});
this.serverFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((value) => {
this.propagateChange(value);
});
}
writeValue(value: any): void {
if (value) {
this.updateValueFields(value);
}
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
if (isDisabled) {
this.serverFormGroup.disable({emitEvent: false});
} else {
this.serverFormGroup.enable({emitEvent: false});
}
}
validate(control): ValidationErrors | null {
return this.serverFormGroup.valid ? null : {
securityConfig: {valid: false}
};
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
private updateValueFields(serverData: ServerSecurityConfig): void {
this.serverFormGroup.patchValue(serverData, {emitEvent: false});
this.updateValidate(serverData.securityMode, true);
}
private updateValidate(securityMode: Lwm2mSecurityType, initValue = false): void {
switch (securityMode) {
case Lwm2mSecurityType.NO_SEC:
this.serverFormGroup.get('clientPublicKeyOrId').clearValidators();
this.serverFormGroup.get('clientSecretKey').clearValidators();
break;
case Lwm2mSecurityType.PSK:
this.lenMinClientPublicKeyOrId = 0;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lengthClientSecretKey = LEN_MAX_PSK;
this.setValidatorsSecurity(securityMode);
break;
case Lwm2mSecurityType.RPK:
this.lenMinClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.setValidatorsSecurity(securityMode);
break;
case Lwm2mSecurityType.X509:
this.lenMinClientPublicKeyOrId = 0;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_X509;
this.lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.setValidatorsSecurity(securityMode);
break;
}
this.serverFormGroup.get('clientPublicKeyOrId').updateValueAndValidity({emitEvent: false});
this.serverFormGroup.get('clientSecretKey').updateValueAndValidity({emitEvent: !initValue});
}
private setValidatorsSecurity = (securityMode: Lwm2mSecurityType): void => {
if (securityMode === Lwm2mSecurityType.PSK) {
this.serverFormGroup.get('clientPublicKeyOrId').setValidators([Validators.required]);
} else {
this.serverFormGroup.get('clientPublicKeyOrId').setValidators([
Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.minLength(this.lenMinClientPublicKeyOrId),
Validators.maxLength(this.lenMaxClientPublicKeyOrId)
]);
}
this.serverFormGroup.get('clientSecretKey').setValidators([
Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.minLength(this.lengthClientSecretKey),
Validators.maxLength(this.lengthClientSecretKey)
]);
}
}

143
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html

@ -0,0 +1,143 @@
<!--
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.
-->
<form [formGroup]="lwm2mConfigFormGroup" (ngSubmit)="save()" style="min-width: 400px;">
<mat-toolbar fxLayout="row" color="primary">
<h2>{{ title }}</h2>
<span fxFlex></span>
<button mat-button mat-icon-button
(click)="cancel()"
type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<div mat-dialog-content>
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.endpoint</mat-label>
<input matInput type="text" formControlName="endPoint" required>
<mat-error *ngIf="lwm2mConfigFormGroup.get('endPoint').hasError('required')">
{{ 'device.lwm2m-security-config.endpoint-required' | translate }}
</mat-error>
</mat-form-field>
<mat-tab-group (selectedTabChange)="tabChanged($event)">
<mat-tab label="{{ 'device.lwm2m-security-config.client-tab' | translate }}">
<mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.mode</mat-label>
<mat-select formControlName="securityConfigClientMode">
<mat-option *ngFor="let securityConfigClientMode of securityConfigLwM2MTypes"
[value]="securityConfigClientMode">
{{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityConfigClientMode]) }}
</mat-option>
</mat-select>
</mat-form-field>
<div *ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.PSK">
<mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.identity' | translate }}</mat-label>
<input matInput type="text" formControlName="identity" required>
<mat-error *ngIf="lwm2mConfigFormGroup.get('identity').hasError('required')">
{{ 'device.lwm2m-security-config.identity-required' | translate }}
</mat-error>
</mat-form-field>
</div>
<mat-form-field class="mat-block" *ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.RPK ||
lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.PSK">
<mat-label>{{ 'device.lwm2m-security-config.client-key' | translate }}</mat-label>
<textarea matInput
#key
maxlength="{{lenMaxKeyClient}}"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1"
formControlName="key"
required>
</textarea>
<mat-hint align="end">{{key.value?.length || 0}}/{{lenMaxKeyClient}}</mat-hint>
<mat-error *ngIf="lwm2mConfigFormGroup.get('key').hasError('required')">
{{ 'device.lwm2m-security-config.client-key-required' | translate }}
</mat-error>
<mat-error *ngIf="lwm2mConfigFormGroup.get('key').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-key-pattern' | translate }}
</mat-error>
<mat-error *ngIf="(lwm2mConfigFormGroup.get('key').hasError('maxlength') ||
lwm2mConfigFormGroup.get('key').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-key-length' | translate: {
count: lenMaxKeyClient
} }}
</mat-error>
</mat-form-field>
<mat-checkbox formControlName="x509" color="primary"
*ngIf="lwm2mConfigFormGroup.get('securityConfigClientMode').value === securityConfigLwM2MType.X509">
{{ 'device.lwm2m-security-config.client-certificate' | translate }}
</mat-checkbox>
</mat-tab>
<mat-tab label="{{ 'device.lwm2m-security-config.bootstrap-tab' | translate }}">
<div style="padding: 2px;">
<mat-accordion multi="true" class="mat-body-1">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{ 'device.lwm2m-security-config.bootstrap-server' | translate }}
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-security-config-lwm2m-server
formControlName="bootstrapServer">
</tb-security-config-lwm2m-server>
</ng-template>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{ 'device.lwm2m-security-config.lwm2m-server' | translate }}
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-security-config-lwm2m-server
formControlName="lwm2mServer">
</tb-security-config-lwm2m-server>
</ng-template>
</mat-expansion-panel>
</mat-accordion>
</div>
</mat-tab>
<mat-tab label="{{ 'device.lwm2m-security-config.config-json-tab' | translate }}">
<ng-template matTabContent>
<tb-json-object-edit
[formControlName]="formControlNameJsonAllConfig"
label="{{ 'device.lwm2m-value' | translate }}"
readonly
[required]="true"
[fillHeight]="false">
</tb-json-object-edit>
</ng-template>
</mat-tab>
</mat-tab-group>
</div>
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || lwm2mConfigFormGroup.invalid || !lwm2mConfigFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div>
</form>

34
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss

@ -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.
*/
:host {
mat-tab-group {
min-height: 330px;
}
}
:host ::ng-deep {
.mat-tab-body-wrapper {
min-height: 200px;
}
.mat-tab-body {
padding: 16px 0;
}
textarea.mat-input-element.cdk-textarea-autosize {
box-sizing: content-box;
}
}

257
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts

@ -0,0 +1,257 @@
///
/// 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.
///
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import {
DeviceCredentialsDialogLwm2mData,
getClientSecurityConfig,
JSON_ALL_CONFIG,
KEY_REGEXP_HEX_DEC,
LEN_MAX_PSK,
LEN_MAX_PUBLIC_KEY_RPK,
Lwm2mSecurityConfigModels,
Lwm2mSecurityType,
Lwm2mSecurityTypeTranslationMap
} from '@shared/models/lwm2m-security-config.models';
import { MatTabChangeEvent } from '@angular/material/tabs';
import { MatTab } from '@angular/material/tabs/tab';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'tb-security-config-lwm2m',
templateUrl: './security-config-lwm2m.component.html',
styleUrls: ['./security-config-lwm2m.component.scss']
})
export class SecurityConfigLwm2mComponent extends DialogComponent<SecurityConfigLwm2mComponent, object> implements OnInit, OnDestroy {
private destroy$ = new Subject();
lwm2mConfigFormGroup: FormGroup;
title: string;
securityConfigLwM2MType = Lwm2mSecurityType;
securityConfigLwM2MTypes = Object.keys(Lwm2mSecurityType);
credentialTypeLwM2MNamesMap = Lwm2mSecurityTypeTranslationMap;
formControlNameJsonAllConfig = JSON_ALL_CONFIG;
jsonAllConfig: Lwm2mSecurityConfigModels;
lenMaxKeyClient = LEN_MAX_PSK;
tabPrevious: MatTab;
tabIndexPrevious = 0;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: DeviceCredentialsDialogLwm2mData,
public dialogRef: MatDialogRef<SecurityConfigLwm2mComponent, object>,
public fb: FormBuilder,
public translate: TranslateService) {
super(store, router, dialogRef);
}
ngOnInit() {
this.jsonAllConfig = JSON.parse(JSON.stringify(this.data.jsonAllConfig));
this.lwm2mConfigFormGroup = this.initLwm2mConfigFormGroup();
this.title = this.translate.instant('device.lwm2m-security-info') + ': ' + this.data.endPoint;
this.lwm2mConfigFormGroup.get('x509').disable();
this.initClientSecurityConfig(this.lwm2mConfigFormGroup.get('jsonAllConfig').value);
this.registerDisableOnLoadFormControl(this.lwm2mConfigFormGroup.get('securityConfigClientMode'));
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
private initClientSecurityConfig = (jsonAllConfig: Lwm2mSecurityConfigModels): void => {
if (jsonAllConfig.client.securityConfigClientMode !== Lwm2mSecurityType.NO_SEC) {
this.lwm2mConfigFormGroup.patchValue(jsonAllConfig.client, {emitEvent: false});
}
this.securityConfigClientUpdateValidators(jsonAllConfig.client.securityConfigClientMode);
}
private securityConfigClientModeChanged(type: Lwm2mSecurityType): void {
const config = getClientSecurityConfig(type, this.lwm2mConfigFormGroup.get('endPoint').value);
switch (type) {
case Lwm2mSecurityType.PSK:
config.identity = this.data.endPoint;
config.key = this.lwm2mConfigFormGroup.get('key').value;
break;
case Lwm2mSecurityType.RPK:
config.key = this.lwm2mConfigFormGroup.get('key').value;
break;
}
this.jsonAllConfig.client = config;
this.lwm2mConfigFormGroup.patchValue({
...config,
jsonAllConfig: this.jsonAllConfig
}, {emitEvent: false});
this.securityConfigClientUpdateValidators(type);
}
private securityConfigClientUpdateValidators = (mode: Lwm2mSecurityType): void => {
switch (mode) {
case Lwm2mSecurityType.NO_SEC:
case Lwm2mSecurityType.X509:
this.setValidatorsNoSecX509();
break;
case Lwm2mSecurityType.PSK:
this.lenMaxKeyClient = LEN_MAX_PSK;
this.setValidatorsPskRpk(mode);
break;
case Lwm2mSecurityType.RPK:
this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK;
this.setValidatorsPskRpk(mode);
break;
}
this.lwm2mConfigFormGroup.get('identity').updateValueAndValidity({emitEvent: false});
this.lwm2mConfigFormGroup.get('key').updateValueAndValidity({emitEvent: false});
}
private setValidatorsNoSecX509 = (): void => {
this.lwm2mConfigFormGroup.get('identity').setValidators([]);
this.lwm2mConfigFormGroup.get('key').setValidators([]);
}
private setValidatorsPskRpk = (mode: Lwm2mSecurityType): void => {
if (mode === Lwm2mSecurityType.PSK) {
this.lwm2mConfigFormGroup.get('identity').setValidators([Validators.required]);
} else {
this.lwm2mConfigFormGroup.get('identity').setValidators([]);
}
this.lwm2mConfigFormGroup.get('key').setValidators([Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.maxLength(this.lenMaxKeyClient), Validators.minLength(this.lenMaxKeyClient)]);
}
tabChanged = (tabChangeEvent: MatTabChangeEvent): void => {
if (this.tabIndexPrevious !== tabChangeEvent.index) {
this.upDateValueToJson();
}
this.tabIndexPrevious = tabChangeEvent.index;
}
private upDateValueToJson(): void {
switch (this.tabIndexPrevious) {
case 0:
this.upDateValueToJsonTab0();
break;
case 1:
this.upDateValueToJsonTab1();
break;
}
}
private upDateValueToJsonTab0 = (): void => {
if (this.lwm2mConfigFormGroup.get('identity').dirty && this.lwm2mConfigFormGroup.get('identity').valid ||
this.lwm2mConfigFormGroup.get('key').dirty && this.lwm2mConfigFormGroup.get('key').valid) {
this.updateBootstrapSettings();
this.upDateJsonAllConfig();
}
}
private upDateValueToJsonTab1 = (): void => {
const bootstrap = this.lwm2mConfigFormGroup.get('bootstrapServer').value;
if (bootstrap !== null
&& this.lwm2mConfigFormGroup.get('bootstrapServer').dirty
&& this.lwm2mConfigFormGroup.get('bootstrapServer').valid) {
this.jsonAllConfig.bootstrap.bootstrapServer = bootstrap;
this.upDateJsonAllConfig();
}
const serverConfig = this.lwm2mConfigFormGroup.get('lwm2mServer').value;
if (serverConfig !== null
&& this.lwm2mConfigFormGroup.get('lwm2mServer').dirty
&& this.lwm2mConfigFormGroup.get('lwm2mServer').valid) {
this.jsonAllConfig.bootstrap.lwm2mServer = serverConfig;
this.upDateJsonAllConfig();
}
}
private updateBootstrapSettings() {
const securityMode = 'securityMode';
this.jsonAllConfig.client.identity = this.lwm2mConfigFormGroup.get('identity').value;
this.jsonAllConfig.client.key = this.lwm2mConfigFormGroup.get('key').value;
if (this.lwm2mConfigFormGroup.get('bootstrapServer').value[securityMode] === Lwm2mSecurityType.PSK) {
this.jsonAllConfig.bootstrap.bootstrapServer.clientPublicKeyOrId = this.jsonAllConfig.client.identity;
this.jsonAllConfig.bootstrap.bootstrapServer.clientSecretKey = this.jsonAllConfig.client.key;
this.lwm2mConfigFormGroup.get('bootstrapServer').patchValue(this.jsonAllConfig.bootstrap.bootstrapServer, {emitEvent: false});
}
if (this.lwm2mConfigFormGroup.get('lwm2mServer').value[securityMode] === Lwm2mSecurityType.PSK) {
this.jsonAllConfig.bootstrap.lwm2mServer.clientPublicKeyOrId = this.jsonAllConfig.client.identity;
this.jsonAllConfig.bootstrap.lwm2mServer.clientSecretKey = this.jsonAllConfig.client.key;
this.lwm2mConfigFormGroup.get('lwm2mServer').patchValue(this.jsonAllConfig.bootstrap.lwm2mServer, {emitEvent: false});
}
}
private upDateJsonAllConfig = (): void => {
this.lwm2mConfigFormGroup.patchValue({
jsonAllConfig: this.jsonAllConfig
}, {emitEvent: false});
}
private initLwm2mConfigFormGroup = (): FormGroup => {
if (this.jsonAllConfig.client.securityConfigClientMode === Lwm2mSecurityType.PSK) {
this.data.endPoint = this.jsonAllConfig.client.endpoint;
}
const formGroup = this.fb.group({
securityConfigClientMode: [this.jsonAllConfig.client.securityConfigClientMode],
identity: [''],
key: [''],
x509: [false],
bootstrapServer: [this.jsonAllConfig.bootstrap.bootstrapServer],
lwm2mServer: [this.jsonAllConfig.bootstrap.lwm2mServer],
endPoint: [this.data.endPoint],
jsonAllConfig: [this.jsonAllConfig]
});
formGroup.get('securityConfigClientMode').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((type) => {
this.securityConfigClientModeChanged(type);
});
formGroup.get('endPoint').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((endpoint) => {
if (formGroup.get('securityConfigClientMode').value === Lwm2mSecurityType.PSK) {
this.jsonAllConfig.client.endpoint = endpoint;
this.upDateJsonAllConfig();
}
});
return formGroup;
}
save(): void {
this.upDateValueToJson();
this.data.endPoint = this.lwm2mConfigFormGroup.get('endPoint').value.split('\'').join('');
this.data.jsonAllConfig = this.jsonAllConfig;
if (this.lwm2mConfigFormGroup.get('securityConfigClientMode').value === Lwm2mSecurityType.PSK) {
this.data.endPoint = this.data.jsonAllConfig.client.identity;
}
this.dialogRef.close(this.data);
}
cancel(): void {
this.dialogRef.close(undefined);
}
}

3
ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html

@ -39,7 +39,8 @@
</mat-form-field> </mat-form-field>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="'isError'"> <ng-template [ngSwitchCase]="'isError'">
<tb-checkbox formControlName="isError" [falseValue]="''"> <tb-checkbox formControlName="isError" [falseValue]="''"
(ngModelChange)="changeIsError($event)">
{{ 'event.has-error' | translate }} {{ 'event.has-error' | translate }}
</tb-checkbox> </tb-checkbox>
</ng-template> </ng-template>

6
ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.ts

@ -105,5 +105,11 @@ export class EventFilterPanelComponent {
cancel() { cancel() {
this.overlayRef.dispose(); this.overlayRef.dispose();
} }
changeIsError(value: boolean | string) {
if (this.conditionError && value === '') {
this.eventFilterFormGroup.get('error').reset('', {emitEvent: false});
}
}
} }

3
ui-ngx/src/app/modules/home/components/event/event-table-config.ts

@ -288,7 +288,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
break; break;
case EventType.LC_EVENT: case EventType.LC_EVENT:
this.filterColumns.push( this.filterColumns.push(
{key: 'method', title: 'event.event'}, {key: 'event', title: 'event.event'},
{key: 'status', title: 'event.status'}, {key: 'status', title: 'event.status'},
{key: 'error', title: 'event.error'} {key: 'error', title: 'event.error'}
); );
@ -356,6 +356,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
componentRef.onDestroy(() => { componentRef.onDestroy(() => {
if (componentRef.instance.result && !isEqual(this.filterParams, componentRef.instance.result.filterParams)) { if (componentRef.instance.result && !isEqual(this.filterParams, componentRef.instance.result.filterParams)) {
this.filterParams = componentRef.instance.result.filterParams; this.filterParams = componentRef.instance.result.filterParams;
this.table.paginator.pageIndex = 0;
this.table.updateData(); this.table.updateData();
} }
}); });

6
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -139,6 +139,8 @@ import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/emb
import { EdgeDownlinkTableComponent } from '@home/components/edge/edge-downlink-table.component'; import { EdgeDownlinkTableComponent } from '@home/components/edge/edge-downlink-table.component';
import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.component'; import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.component';
import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component'; import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component';
import { SecurityConfigLwm2mComponent } from '@home/components/device/security-config-lwm2m.component';
import { SecurityConfigLwm2mServerComponent } from '@home/components/device/security-config-lwm2m-server.component';
@NgModule({ @NgModule({
declarations: declarations:
@ -241,6 +243,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
DeviceWizardDialogComponent, DeviceWizardDialogComponent,
DeviceCredentialsComponent, DeviceCredentialsComponent,
CopyDeviceCredentialsComponent, CopyDeviceCredentialsComponent,
SecurityConfigLwm2mComponent,
SecurityConfigLwm2mServerComponent,
AlarmScheduleDialogComponent, AlarmScheduleDialogComponent,
EditAlarmDetailsDialogComponent, EditAlarmDetailsDialogComponent,
SmsProviderConfigurationComponent, SmsProviderConfigurationComponent,
@ -345,6 +349,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
DeviceWizardDialogComponent, DeviceWizardDialogComponent,
DeviceCredentialsComponent, DeviceCredentialsComponent,
CopyDeviceCredentialsComponent, CopyDeviceCredentialsComponent,
SecurityConfigLwm2mComponent,
SecurityConfigLwm2mServerComponent,
AlarmScheduleInfoComponent, AlarmScheduleInfoComponent,
AlarmScheduleComponent, AlarmScheduleComponent,
AlarmScheduleDialogComponent, AlarmScheduleDialogComponent,

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

Loading…
Cancel
Save