From ff334b54389f9a4ceda9ea6e7fa3cb4415b16128 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 30 Apr 2021 14:22:36 +0300 Subject: [PATCH 1/6] Improvements to support large files for firmware updates --- .../server/transport/lwm2m/server/LwM2mNetworkConfig.java | 6 +++--- .../transport/lwm2m/server/LwM2mTransportRequest.java | 2 +- .../transport/lwm2m/server/LwM2mTransportServiceImpl.java | 7 ++++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java index 22f822bae6..4cf12941a5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java @@ -66,7 +66,7 @@ public class LwM2mNetworkConfig { * A value of {@code 0} turns off transparent handling of blockwise transfers altogether. */ // coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 8192); - coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 16384); + coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024); /** * The default DTLS response matcher. * Supported values are STRICT, RELAXED, or PRINCIPAL. @@ -83,7 +83,7 @@ public class LwM2mNetworkConfig { * 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, 512); + 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. @@ -91,7 +91,7 @@ public class LwM2mNetworkConfig { * 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, 512); + coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024); coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index 370abaa9cd..5cf106419c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -424,7 +424,7 @@ public class LwM2mTransportRequest { LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(), value); serviceImpl.sendLogsToThingsboard(msg, registration.getId()); - log.warn("[{}] [{}] [{}] - [{}] [{}] Update finished successfully: [{}]", request.getClass().getName().toString(), registration.getEndpoint(), + log.trace("[{}] [{}] [{}] - [{}] [{}] Update finished successfully: [{}]", request.getClass().getName().toString(), registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(), value); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java index ae9055da9e..350aebc4c7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java @@ -334,6 +334,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { String pathName = tsKvProto.getKv().getKey(); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); + //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); } @@ -612,6 +613,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { public void sendLogsToThingsboard(String logMsg, String registrationId) { SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId); if (logMsg != null && sessionInfo != null) { + if(logMsg.length() > 1024){ + logMsg = logMsg.substring(0, 1024); + } this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo); } } @@ -1372,6 +1376,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) .setTenantIdMSB(sessionInfo.getTenantIdMSB()) .setTenantIdLSB(sessionInfo.getTenantIdLSB()) + .setType(FirmwareType.FIRMWARE.name()) .build(); transportService.process(sessionInfo, getFirmwareRequestMsg, new TransportServiceCallback<>() { @@ -1416,7 +1421,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { Integer objectId = 5; String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(objectId); String targetIdVer = LWM2M_SEPARATOR_PATH + objectId + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0; - lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.TLV.getName(), + lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), firmwareChunk, lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); log.warn("updateFirmwareClient [{}] [{}]", lwM2MClient.getFrUpdate().getCurrentFwVersion(), lwM2MClient.getFrUpdate().getClientFwVersion()); } From a787ca9d5d933277760dd86d63defed0c8520be8 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 30 Apr 2021 16:35:21 +0300 Subject: [PATCH 2/6] Refactoring of the lwm2m yml configuration --- .../server/controller/BaseController.java | 4 +- .../server/controller/Lwm2mController.java | 4 +- ...=> LwM2MServerSecurityInfoRepository.java} | 113 +++------ .../src/main/resources/thingsboard.yml | 60 ++--- ...TransportBootstrapServerConfiguration.java | 51 ++-- .../LwM2MTransportContextBootstrap.java | 12 +- .../secure/LwM2MBootstrapSecurityStore.java | 18 +- .../lwm2m/config/LwM2MSecureServerConfig.java | 38 +++ .../LwM2MTransportBootstrapConfig.java} | 47 ++-- .../config/LwM2MTransportServerConfig.java | 153 ++++++++++++ ...LwM2mCredentialsSecurityInfoValidator.java | 14 +- .../lwm2m/server/LwM2mServerListener.java | 4 +- .../server/LwM2mTransportContextServer.java | 14 +- ...er.java => LwM2mTransportHandlerUtil.java} | 4 +- .../lwm2m/server/LwM2mTransportRequest.java | 30 +-- .../LwM2mTransportServerConfiguration.java | 69 +++--- .../LwM2mTransportServerInitializer.java | 2 +- .../server/LwM2mTransportServiceImpl.java | 94 ++++---- .../lwm2m/server/client/LwM2mClient.java | 6 +- .../server/client/LwM2mClientContextImpl.java | 8 +- .../server/client/Lwm2mClientRpcRequest.java | 4 +- .../credentials/shell/lwM2M_credentials.sh | 0 .../serverKeyStore.jks => lwm2mserver.jks} | Bin .../lwm2m/LwM2MTransportConfigServer.java | 217 ------------------ .../serverKeyStore.jks => lwm2mserver.jks} | Bin 25 files changed, 433 insertions(+), 533 deletions(-) rename application/src/main/java/org/thingsboard/server/service/lwm2m/{LwM2MModelsRepository.java => LwM2MServerSecurityInfoRepository.java} (53%) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java rename common/transport/{transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java => lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java} (54%) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{LwM2mTransportHandler.java => LwM2mTransportHandlerUtil.java} (99%) mode change 100755 => 100644 common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh rename common/transport/lwm2m/src/main/resources/{credentials/serverKeyStore.jks => lwm2mserver.jks} (100%) delete mode 100644 common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java rename transport/lwm2m/src/main/data/{credentials/serverKeyStore.jks => lwm2mserver.jks} (100%) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 60a71a60ac..505416f4d6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/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.rpc.EdgeGrpcService; 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.queue.TbClusterService; import org.thingsboard.server.service.resource.TbResourceService; @@ -267,7 +267,7 @@ public abstract class BaseController { protected TbDeviceProfileCache deviceProfileCache; @Autowired - protected LwM2MModelsRepository lwM2MModelsRepository; + protected LwM2MServerSecurityInfoRepository lwM2MServerSecurityInfoRepository; @Autowired(required = false) protected EdgeService edgeService; diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index 02605ffaf0..0855e4ee1f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/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) @ResponseBody public ServerSecurityConfig getLwm2mBootstrapSecurityInfo(@PathVariable("securityMode") String securityMode, - @PathVariable("bootstrapServerIs") boolean bootstrapServerIs) throws ThingsboardException { + @PathVariable("bootstrapServerIs") boolean bootstrapServer) throws ThingsboardException { try { - return lwM2MModelsRepository.getBootstrapSecurityInfo(securityMode, bootstrapServerIs); + return lwM2MServerSecurityInfoRepository.getServerSecurityInfo(securityMode, bootstrapServer); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java similarity index 53% rename from application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java rename to application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java index 51ae94ba2e..012cd3e359 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MModelsRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java @@ -16,14 +16,15 @@ package org.thingsboard.server.service.lwm2m; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.util.Hex; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig; -import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap; -import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer; +import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig; +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 java.math.BigInteger; @@ -42,96 +43,59 @@ import java.security.spec.KeySpec; @Slf4j @Service +@RequiredArgsConstructor @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 "; - - @Autowired - LwM2MTransportConfigServer contextServer; - - - @Autowired - LwM2MTransportConfigBootstrap contextBootStrap; + private final LwM2MTransportServerConfig serverConfig; + private final LwM2MTransportBootstrapConfig bootstrapConfig; /** * @param securityMode - * @param bootstrapServerIs + * @param bootstrapServer * @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()); - return getBootstrapServer(bootstrapServerIs, lwM2MSecurityMode); + ServerSecurityConfig result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig, lwM2MSecurityMode); + result.setBootstrapServerIs(bootstrapServer); + return result; } - /** - * @param bootstrapServerIs - * @param mode - * @return ServerSecurityConfig more value is default: Important - port, host, publicKey - */ - private ServerSecurityConfig getBootstrapServer(boolean bootstrapServerIs, LwM2MSecurityMode mode) { + private ServerSecurityConfig getServerSecurityConfig(LwM2MSecureServerConfig serverConfig, LwM2MSecurityMode mode) { ServerSecurityConfig bsServ = new ServerSecurityConfig(); - bsServ.setBootstrapServerIs(bootstrapServerIs); - if (bootstrapServerIs) { - bsServ.setServerId(contextBootStrap.getBootstrapServerId()); - switch (mode) { - case NO_SEC: - bsServ.setHost(contextBootStrap.getBootstrapHost()); - bsServ.setPort(contextBootStrap.getBootstrapPortNoSec()); - bsServ.setServerPublicKey(""); - break; - case PSK: - bsServ.setHost(contextBootStrap.getBootstrapHostSecurity()); - bsServ.setPort(contextBootStrap.getBootstrapPortSecurity()); - bsServ.setServerPublicKey(""); - break; - case RPK: - case X509: - bsServ.setHost(contextBootStrap.getBootstrapHostSecurity()); - bsServ.setPort(contextBootStrap.getBootstrapPortSecurity()); - bsServ.setServerPublicKey(getPublicKey (contextBootStrap.getBootstrapAlias(), this.contextBootStrap.getBootstrapPublicX(), this.contextBootStrap.getBootstrapPublicY())); - 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; - } + bsServ.setServerId(serverConfig.getId()); + switch (mode) { + case NO_SEC: + bsServ.setHost(serverConfig.getHost()); + bsServ.setPort(serverConfig.getPort()); + bsServ.setServerPublicKey(""); + break; + case PSK: + bsServ.setHost(serverConfig.getSecureHost()); + bsServ.setPort(serverConfig.getSecurePort()); + bsServ.setServerPublicKey(""); + break; + case RPK: + case X509: + bsServ.setHost(serverConfig.getSecureHost()); + bsServ.setPort(serverConfig.getSecurePort()); + bsServ.setServerPublicKey(getPublicKey(serverConfig.getCertificateAlias(), this.serverConfig.getPublicX(), this.serverConfig.getPublicY())); + break; + default: + break; } return bsServ; } - private String getPublicKey (String alias, String publicServerX, String publicServerY) { + private String getPublicKey(String alias, String publicServerX, String publicServerY) { String publicKey = getServerPublicKeyX509(alias); return publicKey != null ? publicKey : getRPKPublicKey(publicServerX, publicServerY); } - /** - * @param alias - * @return PublicKey format HexString or null - */ private String getServerPublicKeyX509(String alias) { try { - X509Certificate serverCertificate = (X509Certificate) contextServer.getKeyStoreValue().getCertificate(alias); + X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(alias); return Hex.encodeHexString(serverCertificate.getEncoded()); } catch (CertificateEncodingException | KeyStoreException e) { e.printStackTrace(); @@ -139,11 +103,6 @@ public class LwM2MModelsRepository { return null; } - /** - * @param publicServerX - * @param publicServerY - * @return PublicKey format HexString or null - */ private String getRPKPublicKey(String publicServerX, String publicServerY) { try { /** Get Elliptic Curve Parameter spec for secp256r1 */ diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 33760d174c..83f1c8fa02 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -630,38 +630,14 @@ transport: lwm2m: # Enable/disable lvm2m transport protocol. 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: id: "${LWM2M_SERVER_ID:123}" bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" - bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC:5685}" - secure: - bind_address_security: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}" - bind_port_security: "${LWM2M_BIND_PORT_SECURITY:5686}" - # create_rpk: "${CREATE_RPK:}" + bind_port: "${LWM2M_BIND_PORT:5685}" + security: + bind_address: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}" + bind_port: "${LWM2M_BIND_PORT_SECURITY:5686}" # 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_y: "${LWM2M_SERVER_PUBLIC_Y:5eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}" private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}" @@ -671,19 +647,33 @@ transport: enable: "${LWM2M_ENABLED_BS:true}" id: "${LWM2M_SERVER_ID_BS:111}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" - bind_port_no_sec: "${LWM2M_BIND_PORT_NO_SEC_BS:5687}" - secure: - bind_address_security: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" - bind_port_security: "${LWM2M_BIND_PORT_SECURITY_BS:5688}" + bind_port: "${LWM2M_BIND_PORT_BS:5687}" + security: + bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" + bind_port: "${LWM2M_BIND_PORT_SECURITY_BS:5688}" # 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_y: "${LWM2M_SERVER_PUBLIC_Y_BS:3fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}" private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED_BS:308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104205ecafd90caa7be45c42e1f3f32571632b8409e6e6249d7124f4ba56fab3c8083a00a06082a8648ce3d030107a144034200045017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f913fc4e61bcd8901ec27c424114c3e887ed372497f0c2cf85839b8443e76988b34}" # Only Certificate_x509: 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}" # Use redis for Security and Registration stores redis.enabled: "${LWM2M_REDIS_ENABLED:false}" snmp: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java index 9e40c736df..2c0e01d169 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java @@ -25,9 +25,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; 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.LwM2MInMemoryBootstrapConfigStore; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContextServer; import java.math.BigInteger; @@ -81,13 +83,13 @@ public class LwM2MTransportBootstrapServerConfiguration { @Bean public LeshanBootstrapServer getLeshanBootstrapServer() { 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) { LeshanBootstrapServerBuilder builder = new LeshanBootstrapServerBuilder(); - builder.setLocalAddress(this.contextBs.getCtxBootStrap().getBootstrapHost(), bootstrapPortNoSec); - builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getBootstrapHostSecurity(), bootstrapSecurePort); + builder.setLocalAddress(this.contextBs.getCtxBootStrap().getHost(), bootstrapPortNoSec); + builder.setLocalSecureAddress(this.contextBs.getCtxBootStrap().getSecureHost(), bootstrapSecurePort); /** Create CoAP Config */ builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort)); @@ -106,14 +108,13 @@ public class LwM2MTransportBootstrapServerConfiguration { /** Create and Set DTLS Config */ DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); - dtlsConfig.setRecommendedSupportedGroupsOnly(this.contextS.getLwM2MTransportConfigServer().isRecommendedSupportedGroups()); - dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getLwM2MTransportConfigServer().isRecommendedCiphers()); + dtlsConfig.setRecommendedSupportedGroupsOnly(this.contextS.getLwM2MTransportServerConfig().isRecommendedSupportedGroups()); + dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getLwM2MTransportServerConfig().isRecommendedCiphers()); if (this.pskMode) { dtlsConfig.setSupportedCipherSuites( TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256); - } - else { + } else { dtlsConfig.setSupportedCipherSuites( TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256, @@ -133,10 +134,10 @@ public class LwM2MTransportBootstrapServerConfiguration { private void setServerWithCredentials(LeshanBootstrapServerBuilder builder) { try { - if (this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue() != null) { - KeyStore keyStoreServer = this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue(); + if (this.contextS.getLwM2MTransportServerConfig().getKeyStoreValue() != null) { + KeyStore keyStoreServer = this.contextS.getLwM2MTransportServerConfig().getKeyStoreValue(); if (this.setBuilderX509(builder)) { - X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getLwM2MTransportConfigServer().getRootAlias()); + X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getLwM2MTransportServerConfig().getRootCertificateAlias()); if (rootCAX509Cert != null) { X509Certificate[] trustedCertificates = new X509Certificate[1]; trustedCertificates[0] = rootCAX509Cert; @@ -167,12 +168,10 @@ public class LwM2MTransportBootstrapServerConfiguration { * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks */ try { - X509Certificate serverCertificate = (X509Certificate) this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getBootstrapAlias()); - PrivateKey privateKey = (PrivateKey) this.contextS.getLwM2MTransportConfigServer().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getBootstrapAlias(), this.contextS.getLwM2MTransportConfigServer().getKeyStorePasswordServer() == null ? null : this.contextS.getLwM2MTransportConfigServer().getKeyStorePasswordServer().toCharArray()); + X509Certificate serverCertificate = (X509Certificate) this.contextS.getLwM2MTransportServerConfig().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getCertificateAlias()); + PrivateKey privateKey = (PrivateKey) this.contextS.getLwM2MTransportServerConfig().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getCertificateAlias(), this.contextS.getLwM2MTransportServerConfig().getKeyStorePassword() == null ? null : this.contextS.getLwM2MTransportServerConfig().getKeyStorePassword().toCharArray()); PublicKey publicKey = serverCertificate.getPublicKey(); - if (serverCertificate != null && - privateKey != null && privateKey.getEncoded().length > 0 && - publicKey != null && publicKey.getEncoded().length > 0) { + if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) { builder.setPublicKey(serverCertificate.getPublicKey()); builder.setPrivateKey(privateKey); builder.setCertificateChain(new X509Certificate[]{serverCertificate}); @@ -199,10 +198,12 @@ public class LwM2MTransportBootstrapServerConfiguration { } private void infoPramsUri(String mode) { - log.info("Bootstrap Server uses [{}]: serverNoSecureURI : [{}], serverSecureURI : [{}]", + log.info("Bootstrap Server uses [{}]: serverNoSecureURI : [{}:{}], serverSecureURI : [{}:{}]", mode, - this.contextBs.getCtxBootStrap().getBootstrapHost() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortNoSec(), - this.contextBs.getCtxBootStrap().getBootstrapHostSecurity() + ":" + this.contextBs.getCtxBootStrap().getBootstrapPortSecurity()); + this.contextBs.getCtxBootStrap().getHost(), + this.contextBs.getCtxBootStrap().getPort(), + this.contextBs.getCtxBootStrap().getSecureHost(), + this.contextBs.getCtxBootStrap().getSecurePort()); } @@ -236,23 +237,25 @@ public class LwM2MTransportBootstrapServerConfiguration { AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); algoParameters.init(new ECGenParameterSpec("secp256r1")); 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 */ - byte[] publicX = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicX().toCharArray()); - byte[] publicY = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPublicY().toCharArray()); + byte[] publicX = Hex.decodeHex(serverConfig.getPublicX().toCharArray()); + byte[] publicY = Hex.decodeHex(serverConfig.getPublicY().toCharArray()); /** Create key specs */ KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), parameterSpec); /** Get public key */ 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 */ - byte[] privateS = Hex.decodeHex(this.contextBs.getCtxBootStrap().getBootstrapPrivateEncoded().toCharArray()); + byte[] privateS = Hex.decodeHex(privateEncodedKey.toCharArray()); try { this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS)); } 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); } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportContextBootstrap.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportContextBootstrap.java index 43e3781716..35dff0181d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportContextBootstrap.java +++ b/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.stereotype.Component; import org.thingsboard.server.common.transport.TransportContext; -import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigBootstrap; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; @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'") public class LwM2MTransportContextBootstrap extends TransportContext { - private final LwM2MTransportConfigBootstrap lwM2MTransportConfigBootstrap; + private final LwM2MTransportBootstrapConfig lwM2MTransportBootstrapConfig; - public LwM2MTransportContextBootstrap(LwM2MTransportConfigBootstrap ctxBootStrap) { - this.lwM2MTransportConfigBootstrap = ctxBootStrap; + public LwM2MTransportContextBootstrap(LwM2MTransportBootstrapConfig ctxBootStrap) { + this.lwM2MTransportBootstrapConfig = ctxBootStrap; } - public LwM2MTransportConfigBootstrap getCtxBootStrap() { - return this.lwM2MTransportConfigBootstrap; + public LwM2MTransportBootstrapConfig getCtxBootStrap() { + return this.lwM2MTransportBootstrapConfig; } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index 306603cf78..7aefe19da5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -35,7 +35,7 @@ import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInf import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContextServer; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil; import java.io.IOException; import java.security.GeneralSecurityException; @@ -43,12 +43,12 @@ import java.util.Collections; import java.util.List; import java.util.UUID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.BOOTSTRAP_SERVER; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LWM2M_SERVER; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.SERVERS; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getBootstrapParametersFromThingsboard; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.BOOTSTRAP_SERVER; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LWM2M_SERVER; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.SERVERS; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.getBootstrapParametersFromThingsboard; @Slf4j @Service("LwM2MBootstrapSecurityStore") @@ -69,7 +69,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @Override public List 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) { /** add value to store from BootstrapJson */ this.setBootstrapConfigScurityInfo(store); @@ -93,7 +93,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @Override 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) { /** add value to store from BootstrapJson */ this.setBootstrapConfigScurityInfo(store); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java new file mode 100644 index 0000000000..fcca9fb975 --- /dev/null +++ b/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(); + +} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java similarity index 54% rename from common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java index 14da88ecd1..ea4f7ad391 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigBootstrap.java +++ b/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 * limitations under the License. */ -package org.thingsboard.server.common.transport.lwm2m; +package org.thingsboard.server.transport.lwm2m.config; import lombok.Getter; import lombok.Setter; @@ -29,53 +29,42 @@ import java.util.Map; @Slf4j @Component @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true') || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") -public class LwM2MTransportConfigBootstrap { - - @Getter - @Value("${transport.lwm2m.bootstrap.enable:}") - private Boolean bootstrapEnable; +public class LwM2MTransportBootstrapConfig implements LwM2MSecureServerConfig { @Getter @Value("${transport.lwm2m.bootstrap.id:}") - private Integer bootstrapServerId; + private Integer id; @Getter @Value("${transport.lwm2m.bootstrap.bind_address:}") - private String bootstrapHost; + private String host; @Getter - @Value("${transport.lwm2m.bootstrap.bind_port_no_sec:}") - private Integer bootstrapPortNoSec; + @Value("${transport.lwm2m.bootstrap.bind_port:}") + private Integer port; @Getter - @Value("${transport.lwm2m.bootstrap.secure.bind_address_security:}") - private String bootstrapHostSecurity; + @Value("${transport.lwm2m.bootstrap.security.bind_address:}") + private String secureHost; @Getter - @Value("${transport.lwm2m.bootstrap.secure.bind_port_security:}") - private Integer bootstrapPortSecurity; + @Value("${transport.lwm2m.bootstrap.security.bind_port:}") + private Integer securePort; @Getter - @Value("${transport.lwm2m.bootstrap.secure.public_x:}") - private String bootstrapPublicX; + @Value("${transport.lwm2m.bootstrap.security.public_x:}") + private String publicX; @Getter - @Value("${transport.lwm2m.bootstrap.secure.public_y:}") - private String bootstrapPublicY; + @Value("${transport.lwm2m.bootstrap.security.public_y:}") + private String publicY; @Getter - @Setter - private PublicKey bootstrapPublicKey; + @Value("${transport.lwm2m.bootstrap.security.private_encoded:}") + private String privateEncoded; @Getter - @Value("${transport.lwm2m.bootstrap.secure.private_encoded:}") - private String bootstrapPrivateEncoded; + @Value("${transport.lwm2m.bootstrap.security.alias:}") + private String certificateAlias; - @Getter - @Value("${transport.lwm2m.bootstrap.secure.alias:}") - private String bootstrapAlias; - - @Getter - @Setter - private Map sessions; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java new file mode 100644 index 0000000000..a68359a1ca --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java @@ -0,0 +1,153 @@ +/** + * 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.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; + + + @PostConstruct + public void init() { + try { + File keyStoreFile = new File(Resources.getResource(keyStorePathFile).toURI()); + InputStream inKeyStore = new FileInputStream(keyStoreFile); + keyStoreValue = KeyStore.getInstance(keyStoreType); + keyStoreValue.load(inKeyStore, keyStorePassword == null ? null : keyStorePassword.toCharArray()); + } catch (Exception e) { + log.error("Unable to lookup LwM2M keystore. Reason: " + e.getMessage(), e); + throw new RuntimeException("Failed to lookup LwM2M keystore", e); + } + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java index bc8eb51b45..e9a09c54d3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java @@ -27,7 +27,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCreden import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContextServer; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil; import java.io.IOException; import java.security.GeneralSecurityException; @@ -58,7 +58,7 @@ public class LwM2mCredentialsSecurityInfoValidator { * @param keyValue - * @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); final ReadResultSecurityStore[] resultSecurityStore = new ReadResultSecurityStore[1]; contextS.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endpoint).build(), @@ -68,7 +68,7 @@ public class LwM2mCredentialsSecurityInfoValidator { String credentialsBody = msg.getCredentialsBody(); resultSecurityStore[0] = createSecurityInfo(endpoint, credentialsBody, keyValue); resultSecurityStore[0].setMsg(msg); - Optional deviceProfileOpt = LwM2mTransportHandler.decode(msg.getProfileBody().toByteArray()); + Optional deviceProfileOpt = LwM2mTransportHandlerUtil.decode(msg.getProfileBody().toByteArray()); deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile)); latch.countDown(); } @@ -81,7 +81,7 @@ public class LwM2mCredentialsSecurityInfoValidator { } }); try { - latch.await(contextS.getLwM2MTransportConfigServer().getTimeout(), TimeUnit.MILLISECONDS); + latch.await(contextS.getLwM2MTransportServerConfig().getTimeout(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { log.error("Failed to await credentials!", e); } @@ -95,9 +95,9 @@ public class LwM2mCredentialsSecurityInfoValidator { * @param keyValue - * @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(); - JsonObject objectMsg = LwM2mTransportHandler.validateJson(jsonStr); + JsonObject objectMsg = LwM2mTransportHandlerUtil.validateJson(jsonStr); if (objectMsg != null && !objectMsg.isJsonNull()) { JsonObject object = (objectMsg.has(keyValue.type) && !objectMsg.get(keyValue.type).isJsonNull()) ? objectMsg.get(keyValue.type).getAsJsonObject() : null; /** @@ -108,7 +108,7 @@ public class LwM2mCredentialsSecurityInfoValidator { && objectMsg.get("client").getAsJsonObject().get("endpoint").isJsonPrimitive()) ? objectMsg.get("client").getAsJsonObject().get("endpoint").getAsString() : null; endPoint = (endPointPsk == null || endPointPsk.isEmpty()) ? endPoint : endPointPsk; if (object != null && !object.isJsonNull()) { - if (keyValue.equals(LwM2mTransportHandler.LwM2mTypeServer.BOOTSTRAP)) { + if (keyValue.equals(LwM2mTransportHandlerUtil.LwM2mTypeServer.BOOTSTRAP)) { result.setBootstrapJsonCredential(object); result.setEndPoint(endPoint); result.setSecurityMode(LwM2MSecurityMode.fromSecurityMode(object.get("bootstrapServer").getAsJsonObject().get("securityMode").getAsString().toLowerCase()).code); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index 72626f1103..c8108b10f5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -26,8 +26,8 @@ import org.eclipse.leshan.server.registration.RegistrationUpdate; import java.util.Collection; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; -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 public class LwM2mServerListener { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java index d3ccf0e86d..e1a322f646 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java @@ -43,7 +43,7 @@ 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.TransportServiceCallback; -import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; @@ -57,7 +57,7 @@ import java.util.ArrayList; import java.util.List; 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 @Component @@ -65,7 +65,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandle public class LwM2mTransportContextServer extends TransportContext { - private final LwM2MTransportConfigServer lwM2MTransportConfigServer; + private final LwM2MTransportServerConfig lwM2MTransportServerConfig; private final TransportService transportService; @@ -75,15 +75,15 @@ public class LwM2mTransportContextServer extends TransportContext { @Getter private final LwM2MJsonAdaptor adaptor; - public LwM2mTransportContextServer(LwM2MTransportConfigServer lwM2MTransportConfigServer, TransportService transportService, TransportResourceCache transportResourceCache, LwM2MJsonAdaptor adaptor) { - this.lwM2MTransportConfigServer = lwM2MTransportConfigServer; + public LwM2mTransportContextServer(LwM2MTransportServerConfig lwM2MTransportServerConfig, TransportService transportService, TransportResourceCache transportResourceCache, LwM2MJsonAdaptor adaptor) { + this.lwM2MTransportServerConfig = lwM2MTransportServerConfig; this.transportService = transportService; this.transportResourceCache = transportResourceCache; this.adaptor = adaptor; } - public LwM2MTransportConfigServer getLwM2MTransportConfigServer() { - return this.lwM2MTransportConfigServer; + public LwM2MTransportServerConfig getLwM2MTransportServerConfig() { + return this.lwM2MTransportServerConfig; } public TransportResourceCache getTransportResourceCache() { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandlerUtil.java similarity index 99% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandler.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandlerUtil.java index 08e46eb288..a5780d9879 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandlerUtil.java @@ -67,7 +67,7 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; @Slf4j -public class LwM2mTransportHandler { +public class LwM2mTransportHandlerUtil { public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0"; public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings"; @@ -256,7 +256,7 @@ public class LwM2mTransportHandler { ObjectMapper mapper = new ObjectMapper(); String profileStr = mapper.writeValueAsString(profile); 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) { log.error("", e); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index 5cf106419c..a49e9dbb23 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -66,18 +66,18 @@ import java.util.stream.Collectors; 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.NOT_FOUND; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.FR_PATH_RESOURCE_VER_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_CANCEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_READ_ALL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.RESPONSE_CHANNEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromIdVerToObjectId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.createWriteAttributeRequest; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.DEFAULT_TIMEOUT; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.FR_PATH_RESOURCE_VER_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_VALUE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_CANCEL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_READ_ALL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.RESPONSE_CHANNEL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromIdVerToObjectId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromObjectIdToIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.createWriteAttributeRequest; @Slf4j @Service @@ -110,7 +110,7 @@ public class LwM2mTransportRequest { @PostConstruct public void init() { this.converter = LwM2mValueConverterImpl.getInstance(); - executorResponse = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getResponsePoolSize(), + executorResponse = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getResponsePoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL))); } @@ -161,7 +161,7 @@ public class LwM2mTransportRequest { leshanServer.getObservationService().cancelObservations(registration, target); break; case EXECUTE: - resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() + resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig() .getModelProvider()); if (params != null && !resourceModel.multiple) { request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModel.type, ResourceModel.Type.STRING, resultIds)); @@ -171,7 +171,7 @@ public class LwM2mTransportRequest { break; case WRITE_REPLACE: // Request to write a String Single-Instance Resource using the TLV content format. - resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() + resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig() .getModelProvider()); if (contentFormat.equals(ContentFormat.TLV)) { request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java index 86bec24984..e320e3fcfa 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java @@ -31,7 +31,9 @@ import org.eclipse.leshan.server.security.EditableSecurityStore; import org.eclipse.leshan.server.security.SecurityChecker; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; @@ -83,13 +85,13 @@ public class LwM2mTransportServerConfiguration { @Bean public LeshanServer getLeshanServer() { log.info("Starting LwM2M transport Server... PostConstruct"); - return this.getLhServer(this.context.getLwM2MTransportConfigServer().getServerPortNoSec(), this.context.getLwM2MTransportConfigServer().getServerPortSecurity()); + return this.getLhServer(this.context.getLwM2MTransportServerConfig().getPort(), this.context.getLwM2MTransportServerConfig().getSecurePort()); } private LeshanServer getLhServer(Integer serverPortNoSec, Integer serverSecurePort) { LeshanServerBuilder builder = new LeshanServerBuilder(); - builder.setLocalAddress(this.context.getLwM2MTransportConfigServer().getServerHost(), serverPortNoSec); - builder.setLocalSecureAddress(this.context.getLwM2MTransportConfigServer().getServerHostSecurity(), serverSecurePort); + builder.setLocalAddress(this.context.getLwM2MTransportServerConfig().getHost(), serverPortNoSec); + builder.setLocalSecureAddress(this.context.getLwM2MTransportServerConfig().getSecureHost(), serverSecurePort); builder.setDecoder(new DefaultLwM2mNodeDecoder()); /** Use a magic converter to support bad type send by the UI. */ builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); @@ -102,7 +104,7 @@ public class LwM2mTransportServerConfiguration { /** Define model provider (Create Models )*/ LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.context); - this.context.getLwM2MTransportConfigServer().setModelProvider(modelProvider); + this.context.getLwM2MTransportServerConfig().setModelProvider(modelProvider); builder.setObjectModelProvider(modelProvider); /** Create credentials */ @@ -116,8 +118,8 @@ public class LwM2mTransportServerConfiguration { /** Create DTLS Config */ DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); dtlsConfig.setServerOnly(true); - dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups()); - dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers()); + dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportServerConfig().isRecommendedSupportedGroups()); + dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportServerConfig().isRecommendedCiphers()); if (this.pskMode) { dtlsConfig.setSupportedCipherSuites( TLS_PSK_WITH_AES_128_CCM_8, @@ -139,9 +141,9 @@ public class LwM2mTransportServerConfiguration { private void setServerWithCredentials(LeshanServerBuilder builder) { try { - if (this.context.getLwM2MTransportConfigServer().getKeyStoreValue() != null) { + if (this.context.getLwM2MTransportServerConfig().getKeyStoreValue() != null) { if (this.setBuilderX509(builder)) { - X509Certificate rootCAX509Cert = (X509Certificate) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportConfigServer().getRootAlias()); + X509Certificate rootCAX509Cert = (X509Certificate) this.context.getLwM2MTransportServerConfig().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportServerConfig().getRootCertificateAlias()); if (rootCAX509Cert != null) { X509Certificate[] trustedCertificates = new X509Certificate[1]; trustedCertificates[0] = rootCAX509Cert; @@ -175,17 +177,11 @@ public class LwM2mTransportServerConfiguration { } 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 { - X509Certificate serverCertificate = (X509Certificate) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportConfigServer().getServerAlias()); - PrivateKey privateKey = (PrivateKey) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getKey(this.context.getLwM2MTransportConfigServer().getServerAlias(), this.context.getLwM2MTransportConfigServer().getKeyStorePasswordServer() == null ? null : this.context.getLwM2MTransportConfigServer().getKeyStorePasswordServer().toCharArray()); + X509Certificate serverCertificate = (X509Certificate) this.context.getLwM2MTransportServerConfig().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportServerConfig().getCertificateAlias()); + PrivateKey privateKey = (PrivateKey) this.context.getLwM2MTransportServerConfig().getKeyStoreValue().getKey(this.context.getLwM2MTransportServerConfig().getCertificateAlias(), this.context.getLwM2MTransportServerConfig().getKeyStorePassword() == null ? null : this.context.getLwM2MTransportServerConfig().getKeyStorePassword().toCharArray()); PublicKey publicKey = serverCertificate.getPublicKey(); - if (serverCertificate != null && - privateKey != null && privateKey.getEncoded().length > 0 && - publicKey != null && publicKey.getEncoded().length > 0) { + if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) { builder.setPublicKey(serverCertificate.getPublicKey()); builder.setPrivateKey(privateKey); builder.setCertificateChain(new X509Certificate[]{serverCertificate}); @@ -212,10 +208,12 @@ public class LwM2mTransportServerConfiguration { } private void infoPramsUri(String mode) { - log.info("Server uses [{}]: serverNoSecureURI : [{}], serverSecureURI : [{}]", - mode, - this.context.getLwM2MTransportConfigServer().getServerHost() + ":" + this.context.getLwM2MTransportConfigServer().getServerPortNoSec(), - this.context.getLwM2MTransportConfigServer().getServerHostSecurity() + ":" + this.context.getLwM2MTransportConfigServer().getServerPortSecurity()); + LwM2MTransportServerConfig lwM2MTransportServerConfig = this.context.getLwM2MTransportServerConfig(); + log.info("Server uses [{}]: serverNoSecureURI : [{}:{}], serverSecureURI : [{}:{}]", mode, + lwM2MTransportServerConfig.getHost(), + lwM2MTransportServerConfig.getPort(), + lwM2MTransportServerConfig.getSecureHost(), + lwM2MTransportServerConfig.getSecurePort()); } private boolean setServerRPK(LeshanServerBuilder builder) { @@ -233,39 +231,26 @@ public class LwM2mTransportServerConfiguration { 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 { /** Get Elliptic Curve Parameter spec for secp256r1 */ AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); algoParameters.init(new ECGenParameterSpec("secp256r1")); ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - if (this.context.getLwM2MTransportConfigServer().getServerPublicX() != null && - !this.context.getLwM2MTransportConfigServer().getServerPublicX().isEmpty() && - this.context.getLwM2MTransportConfigServer().getServerPublicY() != null && - !this.context.getLwM2MTransportConfigServer().getServerPublicY().isEmpty()) { - /** Get point values */ - byte[] publicX = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPublicX().toCharArray()); - byte[] publicY = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPublicY().toCharArray()); - /** Create key specs */ + LwM2MTransportServerConfig serverConfig = this.context.getLwM2MTransportServerConfig(); + if (StringUtils.isNotEmpty(serverConfig.getPublicX()) && StringUtils.isNotEmpty(serverConfig.getPublicY())) { + byte[] publicX = Hex.decodeHex(serverConfig.getPublicX().toCharArray()); + byte[] publicY = Hex.decodeHex(serverConfig.getPublicY().toCharArray()); KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)), parameterSpec); - /** Get keys */ this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec); } - if (this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded() != null && - !this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded().isEmpty()) { - /** Get private key */ - byte[] privateS = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded().toCharArray()); + String privateEncodedKey = serverConfig.getPrivateEncoded(); + if (StringUtils.isNotEmpty(privateEncodedKey)) { + byte[] privateS = Hex.decodeHex(privateEncodedKey.toCharArray()); try { this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS)); } 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); } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java index 4fad90910a..4e9b8a97fc 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java @@ -41,7 +41,7 @@ public class LwM2mTransportServerInitializer { @PostConstruct public void init() { - if (this.context.getLwM2MTransportConfigServer().getEnableGenNewKeyPskRpk()) { + if (this.context.getLwM2MTransportServerConfig().getEnableGenNewKeyPskRpk()) { new LWM2MGenerationPSkRPkECC(); } this.startLhServer(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java index 350aebc4c7..8dabe625eb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java @@ -84,29 +84,29 @@ import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; 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.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.FR_PATH_RESOURCE_VER_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LWM2M_STRATEGY_2; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.DISCOVER; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.EXECUTE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_CANCEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.OBSERVE_READ_ALL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.READ; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.WRITE_ATTRIBUTES; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.WRITE_REPLACE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LwM2mTypeOper.WRITE_UPDATE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.SERVICE_CHANNEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertJsonArrayToSet; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromIdVerToObjectId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getAckCallback; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.validateObjectVerFromKey; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.CLIENT_NOT_AUTHORIZED; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.DEVICE_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.FR_PATH_RESOURCE_VER_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_VALUE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LWM2M_STRATEGY_2; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.DISCOVER; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.EXECUTE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_CANCEL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.OBSERVE_READ_ALL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.READ; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.WRITE_REPLACE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LwM2mTypeOper.WRITE_UPDATE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.SERVICE_CHANNEL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertJsonArrayToSet; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromIdVerToObjectId; +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 @Service @@ -143,12 +143,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @PostConstruct public void init() { - this.lwM2mTransportContextServer.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) lwM2mTransportContextServer.getLwM2MTransportConfigServer().getSessionReportTimeout()), lwM2mTransportContextServer.getLwM2MTransportConfigServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); - this.executorRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getRegisteredPoolSize(), + this.lwM2mTransportContextServer.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) lwM2mTransportContextServer.getLwM2MTransportServerConfig().getSessionReportTimeout()), lwM2mTransportContextServer.getLwM2MTransportServerConfig().getSessionReportTimeout(), TimeUnit.MILLISECONDS); + this.executorRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getRegisteredPoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL))); - this.executorUpdateRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getUpdateRegisteredPoolSize(), + this.executorUpdateRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getUpdateRegisteredPoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL))); - this.executorUnRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getUnRegisteredPoolSize(), + this.executorUnRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getUnRegisteredPoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL))); this.converter = LwM2mValueConverterImpl.getInstance(); } @@ -281,7 +281,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { Set observations = leshanServer.getObservationService().getObservations(registration); observations.forEach(observation -> lwM2mTransportRequest.sendAllRequest(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), OBSERVE_CANCEL, - null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); + null, null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null)); } } @@ -339,7 +339,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { this.getInfoFirmwareUpdate(lwM2MClient); } if (pathIdVer != null) { - ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() + ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig() .getModelProvider()); if (resourceModel != null && resourceModel.operations.isWritable()) { this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), valueNew, pathIdVer); @@ -404,7 +404,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @Override public void onResourceUpdate(Optional resourceUpdateMsgOpt) { 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.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getModelProvider())); } /** @@ -413,7 +413,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @Override public void onResourceDelete(Optional resourceDeleteMsgOpt) { 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.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getModelProvider())); } @Override @@ -429,7 +429,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { } else { lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), lwm2mClientRpcRequest.getContentFormatName(), lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), - this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), lwm2mClientRpcRequest); + this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), lwm2mClientRpcRequest); } } catch (Exception e) { if (lwm2mClientRpcRequest == null) { @@ -457,7 +457,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { lwm2mClientRpcRequest.setRequestId(toDeviceRequest.getRequestId()); lwm2mClientRpcRequest.setSessionInfo(sessionInfo); lwm2mClientRpcRequest.setValidTypeOper(toDeviceRequest.getMethodName()); - JsonObject rpcRequest = LwM2mTransportHandler.validateJson(toDeviceRequest.getParams()); + JsonObject rpcRequest = LwM2mTransportHandlerUtil.validateJson(toDeviceRequest.getParams()); if (rpcRequest != null) { if (rpcRequest.has(lwm2mClientRpcRequest.keyNameKey)) { String targetIdVer = this.getPresentPathIntoProfile(sessionInfo, @@ -546,7 +546,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @Override public void doTrigger(Registration registration, String path) { lwM2mTransportRequest.sendAllRequest(registration, path, EXECUTE, - ContentFormat.TLV.getName(), null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + ContentFormat.TLV.getName(), null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); } /** @@ -636,11 +636,11 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration); Set clientObjects = lwM2mClientContext.getSupportedIdVerInClient(registration); if (clientObjects != null && clientObjects.size() > 0) { - if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { + if (LWM2M_STRATEGY_2 == LwM2mTransportHandlerUtil.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { // #2 lwM2MClient.getPendingReadRequests().addAll(clientObjects); clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(), - null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); + null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null)); } // #1 this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, READ, clientObjects); @@ -689,7 +689,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { */ private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) { LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); - if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() + if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig() .getModelProvider())) { if (FR_PATH_RESOURCE_VER_ID.equals(convertPathFromIdVerToObjectId(path)) && lwM2MClient.getFrUpdate().getCurrentFwVersion() != null @@ -780,7 +780,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { ConcurrentHashMap finalParams = params; pathSend.forEach(target -> { lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(), - finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); }); if (OBSERVE.equals(typeOper)) { lwM2MClient.initReadValue(this, null); @@ -1094,10 +1094,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { if (pathIds.isResource()) { if (READ.equals(typeOper)) { lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, - ContentFormat.TLV.getName(), null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + ContentFormat.TLV.getName(), null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); } else if (OBSERVE.equals(typeOper)) { lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, - null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + null, null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); } } }); @@ -1153,7 +1153,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { if (!pathSend.isEmpty()) { ConcurrentHashMap finalParams = lwm2mAttributesNew; pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), - finalParams.get(target), this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); + finalParams.get(target), this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null)); } }); } @@ -1170,7 +1170,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { params.clear(); params.put(OBJECT_VERSION, ""); lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), - params, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + params, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); }); } }); @@ -1183,7 +1183,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { paramAnallyzer.forEach(pathIdVer -> { if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) { lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, OBSERVE_CANCEL, null, - null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); } } ); @@ -1193,7 +1193,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) { lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, WRITE_REPLACE, ContentFormat.TLV.getName(), valueNew, - this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); } else { 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", @@ -1406,7 +1406,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { public void readRequestToClientFirmwareVer(Registration registration) { String pathIdVer = convertPathFromObjectIdToIdVer(FR_PATH_RESOURCE_VER_ID, registration); lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, READ, ContentFormat.TLV.getName(), - null, lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + null, lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); } /** @@ -1422,7 +1422,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(objectId); String targetIdVer = LWM2M_SEPARATOR_PATH + objectId + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0; lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), - firmwareChunk, lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + firmwareChunk, lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); log.warn("updateFirmwareClient [{}] [{}]", lwM2MClient.getFrUpdate().getCurrentFwVersion(), lwM2MClient.getFrUpdate().getClientFwVersion()); } } @@ -1444,7 +1444,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { } private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { - ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() + ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig() .getModelProvider()); Integer objectId = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)).getObjectId(); String objectVer = validateObjectVerFromKey(pathIdVer); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 0617847246..b79f6b5241 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -42,9 +42,9 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; 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.LwM2mTransportHandler.convertPathFromIdVerToObjectId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getVerFromPathIdVerOrId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.TRANSPORT_DEFAULT_LWM2M_VERSION; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.convertPathFromIdVerToObjectId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.getVerFromPathIdVerOrId; @Slf4j @Data diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index fe17ac3819..44df451be7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/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.LwM2mCredentialsSecurityInfoValidator; 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.Map; @@ -34,7 +34,7 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; 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 @TbLwM2mTransportComponent @@ -118,7 +118,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { */ @Override 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) { UUID profileUuid = (store.getDeviceProfile() != null && addUpdateProfileParameters(store.getDeviceProfile())) ? store.getDeviceProfile().getUuidId() : null; LwM2mClient client; @@ -165,7 +165,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @Override public boolean addUpdateProfileParameters(DeviceProfile deviceProfile) { - LwM2mClientProfile lwM2MClientProfile = LwM2mTransportHandler.getLwM2MClientProfileFromThingsboard(deviceProfile); + LwM2mClientProfile lwM2MClientProfile = LwM2mTransportHandlerUtil.getLwM2MClientProfileFromThingsboard(deviceProfile); if (lwM2MClientProfile != null) { profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile); return true; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java index c71f0e61b0..d2c6ada458 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java +++ b/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.thingsboard.server.gen.transport.TransportProtos; 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 static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.validPathIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.validPathIdVer; @Data public class Lwm2mClientRpcRequest { diff --git a/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh b/common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh old mode 100755 new mode 100644 diff --git a/common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks b/common/transport/lwm2m/src/main/resources/lwm2mserver.jks similarity index 100% rename from common/transport/lwm2m/src/main/resources/credentials/serverKeyStore.jks rename to common/transport/lwm2m/src/main/resources/lwm2mserver.jks diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java deleted file mode 100644 index cb2af9eb3f..0000000000 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/lwm2m/LwM2MTransportConfigServer.java +++ /dev/null @@ -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(); - } -} diff --git a/transport/lwm2m/src/main/data/credentials/serverKeyStore.jks b/transport/lwm2m/src/main/data/lwm2mserver.jks similarity index 100% rename from transport/lwm2m/src/main/data/credentials/serverKeyStore.jks rename to transport/lwm2m/src/main/data/lwm2mserver.jks From 8b3e34f0ef9d853b18eed5136d145f638c3a290d Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 4 May 2021 10:33:54 +0300 Subject: [PATCH 3/6] Refactoring of LwM2M transport --- ...TransportBootstrapServerConfiguration.java | 21 ++-- .../secure/LwM2MBootstrapSecurityStore.java | 15 ++- ...LwM2mCredentialsSecurityInfoValidator.java | 17 +-- ...a => DefaultLwM2MTransportMsgHandler.java} | 105 +++++++++--------- ...java => DefaultLwM2mTransportService.java} | 77 +++++++++---- .../lwm2m/server/LwM2MTransportService.java | 22 ++++ .../lwm2m/server/LwM2mServerListener.java | 4 +- .../lwm2m/server/LwM2mSessionMsgListener.java | 4 +- .../lwm2m/server/LwM2mTransportContext.java | 32 ++++++ ...ice.java => LwM2mTransportMsgHandler.java} | 6 +- .../lwm2m/server/LwM2mTransportRequest.java | 37 +++--- ...r.java => LwM2mTransportServerHelper.java} | 83 ++++++-------- .../LwM2mTransportServerInitializer.java | 31 ------ .../server/LwM2mVersionedModelProvider.java | 16 +-- .../lwm2m/server/client/LwM2mClient.java | 4 +- .../common/transport/TransportContext.java | 4 +- 16 files changed, 251 insertions(+), 227 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{LwM2mTransportServiceImpl.java => DefaultLwM2MTransportMsgHandler.java} (93%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{LwM2mTransportServerConfiguration.java => DefaultLwM2mTransportService.java} (83%) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.java rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{LwM2mTransportService.java => LwM2mTransportMsgHandler.java} (94%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{LwM2mTransportContextServer.java => LwM2mTransportServerHelper.java} (74%) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java index 2c0e01d169..052a4f80e1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java @@ -30,7 +30,8 @@ import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapSec import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MInMemoryBootstrapConfigStore; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2mDefaultBootstrapSessionManager; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContextServer; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; import java.math.BigInteger; import java.security.AlgorithmParameters; @@ -68,10 +69,10 @@ public class LwM2MTransportBootstrapServerConfiguration { private boolean pskMode = false; @Autowired - private LwM2MTransportContextBootstrap contextBs; + private LwM2MTransportServerConfig serverConfig; @Autowired - private LwM2mTransportContextServer contextS; + private LwM2MTransportContextBootstrap contextBs; @Autowired private LwM2MBootstrapSecurityStore lwM2MBootstrapSecurityStore; @@ -108,8 +109,8 @@ public class LwM2MTransportBootstrapServerConfiguration { /** Create and Set DTLS Config */ DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); - dtlsConfig.setRecommendedSupportedGroupsOnly(this.contextS.getLwM2MTransportServerConfig().isRecommendedSupportedGroups()); - dtlsConfig.setRecommendedCipherSuitesOnly(this.contextS.getLwM2MTransportServerConfig().isRecommendedCiphers()); + dtlsConfig.setRecommendedSupportedGroupsOnly(serverConfig.isRecommendedSupportedGroups()); + dtlsConfig.setRecommendedCipherSuitesOnly(serverConfig.isRecommendedCiphers()); if (this.pskMode) { dtlsConfig.setSupportedCipherSuites( TLS_PSK_WITH_AES_128_CCM_8, @@ -134,10 +135,10 @@ public class LwM2MTransportBootstrapServerConfiguration { private void setServerWithCredentials(LeshanBootstrapServerBuilder builder) { try { - if (this.contextS.getLwM2MTransportServerConfig().getKeyStoreValue() != null) { - KeyStore keyStoreServer = this.contextS.getLwM2MTransportServerConfig().getKeyStoreValue(); + if (serverConfig.getKeyStoreValue() != null) { + KeyStore keyStoreServer = serverConfig.getKeyStoreValue(); if (this.setBuilderX509(builder)) { - X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(this.contextS.getLwM2MTransportServerConfig().getRootCertificateAlias()); + X509Certificate rootCAX509Cert = (X509Certificate) keyStoreServer.getCertificate(serverConfig.getRootCertificateAlias()); if (rootCAX509Cert != null) { X509Certificate[] trustedCertificates = new X509Certificate[1]; trustedCertificates[0] = rootCAX509Cert; @@ -168,8 +169,8 @@ public class LwM2MTransportBootstrapServerConfiguration { * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks */ try { - X509Certificate serverCertificate = (X509Certificate) this.contextS.getLwM2MTransportServerConfig().getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getCertificateAlias()); - PrivateKey privateKey = (PrivateKey) this.contextS.getLwM2MTransportServerConfig().getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getCertificateAlias(), this.contextS.getLwM2MTransportServerConfig().getKeyStorePassword() == null ? null : this.contextS.getLwM2MTransportServerConfig().getKeyStorePassword().toCharArray()); + X509Certificate serverCertificate = (X509Certificate) serverConfig.getKeyStoreValue().getCertificate(this.contextBs.getCtxBootStrap().getCertificateAlias()); + PrivateKey privateKey = (PrivateKey) serverConfig.getKeyStoreValue().getKey(this.contextBs.getCtxBootStrap().getCertificateAlias(), serverConfig.getKeyStorePassword() == null ? null : serverConfig.getKeyStorePassword().toCharArray()); PublicKey publicKey = serverCertificate.getPublicKey(); if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) { builder.setPublicKey(serverCertificate.getPublicKey()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index 7aefe19da5..9376236af5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -34,7 +34,8 @@ import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; 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.LwM2mTransportServerHelper; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil; import java.io.IOException; @@ -59,12 +60,14 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { 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.lwM2MCredentialsSecurityInfoValidator = lwM2MCredentialsSecurityInfoValidator; this.context = context; + this.helper = helper; } @Override @@ -158,19 +161,19 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class); LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class); 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)); if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); 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()); - context.sendParametersOnThingsboardTelemetry(context.getKvLogyToThingsboard(logMsg), sessionInfo); + helper.sendParametersOnThingsboardTelemetry(helper.getKvLogyToThingsboard(logMsg), sessionInfo); return lwM2MBootstrapConfig; } else { 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()); 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; } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java index e9a09c54d3..cbba4eb490 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java +++ b/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; import com.google.gson.JsonObject; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.SecurityUtil; @@ -26,7 +27,9 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg; 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.LwM2mTransportContext; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil; import java.io.IOException; @@ -44,13 +47,11 @@ import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X5 @Slf4j @Component @TbLwM2mTransportComponent +@RequiredArgsConstructor public class LwM2mCredentialsSecurityInfoValidator { - private final LwM2mTransportContextServer contextS; - - public LwM2mCredentialsSecurityInfoValidator(LwM2mTransportContextServer contextS) { - this.contextS = contextS; - } + private final LwM2mTransportContext context; + private final LwM2MTransportServerConfig config; /** * Request to thingsboard Response from thingsboard ValidateDeviceLwM2MCredentials @@ -61,7 +62,7 @@ public class LwM2mCredentialsSecurityInfoValidator { public ReadResultSecurityStore createAndValidateCredentialsSecurityInfo(String endpoint, LwM2mTransportHandlerUtil.LwM2mTypeServer keyValue) { CountDownLatch latch = new CountDownLatch(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<>() { @Override public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) { @@ -81,7 +82,7 @@ public class LwM2mCredentialsSecurityInfoValidator { } }); try { - latch.await(contextS.getLwM2MTransportServerConfig().getTimeout(), TimeUnit.MILLISECONDS); + latch.await(config.getTimeout(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { log.error("Failed to await credentials!", e); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java similarity index 93% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index 8dabe625eb..5292c1f9fb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -54,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.SessionInfoProto; 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.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; @@ -111,44 +112,43 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandle @Slf4j @Service @TbLwM2mTransportComponent -public class LwM2mTransportServiceImpl implements LwM2mTransportService { +public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler { private ExecutorService executorRegistered; private ExecutorService executorUpdateRegistered; private ExecutorService executorUnRegistered; private LwM2mValueConverterImpl converter; - private FirmwareDataCache firmwareDataCache; - private final TransportService transportService; - - public final LwM2mTransportContextServer lwM2mTransportContextServer; - + private final LwM2mTransportContext context; + private final LwM2MTransportServerConfig config; + private final FirmwareDataCache firmwareDataCache; + private final LwM2mTransportServerHelper helper; private final LwM2mClientContext lwM2mClientContext; - - private final LeshanServer leshanServer; - private final LwM2mTransportRequest lwM2mTransportRequest; - public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer, - LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, - @Lazy LwM2mTransportRequest lwM2mTransportRequest, FirmwareDataCache firmwareDataCache) { + public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, + LwM2mClientContext lwM2mClientContext, + @Lazy LwM2mTransportRequest lwM2mTransportRequest, + FirmwareDataCache firmwareDataCache, + LwM2mTransportContext context) { this.transportService = transportService; - this.lwM2mTransportContextServer = lwM2mTransportContextServer; + this.config = config; + this.helper = helper; this.lwM2mClientContext = lwM2mClientContext; - this.leshanServer = leshanServer; this.lwM2mTransportRequest = lwM2mTransportRequest; this.firmwareDataCache = firmwareDataCache; + this.context = context; } @PostConstruct public void init() { - this.lwM2mTransportContextServer.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) lwM2mTransportContextServer.getLwM2MTransportServerConfig().getSessionReportTimeout()), lwM2mTransportContextServer.getLwM2MTransportServerConfig().getSessionReportTimeout(), TimeUnit.MILLISECONDS); - this.executorRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getRegisteredPoolSize(), + this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) config.getSessionReportTimeout()), config.getSessionReportTimeout(), TimeUnit.MILLISECONDS); + this.executorRegistered = Executors.newFixedThreadPool(this.config.getRegisteredPoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL))); - this.executorUpdateRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getUpdateRegisteredPoolSize(), + this.executorUpdateRegistered = Executors.newFixedThreadPool(this.config.getUpdateRegisteredPoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL))); - this.executorUnRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getUnRegisteredPoolSize(), + this.executorUnRegistered = Executors.newFixedThreadPool(this.config.getUnRegisteredPoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL))); this.converter = LwM2mValueConverterImpl.getInstance(); } @@ -278,10 +278,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @Override public void setCancelObservations(Registration registration) { if (registration != null) { - Set observations = leshanServer.getObservationService().getObservations(registration); + Set observations = context.getServer().getObservationService().getObservations(registration); observations.forEach(observation -> lwM2mTransportRequest.sendAllRequest(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), OBSERVE_CANCEL, - null, null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null)); + null, null, this.config.getTimeout(), null)); } } @@ -333,13 +333,13 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { msg.getSharedUpdatedList().forEach(tsKvProto -> { String pathName = tsKvProto.getKv().getKey(); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); - Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); + Object valueNew = this.helper.getValueFromKvProto(tsKvProto.getKv()); //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) { - ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig() + ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config .getModelProvider()); if (resourceModel != null && resourceModel.operations.isWritable()) { this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), valueNew, pathIdVer); @@ -360,7 +360,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { } else if (msg.getSharedDeletedCount() > 0) { msg.getSharedUpdatedList().forEach(tsKvProto -> { String pathName = tsKvProto.getKv().getKey(); - Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); + 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); } @@ -404,7 +404,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @Override public void onResourceUpdate(Optional resourceUpdateMsgOpt) { String idVer = resourceUpdateMsgOpt.get().getResourceKey(); - lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.updateResourceModel(idVer, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getModelProvider())); + lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.updateResourceModel(idVer, this.config.getModelProvider())); } /** @@ -413,7 +413,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @Override public void onResourceDelete(Optional resourceDeleteMsgOpt) { String pathIdVer = resourceDeleteMsgOpt.get().getResourceKey(); - lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.deleteResources(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getModelProvider())); + lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.deleteResources(pathIdVer, this.config.getModelProvider())); } @Override @@ -429,7 +429,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { } else { lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), lwm2mClientRpcRequest.getContentFormatName(), lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), - this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), lwm2mClientRpcRequest); + this.config.getTimeout(), lwm2mClientRpcRequest); } } catch (Exception e) { if (lwm2mClientRpcRequest == null) { @@ -546,7 +546,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @Override public void doTrigger(Registration registration, String path) { lwM2mTransportRequest.sendAllRequest(registration, path, EXECUTE, - ContentFormat.TLV.getName(), null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); + ContentFormat.TLV.getName(), null, this.config.getTimeout(), null); } /** @@ -579,7 +579,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { * * @param registration - */ - protected void onAwakeDev(Registration registration) { + @Override + public void onAwakeDev(Registration registration) { log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId()); //TODO: associate endpointId with device information. @@ -610,13 +611,14 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { * @param logMsg - text msg * @param registrationId - Id of Registration LwM2M Client */ + @Override public void sendLogsToThingsboard(String logMsg, String registrationId) { SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId); if (logMsg != null && sessionInfo != null) { if(logMsg.length() > 1024){ logMsg = logMsg.substring(0, 1024); } - this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo); + this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvLogyToThingsboard(logMsg), sessionInfo); } } @@ -640,7 +642,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { // #2 lwM2MClient.getPendingReadRequests().addAll(clientObjects); clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(), - null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null)); + null, this.config.getTimeout(), null)); } // #1 this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, READ, clientObjects); @@ -689,7 +691,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { */ private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) { LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); - if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig() + if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config .getModelProvider())) { if (FR_PATH_RESOURCE_VER_ID.equals(convertPathFromIdVerToObjectId(path)) && lwM2MClient.getFrUpdate().getCurrentFwVersion() != null @@ -728,10 +730,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); if (results != null && sessionInfo != null) { if (results.getResultAttributes().size() > 0) { - this.lwM2mTransportContextServer.sendParametersOnThingsboardAttribute(results.getResultAttributes(), sessionInfo); + this.helper.sendParametersOnThingsboardAttribute(results.getResultAttributes(), sessionInfo); } if (results.getResultTelemetries().size() > 0) { - this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(results.getResultTelemetries(), sessionInfo); + this.helper.sendParametersOnThingsboardTelemetry(results.getResultTelemetries(), sessionInfo); } } } catch (Exception e) { @@ -780,7 +782,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { ConcurrentHashMap finalParams = params; pathSend.forEach(target -> { lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(), - finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); + finalParams != null ? finalParams.get(target) : null, this.config.getTimeout(), null); }); if (OBSERVE.equals(typeOper)) { lwM2MClient.initReadValue(this, null); @@ -865,7 +867,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { LwM2mResource resourceValue = lwM2MClient != null ? getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) : null; if (resourceValue != null) { ResourceModel.Type currentType = resourceValue.getType(); - ResourceModel.Type expectedType = this.lwM2mTransportContextServer.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); + ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); Object valueKvProto = null; if (resourceValue.isMultiInstances()) { valueKvProto = new JsonObject(); @@ -882,7 +884,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { valueKvProto = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, 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) { log.error("Failed to add parameters.", e); @@ -901,7 +903,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) { LwM2mResource resourceValue = this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); 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, new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); } @@ -1094,10 +1096,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { if (pathIds.isResource()) { if (READ.equals(typeOper)) { lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, - ContentFormat.TLV.getName(), null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); + ContentFormat.TLV.getName(), null, this.config.getTimeout(), null); } else if (OBSERVE.equals(typeOper)) { lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, - null, null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); + null, null, this.config.getTimeout(), null); } } }); @@ -1153,7 +1155,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { if (!pathSend.isEmpty()) { ConcurrentHashMap finalParams = lwm2mAttributesNew; pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), - finalParams.get(target), this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null)); + finalParams.get(target), this.config.getTimeout(), null)); } }); } @@ -1170,7 +1172,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { params.clear(); params.put(OBJECT_VERSION, ""); lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), - params, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); + params, this.config.getTimeout(), null); }); } }); @@ -1183,7 +1185,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { paramAnallyzer.forEach(pathIdVer -> { if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) { lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, OBSERVE_CANCEL, null, - null, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); + null, this.config.getTimeout(), null); } } ); @@ -1193,7 +1195,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) { lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, WRITE_REPLACE, ContentFormat.TLV.getName(), valueNew, - this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); + this.config.getTimeout(), null); } else { 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", @@ -1271,7 +1273,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { // #2.1 lwM2MClient.getDelayedRequests().forEach((pathIdVer, tsKvProto) -> { this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), - this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()), pathIdVer); + this.helper.getValueFromKvProto(tsKvProto.getKv()), pathIdVer); }); } @@ -1288,7 +1290,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { return null; } else { return SessionInfoProto.newBuilder() - .setNodeId(this.lwM2mTransportContextServer.getNodeId()) + .setNodeId(this.context.getNodeId()) .setSessionIdMSB(lwM2MClient.getSessionId().getMostSignificantBits()) .setSessionIdLSB(lwM2MClient.getSessionId().getLeastSignificantBits()) .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB()) @@ -1358,7 +1360,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { if (keyNamesMap.values().size() > 0) { try { //#1.2 - TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, keyNamesMap.values()); + TransportProtos.GetAttributeRequestMsg getAttributeMsg = helper.getAdaptor().convertToGetAttributes(null, keyNamesMap.values()); transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); } catch (AdaptorException e) { log.warn("Failed to decode get attributes request", e); @@ -1406,7 +1408,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { public void readRequestToClientFirmwareVer(Registration registration) { String pathIdVer = convertPathFromObjectIdToIdVer(FR_PATH_RESOURCE_VER_ID, registration); lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, READ, ContentFormat.TLV.getName(), - null, lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); + null, config.getTimeout(), null); } /** @@ -1422,7 +1424,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(objectId); String targetIdVer = LWM2M_SEPARATOR_PATH + objectId + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0; lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), - firmwareChunk, lwM2mTransportContextServer.getLwM2MTransportServerConfig().getTimeout(), null); + firmwareChunk, config.getTimeout(), null); log.warn("updateFirmwareClient [{}] [{}]", lwM2MClient.getFrUpdate().getCurrentFwVersion(), lwM2MClient.getFrUpdate().getClientFwVersion()); } } @@ -1444,7 +1446,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { } private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { - ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig() + ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config .getModelProvider()); Integer objectId = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)).getObjectId(); String objectVer = validateObjectVerFromKey(pathIdVer); @@ -1453,9 +1455,4 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId))); } - @Override - public String getName() { - return "LWM2M"; - } - } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java similarity index 83% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index e320e3fcfa..418e3d003b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.californium.core.network.stack.BlockwiseLayer; @@ -29,14 +30,16 @@ import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.eclipse.leshan.server.security.DefaultAuthorizer; import org.eclipse.leshan.server.security.EditableSecurityStore; import org.eclipse.leshan.server.security.SecurityChecker; -import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; 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.utils.LwM2mValueConverterImpl; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.math.BigInteger; import java.security.AlgorithmParameters; import java.security.KeyFactory; @@ -66,32 +69,53 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.g @Slf4j @Component @TbLwM2mTransportComponent -public class LwM2mTransportServerConfiguration { +@RequiredArgsConstructor +public class DefaultLwM2mTransportService implements LwM2MTransportService { + private PublicKey publicKey; private PrivateKey privateKey; 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 EditableSecurityStore securityStore; private final LwM2mClientContext lwM2mClientContext; - public LwM2mTransportServerConfiguration(LwM2mTransportContextServer context, CaliforniumRegistrationStore registrationStore, EditableSecurityStore securityStore, LwM2mClientContext lwM2mClientContext) { - this.context = context; - this.registrationStore = registrationStore; - this.securityStore = securityStore; - this.lwM2mClientContext = lwM2mClientContext; + private LeshanServer server; + + @PostConstruct + public void init() { + 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 - public LeshanServer getLeshanServer() { - log.info("Starting LwM2M transport Server... PostConstruct"); - return this.getLhServer(this.context.getLwM2MTransportServerConfig().getPort(), this.context.getLwM2MTransportServerConfig().getSecurePort()); + @PreDestroy + public void shutdown() { + log.info("Stopping LwM2M transport Server!"); + server.destroy(); + log.info("LwM2M transport Server stopped!"); } private LeshanServer getLhServer(Integer serverPortNoSec, Integer serverSecurePort) { LeshanServerBuilder builder = new LeshanServerBuilder(); - builder.setLocalAddress(this.context.getLwM2MTransportServerConfig().getHost(), serverPortNoSec); - builder.setLocalSecureAddress(this.context.getLwM2MTransportServerConfig().getSecureHost(), serverSecurePort); + builder.setLocalAddress(config.getHost(), serverPortNoSec); + builder.setLocalSecureAddress(config.getSecureHost(), serverSecurePort); builder.setDecoder(new DefaultLwM2mNodeDecoder()); /** Use a magic converter to support bad type send by the UI. */ builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); @@ -103,8 +127,8 @@ public class LwM2mTransportServerConfiguration { builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort)); /** Define model provider (Create Models )*/ - LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.context); - this.context.getLwM2MTransportServerConfig().setModelProvider(modelProvider); + LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.helper, this.context); + config.setModelProvider(modelProvider); builder.setObjectModelProvider(modelProvider); /** Create credentials */ @@ -118,8 +142,8 @@ public class LwM2mTransportServerConfiguration { /** Create DTLS Config */ DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); dtlsConfig.setServerOnly(true); - dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportServerConfig().isRecommendedSupportedGroups()); - dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportServerConfig().isRecommendedCiphers()); + dtlsConfig.setRecommendedSupportedGroupsOnly(config.isRecommendedSupportedGroups()); + dtlsConfig.setRecommendedCipherSuitesOnly(config.isRecommendedCiphers()); if (this.pskMode) { dtlsConfig.setSupportedCipherSuites( TLS_PSK_WITH_AES_128_CCM_8, @@ -141,9 +165,9 @@ public class LwM2mTransportServerConfiguration { private void setServerWithCredentials(LeshanServerBuilder builder) { try { - if (this.context.getLwM2MTransportServerConfig().getKeyStoreValue() != null) { + if (config.getKeyStoreValue() != null) { if (this.setBuilderX509(builder)) { - X509Certificate rootCAX509Cert = (X509Certificate) this.context.getLwM2MTransportServerConfig().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportServerConfig().getRootCertificateAlias()); + X509Certificate rootCAX509Cert = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getRootCertificateAlias()); if (rootCAX509Cert != null) { X509Certificate[] trustedCertificates = new X509Certificate[1]; trustedCertificates[0] = rootCAX509Cert; @@ -178,8 +202,8 @@ public class LwM2mTransportServerConfiguration { private boolean setBuilderX509(LeshanServerBuilder builder) { try { - X509Certificate serverCertificate = (X509Certificate) this.context.getLwM2MTransportServerConfig().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportServerConfig().getCertificateAlias()); - PrivateKey privateKey = (PrivateKey) this.context.getLwM2MTransportServerConfig().getKeyStoreValue().getKey(this.context.getLwM2MTransportServerConfig().getCertificateAlias(), this.context.getLwM2MTransportServerConfig().getKeyStorePassword() == null ? null : this.context.getLwM2MTransportServerConfig().getKeyStorePassword().toCharArray()); + X509Certificate serverCertificate = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getCertificateAlias()); + PrivateKey privateKey = (PrivateKey) config.getKeyStoreValue().getKey(config.getCertificateAlias(), config.getKeyStorePassword() == null ? null : config.getKeyStorePassword().toCharArray()); PublicKey publicKey = serverCertificate.getPublicKey(); if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) { builder.setPublicKey(serverCertificate.getPublicKey()); @@ -208,7 +232,7 @@ public class LwM2mTransportServerConfiguration { } private void infoPramsUri(String mode) { - LwM2MTransportServerConfig lwM2MTransportServerConfig = this.context.getLwM2MTransportServerConfig(); + LwM2MTransportServerConfig lwM2MTransportServerConfig = config; log.info("Server uses [{}]: serverNoSecureURI : [{}:{}], serverSecureURI : [{}:{}]", mode, lwM2MTransportServerConfig.getHost(), lwM2MTransportServerConfig.getPort(), @@ -236,7 +260,7 @@ public class LwM2mTransportServerConfiguration { AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC"); algoParameters.init(new ECGenParameterSpec("secp256r1")); ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); - LwM2MTransportServerConfig serverConfig = this.context.getLwM2MTransportServerConfig(); + LwM2MTransportServerConfig serverConfig = config; if (StringUtils.isNotEmpty(serverConfig.getPublicX()) && StringUtils.isNotEmpty(serverConfig.getPublicY())) { byte[] publicX = Hex.decodeHex(serverConfig.getPublicX().toCharArray()); byte[] publicY = Hex.decodeHex(serverConfig.getPublicY().toCharArray()); @@ -283,4 +307,9 @@ public class LwM2mTransportServerConfiguration { params); } + @Override + public String getName() { + return "LWM2M"; + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java new file mode 100644 index 0000000000..6ad033503c --- /dev/null +++ b/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 { + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index c8108b10f5..f4084708c3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -32,9 +32,9 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandle @Slf4j public class LwM2mServerListener { - private final LwM2mTransportServiceImpl service; + private final LwM2mTransportMsgHandler service; - public LwM2mServerListener(LwM2mTransportServiceImpl service) { + public LwM2mServerListener(LwM2mTransportMsgHandler service) { this.service = service; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java index 6c0d6f29de..3a9114aa43 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java @@ -35,10 +35,10 @@ import java.util.Optional; @Slf4j public class LwM2mSessionMsgListener implements GenericFutureListener>, SessionMsgListener { - private LwM2mTransportServiceImpl service; + private DefaultLwM2MTransportMsgHandler service; private TransportProtos.SessionInfoProto sessionInfo; - public LwM2mSessionMsgListener(LwM2mTransportServiceImpl service, TransportProtos.SessionInfoProto sessionInfo) { + public LwM2mSessionMsgListener(DefaultLwM2MTransportMsgHandler service, TransportProtos.SessionInfoProto sessionInfo) { this.service = service; this.sessionInfo = sessionInfo; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.java new file mode 100644 index 0000000000..6ae204affc --- /dev/null +++ b/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; + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java similarity index 94% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java index dec1e7889d..d620492173 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java +++ b/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.Optional; -public interface LwM2mTransportService extends TbTransportService { +public interface LwM2mTransportMsgHandler { void onRegistered(Registration registration, Collection previousObsersations); @@ -60,4 +60,8 @@ public interface LwM2mTransportService extends TbTransportService { void doTrigger(Registration registration, String path); void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); + + void onAwakeDev(Registration registration); + + void sendLogsToThingsboard(String msg, String registrationId); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index a49e9dbb23..6a1be12938 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/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; +import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; 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.thingsboard.server.common.transport.TransportService; 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.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; @@ -82,35 +84,22 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandle @Slf4j @Service @TbLwM2mTransportComponent +@RequiredArgsConstructor public class LwM2mTransportRequest { private ExecutorService executorResponse; 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 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; - } + private final DefaultLwM2MTransportMsgHandler serviceImpl; @PostConstruct public void init() { this.converter = LwM2mValueConverterImpl.getInstance(); - executorResponse = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportServerConfig().getResponsePoolSize(), + executorResponse = Executors.newFixedThreadPool(this.config.getResponsePoolSize(), new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL))); } @@ -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 * {@code ObservationService#cancelObservation()} */ - leshanServer.getObservationService().cancelObservations(registration, target); + context.getServer().getObservationService().cancelObservations(registration, target); break; case EXECUTE: - resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig() + resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config .getModelProvider()); if (params != null && !resourceModel.multiple) { request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModel.type, ResourceModel.Type.STRING, resultIds)); @@ -171,7 +160,7 @@ public class LwM2mTransportRequest { break; case WRITE_REPLACE: // Request to write a String Single-Instance Resource using the TLV content format. - resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportServerConfig() + resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config .getModelProvider()); if (contentFormat.equals(ContentFormat.TLV)) { request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), @@ -232,7 +221,7 @@ public class LwM2mTransportRequest { serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); } } else if (OBSERVE_READ_ALL.name().equals(typeOper.name())) { - Set observations = leshanServer.getObservationService().getObservations(registration); + Set observations = context.getServer().getObservationService().getObservations(registration); Set 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, OBSERVE_READ_ALL.type, observationPaths); @@ -259,7 +248,7 @@ public class LwM2mTransportRequest { @SuppressWarnings("unchecked") 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()) { lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java similarity index 74% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java index e1a322f646..210f2451a7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java +++ b/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.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.model.DDFFileParser; 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.node.codec.CodecException; 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.TransportServiceCallback; -import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; @@ -62,34 +60,16 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandle @Slf4j @Component @TbLwM2mTransportComponent -public class LwM2mTransportContextServer extends TransportContext { +@RequiredArgsConstructor +public class LwM2mTransportServerHelper { - - private final LwM2MTransportServerConfig lwM2MTransportServerConfig; + private final LwM2mTransportContext context; private final TransportService transportService; - private final TransportResourceCache transportResourceCache; - - @Getter private final LwM2MJsonAdaptor adaptor; - public LwM2mTransportContextServer(LwM2MTransportServerConfig lwM2MTransportServerConfig, TransportService transportService, TransportResourceCache transportResourceCache, LwM2MJsonAdaptor adaptor) { - this.lwM2MTransportServerConfig = lwM2MTransportServerConfig; - this.transportService = transportService; - this.transportResourceCache = transportResourceCache; - this.adaptor = adaptor; - } - - public LwM2MTransportServerConfig getLwM2MTransportServerConfig() { - return this.lwM2MTransportServerConfig; - } - - public TransportResourceCache getTransportResourceCache() { - return this.transportResourceCache; - } - /** * send to Thingsboard Attribute || Telemetry * @@ -134,7 +114,7 @@ public class LwM2mTransportContextServer extends TransportContext { */ public SessionInfoProto getValidateSessionInfo(TransportProtos.ValidateDeviceCredentialsResponseMsg msg, long mostSignificantBits, long leastSignificantBits) { return SessionInfoProto.newBuilder() - .setNodeId(this.getNodeId()) + .setNodeId(context.getNodeId()) .setSessionIdMSB(mostSignificantBits) .setSessionIdLSB(leastSignificantBits) .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB()) @@ -165,8 +145,8 @@ public class LwM2mTransportContextServer extends TransportContext { * @param logMsg - info about Logs * @return- KeyValueProto for telemetry (Logs) */ - public List getKvLogyToThingsboard(String logMsg) { - List result = new ArrayList<>(); + public List getKvLogyToThingsboard(String logMsg) { + List result = new ArrayList<>(); result.add(TransportProtos.KeyValueProto.newBuilder() .setKey(LOG_LW2M_TELEMETRY) .setType(TransportProtos.KeyValueType.STRING_V) @@ -179,32 +159,31 @@ public class LwM2mTransportContextServer extends TransportContext { * @throws CodecException - */ - public TransportProtos.KeyValueProto getKvAttrTelemetryToThingsboard(ResourceModel.Type resourceType, String resourceName, Object value, boolean isMultiInstances) { - TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(resourceName); - if (isMultiInstances) { - kvProto.setType(TransportProtos.KeyValueType.JSON_V) - .setJsonV((String) value); + public TransportProtos.KeyValueProto getKvAttrTelemetryToThingsboard(ResourceModel.Type resourceType, String resourceName, Object value, boolean isMultiInstances) { + TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(resourceName); + if (isMultiInstances) { + kvProto.setType(TransportProtos.KeyValueType.JSON_V) + .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); } - public Object getValueFromKvProto (TransportProtos.KeyValueProto kv) { + public Object getValueFromKvProto(TransportProtos.KeyValueProto kv) { switch (kv.getType()) { case BOOLEAN_V: return kv.getBoolV(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java index 4e9b8a97fc..1b1ace3c5b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java @@ -30,35 +30,4 @@ import javax.annotation.PreDestroy; @TbLwM2mTransportComponent public class LwM2mTransportServerInitializer { - @Autowired - private LwM2mTransportServiceImpl service; - - @Autowired - private LeshanServer leshanServer; - - @Autowired - private LwM2mTransportContextServer context; - - @PostConstruct - public void init() { - if (this.context.getLwM2MTransportServerConfig().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!"); - } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java index 3705547325..bb27b66d69 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java +++ b/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; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.model.DefaultDDFFileValidator; 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; @Slf4j +@RequiredArgsConstructor public class LwM2mVersionedModelProvider implements LwM2mModelProvider { /** @@ -46,12 +48,8 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider { * Value = TenantId */ private final LwM2mClientContext lwM2mClientContext; - private final LwM2mTransportContextServer lwM2mTransportContextServer; - - public LwM2mVersionedModelProvider(LwM2mClientContext lwM2mClientContext, LwM2mTransportContextServer lwM2mTransportContextServer) { - this.lwM2mClientContext = lwM2mClientContext; - this.lwM2mTransportContextServer = lwM2mTransportContextServer; - } + private final LwM2mTransportServerHelper helper; + private final LwM2mTransportContext context; private String getKeyIdVer(Integer objectId, String version) { 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) { String key = getKeyIdVer(objectId, version); - Optional tbResource = lwM2mTransportContextServer - .getTransportResourceCache() - .get(this.tenantId, LWM2M_MODEL, key); + Optional tbResource = context.getTransportResourceCache().get(this.tenantId, LWM2M_MODEL, key); - return tbResource.map(resource -> lwM2mTransportContextServer.parseFromXmlToObjectModel( + return tbResource.map(resource -> helper.parseFromXmlToObjectModel( Base64.getDecoder().decode(resource.getData()), key + ".xml", new DefaultDDFFileValidator())).orElse(null); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index b79f6b5241..df212790da 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/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.ValidateDeviceCredentialsResponseMsg; 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 java.util.Collection; @@ -172,7 +172,7 @@ public class LwM2mClient implements Cloneable { .collect(Collectors.toSet()); } - public void initReadValue(LwM2mTransportServiceImpl serviceImpl, String path) { + public void initReadValue(DefaultLwM2MTransportMsgHandler serviceImpl, String path) { if (path != null) { this.pendingReadRequests.remove(path); } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java index d5f779ab48..d4798108c6 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java @@ -51,11 +51,13 @@ public abstract class TransportContext { @Getter private ExecutorService executor; - @Getter @Autowired private FirmwareDataCache firmwareDataCache; + @Autowired + private TransportResourceCache transportResourceCache; + @PostConstruct public void init() { executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass()); From 54543ae4b51078ed6cb8ff33b8da45d02904bd69 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 4 May 2021 11:47:38 +0300 Subject: [PATCH 4/6] Merge improvements from outdated PR 4527 --- .../src/main/resources/thingsboard.yml | 1 + .../lwm2m/client/LwM2mSoftwareUpdate.java | 27 +++++ .../config/LwM2MTransportServerConfig.java | 17 ++- .../DefaultLwM2MTransportMsgHandler.java | 6 +- .../server/DefaultLwM2mTransportService.java | 30 ++--- .../lwm2m/server/LwM2mNetworkConfig.java | 105 +++++++++--------- .../lwm2m/server/LwM2mTransportRequest.java | 47 ++++++-- 7 files changed, 146 insertions(+), 87 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/client/LwM2mSoftwareUpdate.java diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 83f1c8fa02..d14adc0573 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -674,6 +674,7 @@ transport: 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 redis.enabled: "${LWM2M_REDIS_ENABLED:false}" snmp: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/client/LwM2mSoftwareUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/client/LwM2mSoftwareUpdate.java new file mode 100644 index 0000000000..ed8b316fed --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java index a68359a1ca..dbab13d0d1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java @@ -29,6 +29,7 @@ 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; @@ -136,18 +137,26 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { @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 { - File keyStoreFile = new File(Resources.getResource(keyStorePathFile).toURI()); + 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.error("Unable to lookup LwM2M keystore. Reason: " + e.getMessage(), e); - throw new RuntimeException("Failed to lookup LwM2M keystore", e); +// 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); } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index 5292c1f9fb..8e059e5a3b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -87,6 +87,7 @@ 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.transport.lwm2m.server.LwM2mTransportHandlerUtil.CLIENT_NOT_AUTHORIZED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.DEVICE_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.FR_OBJECT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.FR_PATH_RESOURCE_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandlerUtil.LOG_LW2M_INFO; @@ -1420,9 +1421,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler int chunkSize = 0; int chunk = 0; byte[] firmwareChunk = firmwareDataCache.get(lwM2MClient.getFrUpdate().getCurrentFwId().toString(), chunkSize, chunk); - Integer objectId = 5; - String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(objectId); - String targetIdVer = LWM2M_SEPARATOR_PATH + objectId + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0; + 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()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 418e3d003b..c48fce0bcd 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -117,29 +117,29 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { builder.setLocalAddress(config.getHost(), serverPortNoSec); builder.setLocalSecureAddress(config.getSecureHost(), serverSecurePort); 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())); - /** Create CoAP Config */ + /* Create CoAP Config */ NetworkConfig networkConfig = getCoapConfig(serverPortNoSec, serverSecurePort); BlockwiseLayer blockwiseLayer = new BlockwiseLayer(networkConfig); builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort)); - /** Define model provider (Create Models )*/ + /* Define model provider (Create Models )*/ LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.helper, this.context); config.setModelProvider(modelProvider); builder.setObjectModelProvider(modelProvider); - /** Create credentials */ + /* Create credentials */ this.setServerWithCredentials(builder); - /** Set securityStore with new registrationStore */ + /* Set securityStore with new registrationStore */ builder.setSecurityStore(securityStore); builder.setRegistrationStore(registrationStore); - /** Create DTLS Config */ + /* Create DTLS Config */ DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); dtlsConfig.setServerOnly(true); dtlsConfig.setRecommendedSupportedGroupsOnly(config.isRecommendedSupportedGroups()); @@ -156,10 +156,10 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256); } - /** Set DTLS Config */ + /* Set DTLS Config */ builder.setDtlsConfig(dtlsConfig); - /** Create LWM2M server */ + /* Create LWM2M server */ return builder.build(); } @@ -173,10 +173,10 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { trustedCertificates[0] = rootCAX509Cert; builder.setTrustedCertificates(trustedCertificates); } else { - /** by default trust all */ + /* by default trust all */ builder.setTrustedCertificates(new X509Certificate[0]); } - /** Set securityStore with registrationStore*/ + /* Set securityStore with registrationStore*/ builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() { @Override protected boolean matchX509Identity(String endpoint, String receivedX509CommonName, @@ -189,7 +189,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { this.infoPramsUri("RPK"); this.infoParamsServerKey(this.publicKey, this.privateKey); } else { - /** by default trust all */ + /* by default trust all */ builder.setTrustedCertificates(new X509Certificate[0]); log.info("Unable to load X509 files for LWM2MServer"); this.pskMode = true; @@ -256,7 +256,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { } 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"); algoParameters.init(new ECGenParameterSpec("secp256r1")); ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class); @@ -280,17 +280,17 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { } private void infoParamsServerKey(PublicKey publicKey, PrivateKey privateKey) { - /** Get x coordinate */ + /* Get x coordinate */ byte[] x = ((ECPublicKey) publicKey).getW().getAffineX().toByteArray(); if (x[0] == 0) x = Arrays.copyOfRange(x, 1, x.length); - /** Get Y coordinate */ + /* Get Y coordinate */ byte[] y = ((ECPublicKey) publicKey).getW().getAffineY().toByteArray(); if (y[0] == 0) y = Arrays.copyOfRange(y, 1, y.length); - /** Get Curves params */ + /* Get Curves params */ String params = ((ECPublicKey) publicKey).getParams().toString(); String privHex = Hex.encodeHexString(privateKey.getEncoded()); log.info(" \n- Public Key (Hex): [{}] \n" + diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java index 4cf12941a5..3de5846ca0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java @@ -23,73 +23,72 @@ public class LwM2mNetworkConfig { 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); + /* + 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 + /* + 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. + /* + 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. + /* + !!! 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, 8192); 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 + /* + 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 + /* + 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 + /* + 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); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index 6a1be12938..3557af6d40 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -404,17 +404,40 @@ public class LwM2mTransportRequest { } } - private void infoWriteResponse(Registration registration, LwM2mResponse response, - DownlinkRequest request) { - 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: Update finished successfully: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s", - LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), - response.getCode().getName(), request.getPath().toString(), value); - serviceImpl.sendLogsToThingsboard(msg, registration.getId()); - log.trace("[{}] [{}] [{}] - [{}] [{}] Update finished successfully: [{}]", request.getClass().getName().toString(), registration.getEndpoint(), - ((Response) response.getCoapResponse()).getCode(), response.getCode(), - request.getPath().toString(), value); + 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); + } } } From 8a4c0a71ce3e38c3074aef2cb4bd2f32e96f81d2 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 4 May 2021 12:00:16 +0300 Subject: [PATCH 5/6] Reduced warnings --- .../transport/lwm2m/config/LwM2MTransportServerConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java index dbab13d0d1..cadb98a766 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java @@ -154,7 +154,7 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { keyStoreValue = KeyStore.getInstance(keyStoreType); keyStoreValue.load(inKeyStore, keyStorePassword == null ? null : keyStorePassword.toCharArray()); } catch (Exception e) { - log.error("Unable to lookup LwM2M keystore. Reason: " + e.getMessage(), 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); } From 59606f8330ce2c056b88c8eac94cefab03a764c8 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 4 May 2021 19:44:15 +0300 Subject: [PATCH 6/6] Add support for dashboboards in mobile app. Introduce widget mobile actions. --- .../controller/DashboardController.java | 47 +++ .../src/app/core/services/mobile.service.ts | 70 ++++ .../dashboard-page.component.html | 6 +- .../dashboard-page.component.scss | 9 + .../dashboard-page.component.ts | 23 +- .../dashboard-toolbar.component.scss | 36 ++ .../default-state-controller.component.ts | 5 + .../entity-state-controller.component.ts | 3 + .../home/components/home-components.module.ts | 3 + .../mobile-action-editor.component.html | 100 ++++++ .../action/mobile-action-editor.component.ts | 253 ++++++++++++++ .../action/mobile-action-editor.models.ts | 317 ++++++++++++++++++ .../widget-action-dialog.component.html | 5 + .../action/widget-action-dialog.component.ts | 30 +- .../components/widget/widget.component.ts | 182 +++++++++- .../home/models/widget-component.models.ts | 5 +- ui-ngx/src/app/shared/models/widget.models.ts | 97 +++++- .../assets/locale/locale.constant-en_US.json | 15 +- 18 files changed, 1186 insertions(+), 20 deletions(-) create mode 100644 ui-ngx/src/app/core/services/mobile.service.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.models.ts diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 8616da8714..570f925c45 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -533,6 +533,37 @@ public class DashboardController extends BaseController { } } + @PreAuthorize("isAuthenticated()") + @RequestMapping(value = "/dashboard/home/info", method = RequestMethod.GET) + @ResponseBody + public HomeDashboardInfo getHomeDashboardInfo() throws ThingsboardException { + try { + SecurityUser securityUser = getCurrentUser(); + if (securityUser.isSystemAdmin()) { + return null; + } + User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); + JsonNode additionalInfo = user.getAdditionalInfo(); + HomeDashboardInfo homeDashboardInfo; + homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); + if (homeDashboardInfo == null) { + if (securityUser.isCustomerUser()) { + Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId()); + additionalInfo = customer.getAdditionalInfo(); + homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); + } + if (homeDashboardInfo == null) { + Tenant tenant = tenantService.findTenantById(securityUser.getTenantId()); + additionalInfo = tenant.getAdditionalInfo(); + homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo); + } + } + return homeDashboardInfo; + } catch (Exception e) { + throw handleException(e); + } + } + @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.GET) @ResponseBody @@ -582,6 +613,22 @@ public class DashboardController extends BaseController { } } + private HomeDashboardInfo extractHomeDashboardInfoFromAdditionalInfo(JsonNode additionalInfo) { + try { + if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) { + String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText(); + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); + checkDashboardId(dashboardId, Operation.READ); + boolean hideDashboardToolbar = true; + if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) { + hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean(); + } + return new HomeDashboardInfo(dashboardId, hideDashboardToolbar); + } + } catch (Exception e) {} + return null; + } + private HomeDashboard extractHomeDashboardFromAdditionalInfo(JsonNode additionalInfo) { try { if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) { diff --git a/ui-ngx/src/app/core/services/mobile.service.ts b/ui-ngx/src/app/core/services/mobile.service.ts new file mode 100644 index 0000000000..b6774f37d0 --- /dev/null +++ b/ui-ngx/src/app/core/services/mobile.service.ts @@ -0,0 +1,70 @@ +/// +/// 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 { Inject, Injectable } from '@angular/core'; +import { WINDOW } from '@core/services/window.service'; +import { isDefined } from '@core/utils'; +import { MobileActionResult, WidgetMobileActionResult, WidgetMobileActionType } from '@shared/models/widget.models'; +import { from, of } from 'rxjs'; +import { Observable } from 'rxjs/internal/Observable'; +import { catchError } from 'rxjs/operators'; + +const dashboardStateNameHandler = 'tbMobileDashboardStateNameHandler'; +const mobileHandler = 'tbMobileHandler'; + +// @dynamic +@Injectable({ + providedIn: 'root' +}) +export class MobileService { + + private readonly mobileApp; + private readonly mobileChannel; + + constructor(@Inject(WINDOW) private window: Window) { + const w = (this.window as any); + this.mobileChannel = w.flutter_inappwebview; + this.mobileApp = isDefined(this.mobileChannel); + } + + public isMobileApp(): boolean { + return this.mobileApp; + } + + public handleDashboardStateName(name: string) { + if (this.mobileApp) { + this.mobileChannel.callHandler(dashboardStateNameHandler, name); + } + } + + public handleWidgetMobileAction(type: WidgetMobileActionType, ...args: any[]): + Observable> { + if (this.mobileApp) { + return from( + this.mobileChannel.callHandler(mobileHandler, type, ...args) as Promise>).pipe( + catchError((err: Error) => { + return of({ + hasError: true, + error: err?.message ? err.message : `Failed to execute mobile action ${type}` + } as WidgetMobileActionResult); + }) + ); + } else { + return of(null); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html index 3f03ea4fff..23555da05d 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
-
@@ -49,7 +49,7 @@ div { + height: $mobile-app-toolbar-height; + } + } + } + } + } + } + } + } + } +} + tb-dashboard-toolbar { mat-fab-toolbar { mat-fab-trigger { diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/states/default-state-controller.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/states/default-state-controller.component.ts index 6d2e756489..27e613a64e 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/states/default-state-controller.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/states/default-state-controller.component.ts @@ -26,6 +26,7 @@ import { UtilsService } from '@core/services/utils.service'; import { base64toObj, objToBase64URI } from '@app/core/utils'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { EntityService } from '@core/http/entity.service'; +import { MobileService } from '@core/services/mobile.service'; @Component({ selector: 'tb-default-state-controller', @@ -40,6 +41,7 @@ export class DefaultStateControllerComponent extends StateControllerComponent im protected statesControllerService: StatesControllerService, private utils: UtilsService, private entityService: EntityService, + private mobileService: MobileService, private dashboardUtils: DashboardUtilsService) { super(router, route, ngZone, statesControllerService); } @@ -229,6 +231,9 @@ export class DefaultStateControllerComponent extends StateControllerComponent im private gotoState(stateId: string, update: boolean, openRightLayout?: boolean) { if (this.dashboardCtrl.dashboardCtx.state !== stateId) { this.dashboardCtrl.openDashboardState(stateId, openRightLayout); + if (stateId && this.statesValue[stateId]) { + this.mobileService.handleDashboardStateName(this.getStateName(stateId, this.statesValue[stateId])); + } if (update) { this.updateLocation(); } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/states/entity-state-controller.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/states/entity-state-controller.component.ts index 39b340901d..cd5628dc06 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/states/entity-state-controller.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/states/entity-state-controller.component.ts @@ -28,6 +28,7 @@ import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { EntityService } from '@core/http/entity.service'; import { EntityType } from '@shared/models/entity-type.models'; import { map, tap } from 'rxjs/operators'; +import { MobileService } from '@core/services/mobile.service'; @Component({ selector: 'tb-entity-state-controller', @@ -44,6 +45,7 @@ export class EntityStateControllerComponent extends StateControllerComponent imp protected statesControllerService: StatesControllerService, private utils: UtilsService, private entityService: EntityService, + private mobileService: MobileService, private dashboardUtils: DashboardUtilsService) { super(router, route, ngZone, statesControllerService); } @@ -270,6 +272,7 @@ export class EntityStateControllerComponent extends StateControllerComponent imp private gotoState(stateId: string, update: boolean, openRightLayout?: boolean) { this.dashboardCtrl.openDashboardState(stateId, openRightLayout); + this.mobileService.handleDashboardStateName(this.getStateName(this.stateObject.length - 1)); if (update) { this.updateLocation(); } diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index f2208757d4..b39a566149 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -55,6 +55,7 @@ import { ManageWidgetActionsComponent } from '@home/components/widget/action/man import { WidgetActionDialogComponent } from '@home/components/widget/action/widget-action-dialog.component'; import { CustomActionPrettyResourcesTabsComponent } from '@home/components/widget/action/custom-action-pretty-resources-tabs.component'; import { CustomActionPrettyEditorComponent } from '@home/components/widget/action/custom-action-pretty-editor.component'; +import { MobileActionEditorComponent } from '@home/components/widget/action/mobile-action-editor.component'; import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service'; import { CustomDialogContainerComponent } from '@home/components/widget/dialog/custom-dialog-container.component'; import { ImportExportService } from '@home/components/import-export/import-export.service'; @@ -183,6 +184,7 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag WidgetActionDialogComponent, CustomActionPrettyResourcesTabsComponent, CustomActionPrettyEditorComponent, + MobileActionEditorComponent, CustomDialogContainerComponent, ImportDialogComponent, ImportDialogCsvComponent, @@ -296,6 +298,7 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag WidgetActionDialogComponent, CustomActionPrettyResourcesTabsComponent, CustomActionPrettyEditorComponent, + MobileActionEditorComponent, CustomDialogContainerComponent, ImportDialogComponent, ImportDialogCsvComponent, diff --git a/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.html b/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.html new file mode 100644 index 0000000000..cdb4615068 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.html @@ -0,0 +1,100 @@ + +
+ + widget-action.mobile.action-type + + + {{ mobileActionTypeTranslations.get(mobileActionType[actionType]) | translate }} + + + + {{ 'widget-action.mobile.action-type-required' | translate }} + + +
+ + + + + + + + + + + + + + + + + + +
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.ts new file mode 100644 index 0000000000..966268faa3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.ts @@ -0,0 +1,253 @@ +/// +/// 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, Input, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@app/core/core.state'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { deepClone } from '@core/utils'; +import { + WidgetMobileActionDescriptor, + WidgetMobileActionType, widgetMobileActionTypeTranslationMap +} from '@shared/models/widget.models'; +import { CustomActionEditorCompleter } from '@home/components/widget/action/custom-action.models'; +import { JsFuncComponent } from '@shared/components/js-func.component'; +import { + getDefaultGetLocationFunction, getDefaultGetPhoneNumberFunction, + getDefaultHandleEmptyResultFunction, + getDefaultHandleErrorFunction, + getDefaultProcessImageFunction, + getDefaultProcessLaunchResultFunction, getDefaultProcessLocationFunction, + getDefaultProcessQrCodeFunction +} from '@home/components/widget/action/mobile-action-editor.models'; + +@Component({ + selector: 'tb-mobile-action-editor', + templateUrl: './mobile-action-editor.component.html', + styleUrls: [], + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MobileActionEditorComponent), + multi: true + }] +}) +export class MobileActionEditorComponent implements ControlValueAccessor, OnInit { + + @ViewChildren(JsFuncComponent) jsFuncComponents: QueryList; + + mobileActionTypes = Object.keys(WidgetMobileActionType); + mobileActionTypeTranslations = widgetMobileActionTypeTranslationMap; + mobileActionType = WidgetMobileActionType; + + customActionEditorCompleter = CustomActionEditorCompleter; + + mobileActionFormGroup: FormGroup; + mobileActionTypeFormGroup: FormGroup; + + private requiredValue: boolean; + get required(): boolean { + return this.requiredValue; + } + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + + @Input() + disabled: boolean; + + private propagateChange = (v: any) => { }; + + constructor(private store: Store, + private fb: FormBuilder) { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + ngOnInit() { + this.mobileActionFormGroup = this.fb.group({ + type: [null, Validators.required], + handleEmptyResultFunction: [null], + handleErrorFunction: [null] + }); + this.mobileActionFormGroup.get('type').valueChanges.subscribe((type: WidgetMobileActionType) => { + let action: WidgetMobileActionDescriptor = this.mobileActionFormGroup.value; + if (this.mobileActionTypeFormGroup) { + action = {...action, ...this.mobileActionTypeFormGroup.value}; + } + this.updateMobileActionType(type, action); + }); + this.mobileActionFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.mobileActionFormGroup.disable({emitEvent: false}); + if (this.mobileActionTypeFormGroup) { + this.mobileActionTypeFormGroup.disable({emitEvent: false}); + } + } else { + this.mobileActionFormGroup.enable({emitEvent: false}); + if (this.mobileActionTypeFormGroup) { + this.mobileActionTypeFormGroup.enable({emitEvent: false}); + } + } + } + + writeValue(value: WidgetMobileActionDescriptor | null): void { + this.mobileActionFormGroup.patchValue({type: value?.type, + handleEmptyResultFunction: value?.handleEmptyResultFunction, + handleErrorFunction: value?.handleErrorFunction}, {emitEvent: false}); + this.updateMobileActionType(value?.type, value); + } + + private updateModel() { + let descriptor: WidgetMobileActionDescriptor = null; + if (this.mobileActionFormGroup.valid && this.mobileActionTypeFormGroup.valid) { + descriptor = { ...this.mobileActionFormGroup.getRawValue(), ...this.mobileActionTypeFormGroup.getRawValue() }; + } + this.propagateChange(descriptor); + } + + private updateMobileActionType(type?: WidgetMobileActionType, action?: WidgetMobileActionDescriptor) { + const prevType = action?.type; + const targetType = type || prevType; + const changed = prevType !== type; + if (changed && targetType) { + let handleEmptyResultFunction = action?.handleEmptyResultFunction; + const defaultHandleEmptyResultFunction = getDefaultHandleEmptyResultFunction(targetType); + if (defaultHandleEmptyResultFunction !== handleEmptyResultFunction) { + handleEmptyResultFunction = getDefaultHandleEmptyResultFunction(type); + this.mobileActionFormGroup.patchValue({handleEmptyResultFunction}, {emitEvent: false}); + } + let handleErrorFunction = action?.handleErrorFunction; + const defaultHandleErrorFunction = getDefaultHandleErrorFunction(targetType); + if (defaultHandleErrorFunction !== handleErrorFunction) { + handleErrorFunction = getDefaultHandleErrorFunction(type); + this.mobileActionFormGroup.patchValue({handleErrorFunction}, {emitEvent: false}); + } + } + this.mobileActionTypeFormGroup = this.fb.group({}); + if (type) { + switch (type) { + case WidgetMobileActionType.takePictureFromGallery: + case WidgetMobileActionType.takePhoto: + case WidgetMobileActionType.takeScreenshot: + let processImageFunction = action?.processImageFunction; + if (changed) { + const defaultProcessImageFunction = getDefaultProcessImageFunction(targetType); + if (defaultProcessImageFunction !== processImageFunction) { + processImageFunction = getDefaultProcessImageFunction(type); + } + } + this.mobileActionTypeFormGroup.addControl( + 'processImageFunction', + this.fb.control(processImageFunction, []) + ); + break; + case WidgetMobileActionType.mapDirection: + case WidgetMobileActionType.mapLocation: + let getLocationFunction = action?.getLocationFunction; + let processLaunchResultFunction = action?.processLaunchResultFunction; + if (changed) { + const defaultGetLocationFunction = getDefaultGetLocationFunction(); + if (defaultGetLocationFunction !== getLocationFunction) { + getLocationFunction = defaultGetLocationFunction; + } + const defaultProcessLaunchResultFunction = getDefaultProcessLaunchResultFunction(targetType); + if (defaultProcessLaunchResultFunction !== processLaunchResultFunction) { + processLaunchResultFunction = getDefaultProcessLaunchResultFunction(type); + } + } + this.mobileActionTypeFormGroup.addControl( + 'getLocationFunction', + this.fb.control(getLocationFunction, [Validators.required]) + ); + this.mobileActionTypeFormGroup.addControl( + 'processLaunchResultFunction', + this.fb.control(processLaunchResultFunction, []) + ); + break; + case WidgetMobileActionType.scanQrCode: + let processQrCodeFunction = action?.processQrCodeFunction; + if (changed) { + const defaultProcessQrCodeFunction = getDefaultProcessQrCodeFunction(); + if (defaultProcessQrCodeFunction !== processQrCodeFunction) { + processQrCodeFunction = defaultProcessQrCodeFunction; + } + } + this.mobileActionTypeFormGroup.addControl( + 'processQrCodeFunction', + this.fb.control(processQrCodeFunction, []) + ); + break; + case WidgetMobileActionType.makePhoneCall: + let getPhoneNumberFunction = action?.getPhoneNumberFunction; + processLaunchResultFunction = action?.processLaunchResultFunction; + if (changed) { + const defaultGetPhoneNumberFunction = getDefaultGetPhoneNumberFunction(); + if (defaultGetPhoneNumberFunction !== getPhoneNumberFunction) { + getPhoneNumberFunction = defaultGetPhoneNumberFunction; + } + const defaultProcessLaunchResultFunction = getDefaultProcessLaunchResultFunction(targetType); + if (defaultProcessLaunchResultFunction !== processLaunchResultFunction) { + processLaunchResultFunction = getDefaultProcessLaunchResultFunction(type); + } + } + this.mobileActionTypeFormGroup.addControl( + 'getPhoneNumberFunction', + this.fb.control(getPhoneNumberFunction, [Validators.required]) + ); + this.mobileActionTypeFormGroup.addControl( + 'processLaunchResultFunction', + this.fb.control(processLaunchResultFunction, []) + ); + break; + case WidgetMobileActionType.getLocation: + let processLocationFunction = action?.processLocationFunction; + if (changed) { + const defaultProcessLocationFunction = getDefaultProcessLocationFunction(); + if (defaultProcessLocationFunction !== processLocationFunction) { + processLocationFunction = defaultProcessLocationFunction; + } + } + this.mobileActionTypeFormGroup.addControl( + 'processLocationFunction', + this.fb.control(processLocationFunction, [Validators.required]) + ); + break; + } + } + this.mobileActionTypeFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + } + + public validateOnSubmit() { + for (const jsFuncComponent of this.jsFuncComponents.toArray()) { + jsFuncComponent.validateOnSubmit(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.models.ts b/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.models.ts new file mode 100644 index 0000000000..ea32b812be --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.models.ts @@ -0,0 +1,317 @@ +/// +/// 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 { WidgetMobileActionType } from '@shared/models/widget.models'; + +const processImageFunctionTemplate = + '// Function body to process image obtained as a result of mobile action (take photo, take image from gallery, etc.). \n' + + '// - imageUrl - image URL in base64 data format\n\n' + + 'showImageDialog(\'--TITLE--\', imageUrl);\n' + + '//saveEntityImageAttribute(\'image\', imageUrl);\n' + + '\n' + + 'function showImageDialog(title, imageUrl) {\n' + + ' setTimeout(function() {\n' + + ' widgetContext.customDialog.customDialog(imageDialogTemplate, ImageDialogController, {imageUrl: imageUrl, title: title}).subscribe();\n' + + ' }, 100);\n' + + '}\n' + + '\n' + + 'function saveEntityImageAttribute(attributeName, imageUrl) {\n' + + ' if (entityId) {\n' + + ' let attributes = [{\n' + + ' key: attributeName, value: imageUrl\n' + + ' }];\n' + + ' widgetContext.attributeService.saveEntityAttributes(entityId, "SERVER_SCOPE", attributes).subscribe(\n' + + ' function() {\n' + + ' widgetContext.showSuccessToast(\'Image attribute saved!\');\n' + + ' },\n' + + ' function(error) {\n' + + ' widgetContext.dialogs.alert(\'Image attribute save failed\', JSON.stringify(error));\n' + + ' }\n' + + ' );\n' + + ' }\n' + + '}\n' + + '\n' + + 'var\n' + + ' imageDialogTemplate =\n' + + ' \'
\' +\n' + + ' \'
\' +\n' + + ' \'\' +\n' + + ' \'

{{title}}

\' +\n' + + ' \'\' +\n' + + ' \'\' +\n' + + ' \'
\' +\n' + + ' \'
\' +\n' + + ' \'
\' +\n' + + ' \'
\' +\n' + + ' \'
\' +\n' + + ' \'\' +\n' + + ' \'
\' +\n' + + ' \'
\' +\n' + + ' \'
\' +\n' + + ' \'
\' +\n' + + ' \'
\' +\n' + + ' \'\' +\n' + + ' \'\' +\n' + + ' \'
\' +\n' + + ' \'
\' +\n' + + ' \'
\';\n' + + '\n' + + 'function ImageDialogController(instance) {\n' + + ' let vm = instance;\n' + + ' vm.title = vm.data.title;\n' + + ' vm.imageUrl = vm.data.imageUrl;\n' + + ' vm.close = function ()\n' + + ' {\n' + + ' vm.dialogRef.close(null);\n' + + ' }\n' + + '}\n'; + +const processLaunchResultFunctionTemplate = + '// Optional function body to process result of attempt to launch external mobile application (for ex. map application or phone call application). \n' + + '// - launched - boolean value indicating if the external application was successfully launched.\n\n' + + 'showLaunchStatusDialog(\'--TITLE--\', launched);\n' + + '\n' + + 'function showLaunchStatusDialog(title, status) {\n' + + ' setTimeout(function() {\n' + + ' widgetContext.dialogs.alert(title, status ? \'Successfully launched\' : \'Failed to launch\').subscribe();\n' + + ' }, 100);\n' + + '}\n'; + +const processQrCodeFunction = + '// Function body to process result of QR code scanning. \n' + + '// - code - scanned QR code\n' + + '// - format - scanned QR code format\n\n' + + 'showQrCodeDialog(\'QR Code\', code, format);\n' + + '\n' + + 'function showQrCodeDialog(title, code, format) {\n' + + ' setTimeout(function() {\n' + + ' widgetContext.dialogs.alert(title, \'Code: [\'+code+\']
Format: \' + format).subscribe();\n' + + ' }, 100);\n' + + '}\n'; + +const processLocationFunction = + '// Function body to process current location of the phone. \n' + + '// - latitude - phone location latitude\n' + + '// - longitude - phone location longitude\n\n' + + 'showLocationDialog(\'Location\', latitude, longitude);\n' + + '// saveEntityLocationAttributes(\'latitude\', \'longitude\', latitude, longitude);\n' + + '\n' + + 'function showImageDialog(title, imageUrl) {\n' + + ' setTimeout(function() {\n' + + ' widgetContext.customDialog.customDialog(imageDialogTemplate, ImageDialogController, {imageUrl: imageUrl, title: title}).subscribe();\n' + + ' }, 100);\n' + + '}\n' + + '\n' + + 'function saveEntityLocationAttributes(latitudeAttributeName, longitudeAttributeName, latitude, longitude) {\n' + + ' if (entityId) {\n' + + ' let attributes = [\n' + + ' { key: latitudeAttributeName, value: latitude },\n' + + ' { key: longitudeAttributeName, value: longitude }\n' + + ' ];\n' + + ' widgetContext.attributeService.saveEntityAttributes(entityId, "SERVER_SCOPE", attributes).subscribe(\n' + + ' function() {\n' + + ' widgetContext.showSuccessToast(\'Location attributes saved!\');\n' + + ' },\n' + + ' function(error) {\n' + + ' widgetContext.dialogs.alert(\'Location attributes save failed\', JSON.stringify(error));\n' + + ' }\n' + + ' );\n' + + ' }\n' + + '}\n' + + '\n' + + '\n' + + 'function showLocationDialog(title, latitude, longitude) {\n' + + ' setTimeout(function() {\n' + + ' widgetContext.dialogs.alert(title, \'Latitude: \'+latitude+\'
Longitude: \' + longitude).subscribe();\n' + + ' }, 100);\n' + + '}'; + +const handleEmptyResultFunctionTemplate = + '// Optional function body to handle empty result. \n' + + '// Usually this happens when user cancels the action (for ex. by pressing phone back button). \n\n' + + 'showEmptyResultDialog(\'--MESSAGE--\');\n' + + '\n' + + 'function showEmptyResultDialog(message) {\n' + + ' setTimeout(function() {\n' + + ' widgetContext.dialogs.alert(\'Empty result\', message).subscribe();\n' + + ' }, 100);\n' + + '}\n'; + +const handleErrorFunctionTemplate = + '// Optional function body to handle error occurred while mobile action execution \n' + + '// - error - Error message\n\n' + + 'showErrorDialog(\'--TITLE--\', error);\n' + + '\n' + + 'function showErrorDialog(title, error) {\n' + + ' setTimeout(function() {\n' + + ' widgetContext.dialogs.alert(title, error).subscribe();\n' + + ' }, 100);\n' + + '}\n'; + +const getLocationFunctionTemplate = + '// Function body that should return location as array of two numbers (latitude, longitude) for further processing by mobile action.\n' + + '// Usually location can be obtained from entity attributes/telemetry. \n\n' + + 'return getLocationFromEntityAttributes();\n' + + '//return [30, 30]; // TEST LOCATION\n' + + '\n' + + '\n' + + 'function getLocationFromEntityAttributes() {\n' + + ' if (entityId) {\n' + + ' return widgetContext.attributeService.getEntityAttributes(entityId, \'SERVER_SCOPE\', [\'latitude\', \'longitude\']).pipe(widgetContext.rxjs.map(function(attributeData) {\n' + + ' var res = [0,0];\n' + + ' if (attributeData && attributeData.length === 2) {\n' + + ' res[0] = attributeData.filter(function (data) { return data.key === \'latitude\'})[0].value;\n' + + ' res[1] = attributeData.filter(function (data) { return data.key === \'longitude\'})[0].value;\n' + + ' }\n' + + ' return res;\n' + + ' }));\n' + + ' } else {\n' + + ' return [0,0];\n' + + ' }\n' + + '}\n'; + +const getPhoneNumberFunctionTemplate = + '// Function body that should return phone number for further processing by mobile action.\n' + + '// Usually phone number can be obtained from entity attributes/telemetry. \n\n' + + 'return getPhoneNumberFromEntityAttributes();\n' + + '//return 123456789; // TEST PHONE NUMBER\n' + + '\n' + + '\n' + + 'function getPhoneNumberFromEntityAttributes() {\n' + + ' if (entityId) {\n' + + ' return widgetContext.attributeService.getEntityAttributes(entityId, \'SERVER_SCOPE\', [\'phone\']).pipe(widgetContext.rxjs.map(function(attributeData) {\n' + + ' var res = 0;\n' + + ' if (attributeData && attributeData.length === 1) {\n' + + ' res = attributeData[0].value;\n' + + ' }\n' + + ' return res;\n' + + ' }));\n' + + ' } else {\n' + + ' return 0;\n' + + ' }\n' + + '}\n'; + +export function getDefaultProcessImageFunction(type: WidgetMobileActionType): string { + let title; + switch (type) { + case WidgetMobileActionType.takePictureFromGallery: + title = 'Gallery picture'; + break; + case WidgetMobileActionType.takePhoto: + title = 'Photo'; + break; + case WidgetMobileActionType.takeScreenshot: + title = 'Screenshot'; + break; + } + return processImageFunctionTemplate.replace('--TITLE--', title); +} + +export function getDefaultProcessLaunchResultFunction(type: WidgetMobileActionType): string { + let title; + switch (type) { + case WidgetMobileActionType.mapLocation: + title = 'Map location'; + break; + case WidgetMobileActionType.mapDirection: + title = 'Map direction'; + break; + case WidgetMobileActionType.makePhoneCall: + title = 'Phone call'; + break; + } + return processLaunchResultFunctionTemplate.replace('--TITLE--', title); +} + +export function getDefaultProcessQrCodeFunction() { + return processQrCodeFunction; +} + +export function getDefaultProcessLocationFunction() { + return processLocationFunction; +} + +export function getDefaultGetLocationFunction() { + return getLocationFunctionTemplate; +} + +export function getDefaultGetPhoneNumberFunction() { + return getPhoneNumberFunctionTemplate; +} + +export function getDefaultHandleEmptyResultFunction(type: WidgetMobileActionType): string { + let message = 'Mobile action was cancelled!'; + switch (type) { + case WidgetMobileActionType.takePictureFromGallery: + message = 'Take picture from gallery action was cancelled!'; + break; + case WidgetMobileActionType.takePhoto: + message = 'Take photo action was cancelled!'; + break; + case WidgetMobileActionType.mapDirection: + message = 'Open map directions was not invoked!'; + break; + case WidgetMobileActionType.mapLocation: + message = 'Open location on map was not invoked!'; + break; + case WidgetMobileActionType.scanQrCode: + message = 'Scan QR code action was canceled!'; + break; + case WidgetMobileActionType.makePhoneCall: + message = 'Phone call was not invoked!'; + break; + case WidgetMobileActionType.getLocation: + message = 'Get location action was canceled!'; + break; + case WidgetMobileActionType.takeScreenshot: + message = 'Take screenshot action was cancelled!'; + break; + } + return handleEmptyResultFunctionTemplate.replace('--MESSAGE--', message); +} + +export function getDefaultHandleErrorFunction(type: WidgetMobileActionType): string { + let title = 'Mobile action failed'; + switch (type) { + case WidgetMobileActionType.takePictureFromGallery: + title = 'Failed to take picture from gallery'; + break; + case WidgetMobileActionType.takePhoto: + title = 'Failed to take photo'; + break; + case WidgetMobileActionType.mapDirection: + title = 'Failed to open map directions'; + break; + case WidgetMobileActionType.mapLocation: + title = 'Failed to open map location'; + break; + case WidgetMobileActionType.scanQrCode: + title = 'Failed to scan QR code'; + break; + case WidgetMobileActionType.makePhoneCall: + title = 'Failed to make phone call'; + break; + case WidgetMobileActionType.getLocation: + title = 'Failed to get phone location'; + break; + case WidgetMobileActionType.takeScreenshot: + title = 'Failed to take screenshot'; + break; + } + return handleErrorFunctionTemplate.replace('--TITLE--', title); +} diff --git a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html index 33678c88b3..44bfe44e1e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html @@ -180,6 +180,11 @@ formControlName="customAction"> + + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts index c2fc1e3cfe..0227ded562 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts @@ -45,6 +45,7 @@ import { Dashboard } from '@shared/models/dashboard.models'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { CustomActionEditorCompleter } from '@home/components/widget/action/custom-action.models'; import { isDefinedAndNotNull } from '@core/utils'; +import { MobileActionEditorComponent } from '@home/components/widget/action/mobile-action-editor.component'; export interface WidgetActionDialogData { isAdd: boolean; @@ -64,6 +65,8 @@ export class WidgetActionDialogComponent extends DialogComponent; + switch (type) { + case WidgetMobileActionType.takePictureFromGallery: + case WidgetMobileActionType.takePhoto: + case WidgetMobileActionType.scanQrCode: + case WidgetMobileActionType.getLocation: + case WidgetMobileActionType.takeScreenshot: + argsObservable = of([]); + break; + case WidgetMobileActionType.mapDirection: + case WidgetMobileActionType.mapLocation: + const getLocationFunctionString = mobileAction.getLocationFunction; + const getLocationFunction = new Function('$event', 'widgetContext', 'entityId', + 'entityName', 'additionalParams', 'entityLabel', getLocationFunctionString); + const locationArgs = getLocationFunction($event, this.widgetContext, entityId, entityName, additionalParams, entityLabel); + if (locationArgs && locationArgs instanceof Observable) { + argsObservable = locationArgs; + } else { + argsObservable = of(locationArgs); + } + argsObservable = argsObservable.pipe(map(latLng => { + let valid = false; + if (Array.isArray(latLng) && latLng.length === 2) { + if (typeof latLng[0] === 'number' && typeof latLng[1] === 'number') { + valid = true; + } + } + if (valid) { + return latLng; + } else { + throw new Error('Location function did not return valid array of latitude/longitude!'); + } + })); + break; + case WidgetMobileActionType.makePhoneCall: + const getPhoneNumberFunctionString = mobileAction.getPhoneNumberFunction; + const getPhoneNumberFunction = new Function('$event', 'widgetContext', 'entityId', + 'entityName', 'additionalParams', 'entityLabel', getPhoneNumberFunctionString); + const phoneNumberArg = getPhoneNumberFunction($event, this.widgetContext, entityId, entityName, additionalParams, entityLabel); + if (phoneNumberArg && phoneNumberArg instanceof Observable) { + argsObservable = phoneNumberArg.pipe(map(phoneNumber => [phoneNumber])); + } else { + argsObservable = of([phoneNumberArg]); + } + argsObservable = argsObservable.pipe(map(phoneNumberArr => { + let valid = false; + if (Array.isArray(phoneNumberArr) && phoneNumberArr.length === 1) { + if (phoneNumberArr[0] !== null) { + valid = true; + } + } + if (valid) { + return phoneNumberArr; + } else { + throw new Error('Phone number function did not return valid number!'); + } + })); + break; + } + argsObservable.subscribe((args) => { + this.mobileService.handleWidgetMobileAction(type, ...args).subscribe( + (result) => { + if (result) { + if (result.hasError) { + this.handleWidgetMobileActionError(result.error, $event, mobileAction, entityId, entityName, additionalParams, entityLabel); + } else if (result.hasResult) { + const actionResult = result.result; + switch (type) { + case WidgetMobileActionType.takePictureFromGallery: + case WidgetMobileActionType.takePhoto: + case WidgetMobileActionType.takeScreenshot: + const imageUrl = actionResult.imageUrl; + if (mobileAction.processImageFunction && mobileAction.processImageFunction.length) { + try { + const processImageFunction = new Function('imageUrl', '$event', 'widgetContext', 'entityId', + 'entityName', 'additionalParams', 'entityLabel', mobileAction.processImageFunction); + processImageFunction(imageUrl, $event, this.widgetContext, entityId, entityName, additionalParams, entityLabel); + } catch (e) { + console.error(e); + } + } + break; + case WidgetMobileActionType.scanQrCode: + const code = actionResult.code; + const format = actionResult.format; + if (mobileAction.processQrCodeFunction && mobileAction.processQrCodeFunction.length) { + try { + const processQrCodeFunction = new Function('code', 'format', '$event', 'widgetContext', 'entityId', + 'entityName', 'additionalParams', 'entityLabel', mobileAction.processQrCodeFunction); + processQrCodeFunction(code, format, $event, this.widgetContext, entityId, entityName, additionalParams, entityLabel); + } catch (e) { + console.error(e); + } + } + break; + case WidgetMobileActionType.getLocation: + const latitude = actionResult.latitude; + const longitude = actionResult.longitude; + if (mobileAction.processLocationFunction && mobileAction.processLocationFunction.length) { + try { + const processLocationFunction = new Function('latitude', 'longitude', '$event', 'widgetContext', 'entityId', + 'entityName', 'additionalParams', 'entityLabel', mobileAction.processLocationFunction); + processLocationFunction(latitude, longitude, $event, this.widgetContext, + entityId, entityName, additionalParams, entityLabel); + } catch (e) { + console.error(e); + } + } + break; + case WidgetMobileActionType.mapDirection: + case WidgetMobileActionType.mapLocation: + case WidgetMobileActionType.makePhoneCall: + const launched = actionResult.launched; + if (mobileAction.processLaunchResultFunction && mobileAction.processLaunchResultFunction.length) { + try { + const processLaunchResultFunction = new Function('launched', '$event', 'widgetContext', 'entityId', + 'entityName', 'additionalParams', 'entityLabel', mobileAction.processLaunchResultFunction); + processLaunchResultFunction(launched, $event, this.widgetContext, + entityId, entityName, additionalParams, entityLabel); + } catch (e) { + console.error(e); + } + } + break; + } + } else { + if (mobileAction.handleEmptyResultFunction && mobileAction.handleEmptyResultFunction.length) { + try { + const handleEmptyResultFunction = new Function('$event', 'widgetContext', 'entityId', + 'entityName', 'additionalParams', 'entityLabel', mobileAction.handleEmptyResultFunction); + handleEmptyResultFunction($event, this.widgetContext, entityId, entityName, additionalParams, entityLabel); + } catch (e) { + console.error(e); + } + } + } + } + } + ); + }, + (err) => { + let errorMessage; + if (err && typeof err === 'string') { + errorMessage = err; + } else if (err && err.message) { + errorMessage = err.message; + } + errorMessage = `Failed to get mobile action arguments${errorMessage ? `: ${errorMessage}` : '!'}`; + this.handleWidgetMobileActionError(errorMessage, $event, mobileAction, entityId, entityName, additionalParams, entityLabel); + }); + } + + private handleWidgetMobileActionError(error: string, $event: Event, mobileAction: WidgetMobileActionDescriptor, + entityId?: EntityId, entityName?: string, additionalParams?: any, entityLabel?: string) { + if (mobileAction.handleErrorFunction && mobileAction.handleErrorFunction.length) { + try { + const handleErrorFunction = new Function('error', '$event', 'widgetContext', 'entityId', + 'entityName', 'additionalParams', 'entityLabel', mobileAction.handleErrorFunction); + handleErrorFunction(error, $event, this.widgetContext, entityId, entityName, additionalParams, entityLabel); + } catch (e) { + console.error(e); + } } } diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 74b6496c67..64fbe59efa 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -77,6 +77,7 @@ import { PageLink } from '@shared/models/page/page-link'; import { SortOrder } from '@shared/models/page/sort-order'; import { DomSanitizer } from '@angular/platform-browser'; import { Router } from '@angular/router'; +import { map, mergeMap } from 'rxjs/operators'; export interface IWidgetAction { name: string; @@ -238,7 +239,9 @@ export class WidgetContext { rxjs = { forkJoin, - of + of, + map, + mergeMap }; showSuccessToast(message: string, duration: number = 1000, diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 2d599bdc78..edf0eb803a 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -332,7 +332,19 @@ export enum WidgetActionType { updateDashboardState = 'updateDashboardState', openDashboard = 'openDashboard', custom = 'custom', - customPretty = 'customPretty' + customPretty = 'customPretty', + mobileAction = 'mobileAction' +} + +export enum WidgetMobileActionType { + takePictureFromGallery = 'takePictureFromGallery', + takePhoto = 'takePhoto', + mapDirection = 'mapDirection', + mapLocation = 'mapLocation', + scanQrCode = 'scanQrCode', + makePhoneCall = 'makePhoneCall', + getLocation = 'getLocation', + takeScreenshot = 'takeScreenshot' } export const widgetActionTypeTranslationMap = new Map( @@ -341,10 +353,90 @@ export const widgetActionTypeTranslationMap = new Map( [ WidgetActionType.updateDashboardState, 'widget-action.update-dashboard-state' ], [ WidgetActionType.openDashboard, 'widget-action.open-dashboard' ], [ WidgetActionType.custom, 'widget-action.custom' ], - [ WidgetActionType.customPretty, 'widget-action.custom-pretty' ] + [ WidgetActionType.customPretty, 'widget-action.custom-pretty' ], + [ WidgetActionType.mobileAction, 'widget-action.mobile-action' ] + ] +); + +export const widgetMobileActionTypeTranslationMap = new Map( + [ + [ WidgetMobileActionType.takePictureFromGallery, 'widget-action.mobile.take-picture-from-gallery' ], + [ WidgetMobileActionType.takePhoto, 'widget-action.mobile.take-photo' ], + [ WidgetMobileActionType.mapDirection, 'widget-action.mobile.map-direction' ], + [ WidgetMobileActionType.mapLocation, 'widget-action.mobile.map-location' ], + [ WidgetMobileActionType.scanQrCode, 'widget-action.mobile.scan-qr-code' ], + [ WidgetMobileActionType.makePhoneCall, 'widget-action.mobile.make-phone-call' ], + [ WidgetMobileActionType.getLocation, 'widget-action.mobile.get-location' ], + [ WidgetMobileActionType.takeScreenshot, 'widget-action.mobile.take-screenshot' ] ] ); +export interface MobileLaunchResult { + launched: boolean; +} + +export interface MobileImageResult { + imageUrl: string; +} + +export interface MobileQrCodeResult { + code: string; + format: string; +} + +export interface MobileLocationResult { + latitude: number; + longitude: number; +} + +export type MobileActionResult = MobileLaunchResult & + MobileImageResult & + MobileQrCodeResult & + MobileLocationResult; + +export interface WidgetMobileActionResult { + result?: T; + hasResult: boolean; + error?: string; + hasError: boolean; +} + +export interface ProcessImageDescriptor { + processImageFunction: string; +} + +export interface ProcessLaunchResultDescriptor { + processLaunchResultFunction?: string; +} + +export interface LaunchMapDescriptor extends ProcessLaunchResultDescriptor { + getLocationFunction: string; +} + +export interface ScanQrCodeDescriptor { + processQrCodeFunction: string; +} + +export interface MakePhoneCallDescriptor extends ProcessLaunchResultDescriptor { + getPhoneNumberFunction: string; +} + +export interface GetLocationDescriptor { + processLocationFunction: string; +} + +export type WidgetMobileActionDescriptors = ProcessImageDescriptor & + LaunchMapDescriptor & + ScanQrCodeDescriptor & + MakePhoneCallDescriptor & + GetLocationDescriptor; + +export interface WidgetMobileActionDescriptor extends WidgetMobileActionDescriptors { + type: WidgetMobileActionType; + handleErrorFunction?: string; + handleEmptyResultFunction?: string; +} + export interface CustomActionDescriptor { customFunction?: string; customResources?: Array; @@ -369,6 +461,7 @@ export interface WidgetActionDescriptor extends CustomActionDescriptor { dialogHeight?: number; setEntityId?: boolean; stateEntityParamName?: string; + mobileAction?: WidgetMobileActionDescriptor; } export interface WidgetComparisonSettings { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 08335a0175..160c42ff0a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2704,6 +2704,7 @@ "open-dashboard": "Navigate to other dashboard", "custom": "Custom action", "custom-pretty": "Custom action (with HTML template)", + "mobile-action": "Mobile action", "target-dashboard-state": "Target dashboard state", "target-dashboard-state-required": "Target dashboard state is required", "set-entity-from-widget": "Set entity from widget", @@ -2715,7 +2716,19 @@ "dialog-width": "Dialog width in percents relative to viewport width", "dialog-height": "Dialog height in percents relative to viewport height", "dialog-size-range-error": "Dialog size percent value should be in a range from 1 to 100.", - "open-new-browser-tab": "Open in a new browser tab" + "open-new-browser-tab": "Open in a new browser tab", + "mobile": { + "action-type": "Mobile action type", + "action-type-required": "Mobile action type is required", + "take-picture-from-gallery": "Take picture from gallery", + "take-photo": "Take photo", + "map-direction": "Open map directions", + "map-location": "Open map location", + "scan-qr-code": "Scan QR Code", + "make-phone-call": "Make phone call", + "get-location": "Get phone location", + "take-screenshot": "Take screenshot" + } }, "widgets-bundle": { "current": "Current bundle",