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); + } } }