Browse Source

Merge pull request #4707 from thingsboard/lwm2m-refactoring

LwM2M Refactoring
pull/4712/head
Andrew Shvayka 5 years ago
committed by GitHub
parent
commit
e86af4b677
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
  2. 47
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  3. 69
      application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java
  4. 48
      application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java
  5. 121
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
  6. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java
  7. BIN
      application/src/test/resources/lwm2m/credentials/clientKeyStore.jks
  8. BIN
      application/src/test/resources/lwm2m/credentials/serverKeyStore.jks
  9. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java
  10. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java
  11. 16
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java
  12. 12
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2mDefaultBootstrapSessionManager.java
  13. 31
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java
  14. 3
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MAuthorizer.java
  15. 75
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java
  16. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java
  17. 375
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java
  18. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
  19. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
  20. 7
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java
  21. 88
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
  22. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java
  23. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java
  24. 22
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClientState.java
  25. 31
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClientStateException.java
  26. 70
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java
  27. 19
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java
  28. 189
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java
  29. 16
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java
  30. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java
  31. 27
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbEditableSecurityStore.java
  32. 130
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbInMemorySecurityStore.java
  33. 54
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java
  34. 141
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java
  35. 82
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java
  36. 14
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mStoreFactory.java
  37. 25
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbSecurityStore.java
  38. 12
      pom.xml

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

@ -72,15 +72,15 @@ public class DefaultTbResourceService implements TbResourceService {
if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) {
try { try {
List<ObjectModel> objectModels = List<ObjectModel> objectModels =
ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
if (!objectModels.isEmpty()) { if (!objectModels.isEmpty()) {
ObjectModel objectModel = objectModels.get(0); ObjectModel objectModel = objectModels.get(0);
String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.getVersion(); String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.version;
String name = objectModel.name; String name = objectModel.name;
resource.setResourceKey(resourceKey); resource.setResourceKey(resourceKey);
if (resource.getId() == null) { if (resource.getId() == null) {
resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.getVersion()); resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.version);
} }
resource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name); resource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name);
} else { } else {
@ -176,7 +176,7 @@ public class DefaultTbResourceService implements TbResourceService {
try { try {
DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator()); DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator());
List<ObjectModel> objectModels = List<ObjectModel> objectModels =
ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText());
if (objectModels.size() == 0) { if (objectModels.size() == 0) {
return null; return null;
} else { } else {

47
application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java

@ -60,6 +60,53 @@ import java.util.concurrent.ScheduledExecutorService;
@DaoSqlTest @DaoSqlTest
public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
protected final String TRANSPORT_CONFIGURATION = "{\n" +
" \"type\": \"LWM2M\",\n" +
" \"observeAttr\": {\n" +
" \"keyName\": {\n" +
" \"/3_1.0/0/9\": \"batteryLevel\"\n" +
" },\n" +
" \"observe\": [],\n" +
" \"attribute\": [\n" +
" ],\n" +
" \"telemetry\": [\n" +
" \"/3_1.0/0/9\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrap\": {\n" +
" \"servers\": {\n" +
" \"binding\": \"U\",\n" +
" \"shortId\": 123,\n" +
" \"lifetime\": 300,\n" +
" \"notifIfDisabled\": true,\n" +
" \"defaultMinPeriod\": 1\n" +
" },\n" +
" \"lwm2mServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5686,\n" +
" \"serverId\": 123,\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": false,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" },\n" +
" \"bootstrapServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5687,\n" +
" \"serverId\": 111,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": true,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" }\n" +
" },\n" +
" \"clientLwM2mSettings\": {\n" +
" \"clientOnlyObserveAfterConnect\": 1\n" +
" }\n" +
"}";
protected DeviceProfile deviceProfile; protected DeviceProfile deviceProfile;
protected ScheduledExecutorService executor; protected ScheduledExecutorService executor;
protected TbTestWebSocketClient wsClient; protected TbTestWebSocketClient wsClient;

69
application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java

@ -22,6 +22,7 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials;
import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.data.query.EntityDataQuery;
@ -36,7 +37,6 @@ import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -46,60 +46,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
protected final String TRANSPORT_CONFIGURATION = "{\n" + private final int PORT = 5685;
" \"type\": \"LWM2M\",\n" + private final Security SECURITY = noSec("coap://localhost:" + PORT, 123);
" \"observeAttr\": {\n" + private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT));
" \"keyName\": {\n" + private final String ENDPOINT = "deviceAEndpoint";
" \"/3_1.0/0/9\": \"batteryLevel\"\n" +
" },\n" +
" \"observe\": [],\n" +
" \"attribute\": [\n" +
" ],\n" +
" \"telemetry\": [\n" +
" \"/3_1.0/0/9\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrap\": {\n" +
" \"servers\": {\n" +
" \"binding\": \"UQ\",\n" +
" \"shortId\": 123,\n" +
" \"lifetime\": 300,\n" +
" \"notifIfDisabled\": true,\n" +
" \"defaultMinPeriod\": 1\n" +
" },\n" +
" \"lwm2mServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5685,\n" +
" \"serverId\": 123,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": false,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" },\n" +
" \"bootstrapServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5687,\n" +
" \"serverId\": 111,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": true,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" }\n" +
" },\n" +
" \"clientLwM2mSettings\": {\n" +
" \"clientOnlyObserveAfterConnect\": 1\n" +
" }\n" +
"}";
private final int port = 5685;
private final Security security = noSec("coap://localhost:" + port, 123);
private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_PORT", Integer.toString(port));
@NotNull @NotNull
private Device createDevice(String deviceAEndpoint) throws Exception { private Device createDevice() throws Exception {
Device device = new Device(); Device device = new Device();
device.setName("Device A"); device.setName("Device A");
device.setDeviceProfileId(deviceProfile.getId()); device.setDeviceProfileId(deviceProfile.getId());
@ -114,7 +67,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
LwM2MCredentials noSecCredentials = new LwM2MCredentials(); LwM2MCredentials noSecCredentials = new LwM2MCredentials();
NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
clientCredentials.setEndpoint(deviceAEndpoint); clientCredentials.setEndpoint(ENDPOINT);
noSecCredentials.setClient(clientCredentials); noSecCredentials.setClient(clientCredentials);
deviceCredentials.setCredentialsValue(JacksonUtil.toString(noSecCredentials)); deviceCredentials.setCredentialsValue(JacksonUtil.toString(noSecCredentials));
doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());
@ -125,9 +78,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
public void testConnectAndObserveTelemetry() throws Exception { public void testConnectAndObserveTelemetry() throws Exception {
createDeviceProfile(TRANSPORT_CONFIGURATION); createDeviceProfile(TRANSPORT_CONFIGURATION);
String deviceAEndpoint = "deviceAEndpoint"; Device device = createDevice();
Device device = createDevice(deviceAEndpoint);
SingleEntityFilter sef = new SingleEntityFilter(); SingleEntityFilter sef = new SingleEntityFilter();
sef.setSingleEntity(device.getId()); sef.setSingleEntity(device.getId());
@ -144,8 +95,8 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
wsClient.waitForReply(); wsClient.waitForReply();
wsClient.registerWaitForUpdate(); wsClient.registerWaitForUpdate();
LwM2MTestClient client = new LwM2MTestClient(executor, deviceAEndpoint); LwM2MTestClient client = new LwM2MTestClient(executor, ENDPOINT);
client.init(security, coapConfig); client.init(SECURITY, COAP_CONFIG);
String msg = wsClient.waitForUpdate(); String msg = wsClient.waitForUpdate();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);

48
application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java

@ -47,54 +47,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
protected final String TRANSPORT_CONFIGURATION = "{\n" +
" \"type\": \"LWM2M\",\n" +
" \"observeAttr\": {\n" +
" \"keyName\": {\n" +
" \"/3_1.0/0/9\": \"batteryLevel\"\n" +
" },\n" +
" \"observe\": [],\n" +
" \"attribute\": [\n" +
" ],\n" +
" \"telemetry\": [\n" +
" \"/3_1.0/0/9\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +
" \"bootstrap\": {\n" +
" \"servers\": {\n" +
" \"binding\": \"UQ\",\n" +
" \"shortId\": 123,\n" +
" \"lifetime\": 300,\n" +
" \"notifIfDisabled\": true,\n" +
" \"defaultMinPeriod\": 1\n" +
" },\n" +
" \"lwm2mServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5686,\n" +
" \"serverId\": 123,\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": false,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" },\n" +
" \"bootstrapServer\": {\n" +
" \"host\": \"localhost\",\n" +
" \"port\": 5687,\n" +
" \"serverId\": 111,\n" +
" \"securityMode\": \"NO_SEC\",\n" +
" \"serverPublicKey\": \"\",\n" +
" \"bootstrapServerIs\": true,\n" +
" \"clientHoldOffTime\": 1,\n" +
" \"bootstrapServerAccountTimeout\": 0\n" +
" }\n" +
" },\n" +
" \"clientLwM2mSettings\": {\n" +
" \"clientOnlyObserveAfterConnect\": 1\n" +
" }\n" +
"}";
private final int port = 5686; private final int port = 5686;
private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(port)); private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(port));
private final String endpoint = "deviceAEndpoint"; private final String endpoint = "deviceAEndpoint";

121
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java

@ -17,40 +17,37 @@ package org.thingsboard.server.transport.lwm2m.client;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.californium.elements.Connector; import org.eclipse.californium.core.observe.ObservationStore;
import org.eclipse.californium.scandium.DTLSConnector; import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.ClientHandshaker;
import org.eclipse.californium.scandium.dtls.DTLSSession;
import org.eclipse.californium.scandium.dtls.HandshakeException;
import org.eclipse.californium.scandium.dtls.Handshaker;
import org.eclipse.californium.scandium.dtls.ResumingClientHandshaker;
import org.eclipse.californium.scandium.dtls.ResumingServerHandshaker;
import org.eclipse.californium.scandium.dtls.ServerHandshaker;
import org.eclipse.californium.scandium.dtls.SessionAdapter;
import org.eclipse.leshan.client.californium.LeshanClient; import org.eclipse.leshan.client.californium.LeshanClient;
import org.eclipse.leshan.client.californium.LeshanClientBuilder; import org.eclipse.leshan.client.californium.LeshanClientBuilder;
import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory; import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory;
import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.client.object.Security;
import org.eclipse.leshan.client.object.Server; import org.eclipse.leshan.client.object.Server;
import org.eclipse.leshan.client.observer.LwM2mClientObserver; import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
import org.eclipse.leshan.client.resource.ObjectsInitializer; import org.eclipse.leshan.client.resource.ObjectsInitializer;
import org.eclipse.leshan.client.servers.ServerIdentity; import org.eclipse.leshan.client.servers.ServerIdentity;
import org.eclipse.leshan.core.LwM2mId;
import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.californium.DefaultEndpointFactory; import org.eclipse.leshan.core.californium.EndpointFactory;
import org.eclipse.leshan.core.model.InvalidDDFFileException;
import org.eclipse.leshan.core.model.LwM2mModel; import org.eclipse.leshan.core.model.LwM2mModel;
import org.eclipse.leshan.core.model.ObjectLoader; import org.eclipse.leshan.core.model.ObjectLoader;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.StaticModel; import org.eclipse.leshan.core.model.StaticModel;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.core.request.BootstrapRequest; import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.DeregisterRequest; import org.eclipse.leshan.core.request.DeregisterRequest;
import org.eclipse.leshan.core.request.RegisterRequest; import org.eclipse.leshan.core.request.RegisterRequest;
import org.eclipse.leshan.core.request.UpdateRequest; import org.eclipse.leshan.core.request.UpdateRequest;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -67,7 +64,7 @@ public class LwM2MTestClient {
private final String endpoint; private final String endpoint;
private LeshanClient client; private LeshanClient client;
public void init(Security security, NetworkConfig coapConfig) { public void init(Security security, NetworkConfig coapConfig) throws InvalidDDFFileException, IOException {
String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml"}; String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml"};
List<ObjectModel> models = new ArrayList<>(); List<ObjectModel> models = new ArrayList<>();
for (String resourceName : resources) { for (String resourceName : resources) {
@ -76,82 +73,51 @@ public class LwM2MTestClient {
LwM2mModel model = new StaticModel(models); LwM2mModel model = new StaticModel(models);
ObjectsInitializer initializer = new ObjectsInitializer(model); ObjectsInitializer initializer = new ObjectsInitializer(model);
initializer.setInstancesForObject(SECURITY, security); initializer.setInstancesForObject(SECURITY, security);
initializer.setInstancesForObject(SERVER, new Server(123, 300, BindingMode.U, false)); initializer.setInstancesForObject(SERVER, new Server(123, 300));
initializer.setInstancesForObject(DEVICE, new SimpleLwM2MDevice()); initializer.setInstancesForObject(DEVICE, new SimpleLwM2MDevice());
initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class);
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
dtlsConfig.setRecommendedCipherSuitesOnly(true); dtlsConfig.setRecommendedCipherSuitesOnly(true);
dtlsConfig.setClientOnly();
DefaultRegistrationEngineFactory engineFactory = new DefaultRegistrationEngineFactory(); DefaultRegistrationEngineFactory engineFactory = new DefaultRegistrationEngineFactory();
engineFactory.setReconnectOnUpdate(false); engineFactory.setReconnectOnUpdate(false);
engineFactory.setResumeOnConnect(true); engineFactory.setResumeOnConnect(true);
DefaultEndpointFactory endpointFactory = new DefaultEndpointFactory(endpoint) { EndpointFactory endpointFactory = new EndpointFactory() {
@Override @Override
protected Connector createSecuredConnector(DtlsConnectorConfig dtlsConfig) { public CoapEndpoint createUnsecuredEndpoint(InetSocketAddress address, NetworkConfig coapConfig,
ObservationStore store) {
return new DTLSConnector(dtlsConfig) { CoapEndpoint.Builder builder = new CoapEndpoint.Builder();
@Override builder.setInetSocketAddress(address);
protected void onInitializeHandshaker(Handshaker handshaker) { builder.setNetworkConfig(coapConfig);
handshaker.addSessionListener(new SessionAdapter() { return builder.build();
}
@Override
public void handshakeStarted(Handshaker handshaker) throws HandshakeException { @Override
if (handshaker instanceof ServerHandshaker) { public CoapEndpoint createSecuredEndpoint(DtlsConnectorConfig dtlsConfig, NetworkConfig coapConfig,
log.info("DTLS Full Handshake initiated by server : STARTED ..."); ObservationStore store) {
} else if (handshaker instanceof ResumingServerHandshaker) { CoapEndpoint.Builder builder = new CoapEndpoint.Builder();
log.info("DTLS abbreviated Handshake initiated by server : STARTED ..."); DtlsConnectorConfig.Builder dtlsConfigBuilder = new DtlsConnectorConfig.Builder(dtlsConfig);
} else if (handshaker instanceof ClientHandshaker) {
log.info("DTLS Full Handshake initiated by client : STARTED ..."); // tricks to be able to change psk information on the fly
} else if (handshaker instanceof ResumingClientHandshaker) { // AdvancedPskStore pskStore = dtlsConfig.getAdvancedPskStore();
log.info("DTLS abbreviated Handshake initiated by client : STARTED ..."); // if (pskStore != null) {
} // PskPublicInformation identity = pskStore.getIdentity(null, null);
} // SecretKey key = pskStore
// .requestPskSecretResult(ConnectionId.EMPTY, null, identity, null, null, null).getSecret();
@Override // singlePSKStore = new SinglePSKStore(identity, key);
public void sessionEstablished(Handshaker handshaker, DTLSSession establishedSession) // dtlsConfigBuilder.setAdvancedPskStore(singlePSKStore);
throws HandshakeException { // }
if (handshaker instanceof ServerHandshaker) { builder.setConnector(new DTLSConnector(dtlsConfigBuilder.build()));
log.info("DTLS Full Handshake initiated by server : SUCCEED, handshaker {}", handshaker); builder.setNetworkConfig(coapConfig);
} else if (handshaker instanceof ResumingServerHandshaker) { return builder.build();
log.info("DTLS abbreviated Handshake initiated by server : SUCCEED, handshaker {}", handshaker);
} else if (handshaker instanceof ClientHandshaker) {
log.info("DTLS Full Handshake initiated by client : SUCCEED, handshaker {}", handshaker);
} else if (handshaker instanceof ResumingClientHandshaker) {
log.info("DTLS abbreviated Handshake initiated by client : SUCCEED, handshaker {}", handshaker);
}
}
@Override
public void handshakeFailed(Handshaker handshaker, Throwable error) {
/** get cause */
String cause;
if (error != null) {
if (error.getMessage() != null) {
cause = error.getMessage();
} else {
cause = error.getClass().getName();
}
} else {
cause = "unknown cause";
}
if (handshaker instanceof ServerHandshaker) {
log.info("DTLS Full Handshake initiated by server : FAILED [{}]", cause);
} else if (handshaker instanceof ResumingServerHandshaker) {
log.info("DTLS abbreviated Handshake initiated by server : FAILED [{}]", cause);
} else if (handshaker instanceof ClientHandshaker) {
log.info("DTLS Full Handshake initiated by client : FAILED [{}]", cause);
} else if (handshaker instanceof ResumingClientHandshaker) {
log.info("DTLS abbreviated Handshake initiated by client : FAILED [{}]", cause);
}
}
});
}
};
} }
}; };
LeshanClientBuilder builder = new LeshanClientBuilder(endpoint); LeshanClientBuilder builder = new LeshanClientBuilder(endpoint);
builder.setLocalAddress("0.0.0.0", 11000); builder.setLocalAddress("0.0.0.0", 11000);
builder.setObjects(initializer.createAll()); builder.setObjects(initializer.createAll());
@ -246,6 +212,11 @@ public class LwM2MTestClient {
public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest request) { public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest request) {
log.info("ClientObserver ->onDeregistrationTimeout... DeregisterRequest [{}] [{}]", request.getRegistrationId(), request.getRegistrationId()); log.info("ClientObserver ->onDeregistrationTimeout... DeregisterRequest [{}] [{}]", request.getRegistrationId(), request.getRegistrationId());
} }
@Override
public void onUnexpectedError(Throwable unexpectedError) {
}
}; };
this.client.addObserver(observer); this.client.addObserver(observer);

4
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java

@ -97,7 +97,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
} }
@Override @Override
public WriteResponse write(ServerIdentity identity, int resourceid, LwM2mResource value) { public WriteResponse write(ServerIdentity identity, boolean replace, int resourceid, LwM2mResource value) {
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceid); log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceid);
switch (resourceid) { switch (resourceid) {
@ -112,7 +112,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
fireResourcesChange(resourceid); fireResourcesChange(resourceid);
return WriteResponse.success(); return WriteResponse.success();
default: default:
return super.write(identity, resourceid, value); return super.write(identity, replace, resourceid, value);
} }
} }

BIN
application/src/test/resources/lwm2m/credentials/clientKeyStore.jks

Binary file not shown.

BIN
application/src/test/resources/lwm2m/credentials/serverKeyStore.jks

Binary file not shown.

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

@ -109,8 +109,8 @@ public class LwM2MTransportBootstrapService {
/** Create credentials */ /** Create credentials */
this.setServerWithCredentials(builder); this.setServerWithCredentials(builder);
/** Set securityStore with new ConfigStore */ // /** Set securityStore with new ConfigStore */
builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore); // builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore);
/** SecurityStore */ /** SecurityStore */
builder.setSecurityStore(lwM2MBootstrapSecurityStore); builder.setSecurityStore(lwM2MBootstrapSecurityStore);

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java

@ -69,7 +69,7 @@ public class LwM2MBootstrapConfig {
server0.lifetime = servers.getLifetime(); server0.lifetime = servers.getLifetime();
server0.defaultMinPeriod = servers.getDefaultMinPeriod(); server0.defaultMinPeriod = servers.getDefaultMinPeriod();
server0.notifIfDisabled = servers.isNotifIfDisabled(); server0.notifIfDisabled = servers.isNotifIfDisabled();
server0.binding = BindingMode.valueOf(servers.getBinding()); server0.binding = BindingMode.parse(servers.getBinding());
configBs.servers.put(0, server0); configBs.servers.put(0, server0);
/* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Bootstrap instance = 0 */ /* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Bootstrap instance = 0 */
this.bootstrapServer.setBootstrapServerIs(true); this.bootstrapServer.setBootstrapServerIs(true);

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

@ -30,7 +30,7 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
@ -40,7 +40,7 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.Iterator;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.BOOTSTRAP_SERVER; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.BOOTSTRAP_SERVER;
@ -71,8 +71,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
} }
@Override @Override
public List<SecurityInfo> getAllByEndpoint(String endPoint) { public Iterator<SecurityInfo> getAllByEndpoint(String endPoint) {
EndpointSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
/* add value to store from BootstrapJson */ /* add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store); this.setBootstrapConfigScurityInfo(store);
@ -88,7 +88,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
} catch (InvalidConfigurationException e) { } catch (InvalidConfigurationException e) {
log.error("", e); log.error("", e);
} }
return store.getSecurityInfo() == null ? null : Collections.singletonList(store.getSecurityInfo()); return store.getSecurityInfo() == null ? null : Collections.singletonList(store.getSecurityInfo()).iterator();
} }
} }
return null; return null;
@ -96,7 +96,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
@Override @Override
public SecurityInfo getByIdentity(String identity) { public SecurityInfo getByIdentity(String identity) {
EndpointSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
/* add value to store from BootstrapJson */ /* add value to store from BootstrapJson */
this.setBootstrapConfigScurityInfo(store); this.setBootstrapConfigScurityInfo(store);
@ -113,7 +113,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
return null; return null;
} }
private void setBootstrapConfigScurityInfo(EndpointSecurityInfo store) { private void setBootstrapConfigScurityInfo(TbLwM2MSecurityInfo store) {
/* BootstrapConfig */ /* BootstrapConfig */
LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store); LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store);
if (lwM2MBootstrapConfig != null) { if (lwM2MBootstrapConfig != null) {
@ -150,7 +150,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
} }
} }
private LwM2MBootstrapConfig getParametersBootstrap(EndpointSecurityInfo store) { private LwM2MBootstrapConfig getParametersBootstrap(TbLwM2MSecurityInfo store) {
try { try {
LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig(); LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig();
if (lwM2MBootstrapConfig != null) { if (lwM2MBootstrapConfig != null) {

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

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.lwm2m.bootstrap.secure; package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.Identity; import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.server.bootstrap.BootstrapSession; import org.eclipse.leshan.server.bootstrap.BootstrapSession;
import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSession; import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSession;
@ -25,7 +26,7 @@ import org.eclipse.leshan.server.security.SecurityChecker;
import org.eclipse.leshan.server.security.SecurityInfo; import org.eclipse.leshan.server.security.SecurityInfo;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.Iterator;
@Slf4j @Slf4j
public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSessionManager { public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSessionManager {
@ -50,16 +51,17 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public BootstrapSession begin(String endpoint, Identity clientIdentity) { public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity) {
boolean authorized; boolean authorized;
if (bsSecurityStore != null) { if (bsSecurityStore != null) {
List<SecurityInfo> securityInfos = (clientIdentity.getPskIdentity() != null && !clientIdentity.getPskIdentity().isEmpty()) ? Collections.singletonList(bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity())) : bsSecurityStore.getAllByEndpoint(endpoint); Iterator<SecurityInfo> securityInfos = (clientIdentity.getPskIdentity() != null && !clientIdentity.getPskIdentity().isEmpty()) ?
Collections.singletonList(bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity())).iterator() : bsSecurityStore.getAllByEndpoint(request.getEndpointName());
log.info("Bootstrap session started securityInfos: [{}]", securityInfos); log.info("Bootstrap session started securityInfos: [{}]", securityInfos);
authorized = securityChecker.checkSecurityInfos(endpoint, clientIdentity, securityInfos); authorized = securityChecker.checkSecurityInfos(request.getEndpointName(), clientIdentity, securityInfos);
} else { } else {
authorized = true; authorized = true;
} }
DefaultBootstrapSession session = new DefaultBootstrapSession(endpoint, clientIdentity, authorized); DefaultBootstrapSession session = new DefaultBootstrapSession(request, clientIdentity, authorized);
log.info("Bootstrap session started : {}", session); log.info("Bootstrap session started : {}", session);
return session; return session;
} }

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

@ -22,16 +22,16 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials;
import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
@ -55,15 +55,15 @@ public class LwM2mCredentialsSecurityInfoValidator {
private final LwM2mTransportContext context; private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config; private final LwM2MTransportServerConfig config;
public EndpointSecurityInfo getEndpointSecurityInfo(String endpoint, LwM2mTransportUtil.LwM2mTypeServer keyValue) { public TbLwM2MSecurityInfo getEndpointSecurityInfoByCredentialsId(String credentialsId, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
final EndpointSecurityInfo[] resultSecurityStore = new EndpointSecurityInfo[1]; final TbLwM2MSecurityInfo[] resultSecurityStore = new TbLwM2MSecurityInfo[1];
context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endpoint).build(), context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(credentialsId).build(),
new TransportServiceCallback<>() { new TransportServiceCallback<>() {
@Override @Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) { public void onSuccess(ValidateDeviceCredentialsResponse msg) {
String credentialsBody = msg.getCredentials(); String credentialsBody = msg.getCredentials();
resultSecurityStore[0] = createSecurityInfo(endpoint, credentialsBody, keyValue); resultSecurityStore[0] = createSecurityInfo(credentialsId, credentialsBody, keyValue);
resultSecurityStore[0].setMsg(msg); resultSecurityStore[0].setMsg(msg);
resultSecurityStore[0].setDeviceProfile(msg.getDeviceProfile()); resultSecurityStore[0].setDeviceProfile(msg.getDeviceProfile());
latch.countDown(); latch.countDown();
@ -71,8 +71,8 @@ public class LwM2mCredentialsSecurityInfoValidator {
@Override @Override
public void onError(Throwable e) { public void onError(Throwable e) {
log.trace("[{}] [{}] Failed to process credentials ", endpoint, e); log.trace("[{}] [{}] Failed to process credentials ", credentialsId, e);
resultSecurityStore[0] = createSecurityInfo(endpoint, null, null); resultSecurityStore[0] = createSecurityInfo(credentialsId, null, null);
latch.countDown(); latch.countDown();
} }
}); });
@ -91,8 +91,8 @@ public class LwM2mCredentialsSecurityInfoValidator {
* @param keyValue - * @param keyValue -
* @return SecurityInfo * @return SecurityInfo
*/ */
private EndpointSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTransportUtil.LwM2mTypeServer keyValue) { private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
EndpointSecurityInfo result = new EndpointSecurityInfo(); TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo();
LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class); LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class);
if (credentials != null) { if (credentials != null) {
if (keyValue.equals(LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP)) { if (keyValue.equals(LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP)) {
@ -104,6 +104,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
result.setEndpoint(endpoint); result.setEndpoint(endpoint);
result.setSecurityMode(credentials.getBootstrap().getBootstrapServer().getSecurityMode()); result.setSecurityMode(credentials.getBootstrap().getBootstrapServer().getSecurityMode());
} else { } else {
result.setEndpoint(credentials.getClient().getEndpoint());
switch (credentials.getClient().getSecurityConfigClientMode()) { switch (credentials.getClient().getSecurityConfigClientMode()) {
case NO_SEC: case NO_SEC:
createClientSecurityInfoNoSec(result); createClientSecurityInfoNoSec(result);
@ -125,12 +126,12 @@ public class LwM2mCredentialsSecurityInfoValidator {
return result; return result;
} }
private void createClientSecurityInfoNoSec(EndpointSecurityInfo result) { private void createClientSecurityInfoNoSec(TbLwM2MSecurityInfo result) {
result.setSecurityInfo(null); result.setSecurityInfo(null);
result.setSecurityMode(NO_SEC); result.setSecurityMode(NO_SEC);
} }
private void createClientSecurityInfoPSK(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) { private void createClientSecurityInfoPSK(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
PSKClientCredentials pskConfig = (PSKClientCredentials) clientCredentialsConfig; PSKClientCredentials pskConfig = (PSKClientCredentials) clientCredentialsConfig;
if (StringUtils.isNotEmpty(pskConfig.getIdentity())) { if (StringUtils.isNotEmpty(pskConfig.getIdentity())) {
try { try {
@ -149,7 +150,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
} }
} }
private void createClientSecurityInfoRPK(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) { private void createClientSecurityInfoRPK(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
RPKClientCredentials rpkConfig = (RPKClientCredentials) clientCredentialsConfig; RPKClientCredentials rpkConfig = (RPKClientCredentials) clientCredentialsConfig;
try { try {
if (rpkConfig.getKey() != null) { if (rpkConfig.getKey() != null) {
@ -164,7 +165,7 @@ public class LwM2mCredentialsSecurityInfoValidator {
} }
} }
private void createClientSecurityInfoX509(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) { private void createClientSecurityInfoX509(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint)); result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint));
result.setSecurityMode(X509); result.setSecurityMode(X509);
} }

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

@ -27,6 +27,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@ -34,7 +35,7 @@ import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore;
public class TbLwM2MAuthorizer implements Authorizer { public class TbLwM2MAuthorizer implements Authorizer {
private final TbLwM2MDtlsSessionStore sessionStorage; private final TbLwM2MDtlsSessionStore sessionStorage;
private final TbLwM2mSecurityStore securityStore; private final TbSecurityStore securityStore;
private final SecurityChecker securityChecker = new SecurityChecker(); private final SecurityChecker securityChecker = new SecurityChecker();
private final LwM2mClientContext clientContext; private final LwM2mClientContext clientContext;

75
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java

@ -29,22 +29,21 @@ import org.eclipse.californium.scandium.dtls.HandshakeResultHandler;
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
import org.eclipse.californium.scandium.dtls.x509.StaticCertificateVerifier; import org.eclipse.californium.scandium.dtls.x509.StaticCertificateVerifier;
import org.eclipse.californium.scandium.util.ServerNames; import org.eclipse.californium.scandium.util.ServerNames;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials;
import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.util.SslUtil; import org.thingsboard.server.common.transport.util.SslUtil;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -57,8 +56,6 @@ import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@Slf4j @Slf4j
@Component @Component
@ -66,9 +63,10 @@ import java.util.concurrent.TimeUnit;
@RequiredArgsConstructor @RequiredArgsConstructor
public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVerifier { public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVerifier {
private final TransportService transportService;
private final TbLwM2MDtlsSessionStore sessionStorage; private final TbLwM2MDtlsSessionStore sessionStorage;
private final LwM2MTransportServerConfig config; private final LwM2MTransportServerConfig config;
private final LwM2mCredentialsSecurityInfoValidator securityInfoValidator;
private final TbEditableSecurityStore securityStore;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private StaticCertificateVerifier staticCertificateVerifier; private StaticCertificateVerifier staticCertificateVerifier;
@ -119,48 +117,33 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
String strCert = SslUtil.getCertificateString(cert); String strCert = SslUtil.getCertificateString(cert);
String sha3Hash = EncryptionUtil.getSha3Hash(strCert); String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
final ValidateDeviceCredentialsResponse[] deviceCredentialsResponse = new ValidateDeviceCredentialsResponse[1]; TbLwM2MSecurityInfo securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
CountDownLatch latch = new CountDownLatch(1); ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null;
transportService.process(TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(sha3Hash).build(), if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
new TransportServiceCallback<>() { LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class);
@Override if (!credentials.getClient().getSecurityConfigClientMode().equals(LwM2MSecurityMode.X509)) {
public void onSuccess(ValidateDeviceCredentialsResponse msg) { continue;
if (!StringUtils.isEmpty(msg.getCredentials())) { }
deviceCredentialsResponse[0] = msg; X509ClientCredentials config = (X509ClientCredentials) credentials.getClient();
} String certBody = config.getCert();
latch.countDown(); String endpoint = config.getEndpoint();
} if (strCert.equals(certBody)) {
x509CredentialsFound = true;
@Override DeviceProfile deviceProfile = msg.getDeviceProfile();
public void onError(Throwable e) { if (msg.hasDeviceInfo() && deviceProfile != null) {
log.error(e.getMessage(), e); sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg));
latch.countDown(); try {
} securityStore.put(securityInfo);
}); } catch (NonUniqueSecurityInfoException e) {
if (latch.await(10, TimeUnit.SECONDS)) { log.trace("Failed to add security info: {}", securityInfo, e);
ValidateDeviceCredentialsResponse msg = deviceCredentialsResponse[0];
if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class);
if(!credentials.getClient().getSecurityConfigClientMode().equals(LwM2MSecurityMode.X509)){
continue;
}
X509ClientCredentials config = (X509ClientCredentials) credentials.getClient();
String certBody = config.getCert();
String endpoint = config.getEndpoint();
if (strCert.equals(certBody)) {
x509CredentialsFound = true;
DeviceProfile deviceProfile = msg.getDeviceProfile();
if (msg.hasDeviceInfo() && deviceProfile != null) {
sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg));
break;
} }
} else { break;
log.trace("[{}][{}] Certificate mismatch. Expected: {}, Actual: {}", endpoint, sha3Hash, strCert, certBody);
} }
} else {
log.trace("[{}][{}] Certificate mismatch. Expected: {}, Actual: {}", endpoint, sha3Hash, strCert, certBody);
} }
} }
} catch (InterruptedException | } catch (CertificateEncodingException |
CertificateEncodingException |
CertificateExpiredException | CertificateExpiredException |
CertificateNotYetValidException e) { CertificateNotYetValidException e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/EndpointSecurityInfo.java → common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java

@ -24,7 +24,7 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes
import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
@Data @Data
public class EndpointSecurityInfo { public class TbLwM2MSecurityInfo {
private ValidateDeviceCredentialsResponse msg; private ValidateDeviceCredentialsResponse msg;
private SecurityInfo securityInfo; private SecurityInfo securityInfo;
private SecurityMode securityMode; private SecurityMode securityMode;

375
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -31,7 +31,6 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.request.WriteRequest;
import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
@ -58,6 +57,8 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
@ -91,7 +92,6 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWN
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.CLIENT_NOT_AUTHORIZED;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID;
@ -99,6 +99,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_WARN;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_STRATEGY_2; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_STRATEGY_2;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
@ -182,32 +183,38 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
public void onRegistered(Registration registration, Collection<Observation> previousObservations) { public void onRegistered(Registration registration, Collection<Observation> previousObservations) {
registrationExecutor.submit(() -> { registrationExecutor.submit(() -> {
LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint());
try { try {
log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
LwM2mClient lwM2MClient = this.clientContext.registerOrUpdate(registration);
if (lwM2MClient != null) { if (lwM2MClient != null) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); this.clientContext.register(lwM2MClient, registration);
if (sessionInfo != null) { this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_INFO + ": Client registered with registration id: " + registration.getId());
transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); SessionInfoProto sessionInfo = lwM2MClient.getSession();
TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder() transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo));
.setSessionInfo(sessionInfo) TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder()
.setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN)) .setSessionInfo(sessionInfo)
.setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build()) .setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN))
.setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().build()) .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build())
.build(); .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().build())
transportService.process(msg, null); .build();
this.getInfoFirmwareUpdate(lwM2MClient, null); transportService.process(msg, null);
this.getInfoSoftwareUpdate(lwM2MClient, null); this.getInfoFirmwareUpdate(lwM2MClient, null);
this.initLwM2mFromClientValue(registration, lwM2MClient); this.getInfoSoftwareUpdate(lwM2MClient, null);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId()); this.initClientTelemetry(lwM2MClient);
} else {
log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
}
} else { } else {
log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null); log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null);
}
} catch (LwM2MClientStateException stateException) {
if (LwM2MClientState.UNREGISTERED.equals(stateException.getState())) {
log.info("[{}] retry registration due to race condition: [{}].", registration.getEndpoint(), stateException.getState());
// Race condition detected and the client was in progress of unregistration while new registration arrived. Let's try again.
onRegistered(registration, previousObservations);
} else {
this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_WARN + ": Client registration failed due to invalid state: " + stateException.getState());
} }
} catch (Throwable t) { } catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t); log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t);
this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_WARN + ": Client registration failed due to: " + t.getMessage());
} }
}); });
} }
@ -219,25 +226,26 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
public void updatedReg(Registration registration) { public void updatedReg(Registration registration) {
updateRegistrationExecutor.submit(() -> { updateRegistrationExecutor.submit(() -> {
LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint());
try { try {
LwM2mClient client = clientContext.getOrRegister(registration); clientContext.updateRegistration(lwM2MClient, registration);
if (client != null && client.getSession() != null) { TransportProtos.SessionInfoProto sessionInfo = lwM2MClient.getSession();
SessionInfoProto sessionInfo = client.getSession(); this.reportActivityAndRegister(sessionInfo);
this.reportActivityAndRegister(sessionInfo); if (registration.usesQueueMode()) {
if (registration.getBindingMode().useQueueMode()) { LwM2mQueuedRequest request;
LwM2mQueuedRequest request; while ((request = lwM2MClient.getQueuedRequests().poll()) != null) {
while ((request = client.getQueuedRequests().poll()) != null) { request.send();
request.send();
}
} }
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client update Registration", registration.getId()); }
} catch (LwM2MClientStateException stateException) {
if (LwM2MClientState.UNREGISTERED.equals(stateException.getState())) {
log.info("[{}] update registration failed because client was already unregistered: [{}].", registration.getEndpoint(), stateException.getState());
} else { } else {
log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); log.info("[{}] update registration: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage());
this.sendLogsToThingsboard(LOG_LW2M_ERROR + ": Client update Registration", registration.getId());
} }
} catch (Throwable t) { } catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t); log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t);
this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client update Registration, %s", t.getMessage()), registration.getId()); this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_ERROR + String.format(": Client update Registration, %s", t.getMessage()));
} }
}); });
} }
@ -248,34 +256,32 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
public void unReg(Registration registration, Collection<Observation> observations) { public void unReg(Registration registration, Collection<Observation> observations) {
unRegistrationExecutor.submit(() -> { unRegistrationExecutor.submit(() -> {
LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint());
try { try {
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); this.sendLogsToThingsboard(client, LOG_LW2M_INFO + ": Client unRegistration");
this.closeClientSession(registration); clientContext.unregister(client, registration);
SessionInfoProto sessionInfo = client.getSession();
if (sessionInfo != null) {
transportService.deregisterSession(sessionInfo);
sessionStore.remove(registration.getEndpoint());
this.doCloseSession(sessionInfo);
log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
} else {
log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
}
} catch (LwM2MClientStateException stateException) {
log.info("[{}] delete registration: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage());
} catch (Throwable t) { } catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()), registration.getId()); this.sendLogsToThingsboard(client, LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()));
} }
}); });
} }
private void closeClientSession(Registration registration) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration);
if (sessionInfo != null) {
transportService.deregisterSession(sessionInfo);
sessionStore.remove(registration.getEndpoint());
this.doCloseSession(sessionInfo);
clientContext.removeClientByRegistrationId(registration.getId());
log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
} else {
log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
}
}
@Override @Override
public void onSleepingDev(Registration registration) { public void onSleepingDev(Registration registration) {
log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration.getId()); this.sendLogsToThingsboard(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LW2M_INFO + ": Client is sleeping!");
//TODO: associate endpointId with device information. //TODO: associate endpointId with device information.
} }
@ -285,8 +291,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override @Override
public void setCancelObservationsAll(Registration registration) { public void setCancelObservationsAll(Registration registration) {
if (registration != null) { if (registration != null) {
this.lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL_ALL, LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint());
null, null, this.config.getTimeout(), null); if (client != null && client.getRegistration() != null && client.getRegistration().getId().equals(registration.getId())) {
this.lwM2mTransportRequest.sendAllRequest(client, null, OBSERVE_CANCEL_ALL,
null, null, this.config.getTimeout(), null);
}
} }
} }
@ -300,7 +309,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override @Override
public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) { public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) {
if (response.getContent() != null) { if (response.getContent() != null) {
LwM2mClient lwM2MClient = clientContext.getOrRegister(registration); LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint());
ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, this.config.getModelProvider()); ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, this.config.getModelProvider());
if (objectModelVersion != null) { if (objectModelVersion != null) {
if (response.getContent() instanceof LwM2mObject) { if (response.getContent() instanceof LwM2mObject) {
@ -332,7 +341,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
String msg = String.format("%s: type operation %s path - %s value - %s", LOG_LW2M_INFO, String msg = String.format("%s: type operation %s path - %s value - %s", LOG_LW2M_INFO,
READ, pathIdVer, value); READ, pathIdVer, value);
this.sendLogsToThingsboard(msg, registration.getId()); this.sendLogsToThingsboard(lwM2MClient, msg);
rpcRequest.setValueMsg(String.format("%s", value)); rpcRequest.setValueMsg(String.format("%s", value));
this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE); this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE);
} }
@ -350,9 +359,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
@Override @Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo); LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo);
if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) {
log.warn ("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList()); log.warn("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList());
msg.getSharedUpdatedList().forEach(tsKvProto -> { msg.getSharedUpdatedList().forEach(tsKvProto -> {
String pathName = tsKvProto.getKv().getKey(); String pathName = tsKvProto.getKv().getKey();
String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
@ -377,13 +386,13 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew);
String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
LOG_LW2M_ERROR, pathIdVer, valueNew); LOG_LW2M_ERROR, pathIdVer, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); this.sendLogsToThingsboard(lwM2MClient, logMsg);
} }
} else if (!isFwSwWords(pathName)) { } else if (!isFwSwWords(pathName)) {
log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew);
String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
LOG_LW2M_ERROR, pathName, valueNew); LOG_LW2M_ERROR, pathName, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); this.sendLogsToThingsboard(lwM2MClient, logMsg);
} }
}); });
@ -396,9 +405,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
}); });
log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
} } else if (lwM2MClient == null) {
else if (lwM2MClient == null) { log.error("OnAttributeUpdate, lwM2MClient is null");
log.error ("OnAttributeUpdate, lwM2MClient is null");
} }
} }
@ -408,12 +416,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
@Override @Override
public void onDeviceProfileUpdate(SessionInfoProto sessionInfo, DeviceProfile deviceProfile) { public void onDeviceProfileUpdate(SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
Set<LwM2mClient> clients = clientContext.getLwM2mClients() List<LwM2mClient> clients = clientContext.getLwM2mClients()
.stream().filter(e -> e.getProfileId().equals(deviceProfile.getUuidId())).collect(Collectors.toSet()); .stream().filter(e -> e.getProfileId().equals(deviceProfile.getUuidId())).collect(Collectors.toList());
clients.forEach(client -> client.onDeviceProfileUpdate(deviceProfile)); clients.forEach(client -> client.onDeviceProfileUpdate(deviceProfile));
Set<String> registrationIds = clients.stream().map(LwM2mClient::getRegistration).map(Registration::getId).collect(Collectors.toSet()); if (clients.size() > 0) {
if (registrationIds.size() > 0) { this.onDeviceProfileUpdate(clients, deviceProfile);
this.onDeviceProfileUpdate(registrationIds, deviceProfile);
} }
} }
@ -446,7 +453,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) { public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) {
// #1 // #1
this.checkRpcRequestTimeout(); this.checkRpcRequestTimeout();
log.warn ("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); log.warn("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null"; String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null";
LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName()); LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName());
UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB()); UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB());
@ -454,13 +461,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime()); this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime());
Lwm2mClientRpcRequest lwm2mClientRpcRequest = null; Lwm2mClientRpcRequest lwm2mClientRpcRequest = null;
try { try {
Registration registration = clientContext.getClient(sessionInfo).getRegistration(); LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo);
Registration registration = client.getRegistration();
lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this); lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this);
if (lwm2mClientRpcRequest.getErrorMsg() != null) { if (lwm2mClientRpcRequest.getErrorMsg() != null) {
lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
} else { } else {
lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), lwM2mTransportRequest.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(),
null, null,
lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(),
this.config.getTimeout(), lwm2mClientRpcRequest); this.config.getTimeout(), lwm2mClientRpcRequest);
@ -506,7 +514,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override @Override
public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) { public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) {
log.warn ("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); log.warn("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
transportService.process(sessionInfo, toDeviceResponse, null); transportService.process(sessionInfo, toDeviceResponse, null);
} }
@ -514,17 +522,6 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
log.info("[{}] toServerRpcResponse", toServerResponse); log.info("[{}] toServerRpcResponse", toServerResponse);
} }
/**
* Trigger Server path = "/1/0/8"
* <p>
* Trigger bootStrap path = "/1/0/9" - have to implemented on client
*/
@Override
public void doTrigger(Registration registration, String path) {
lwM2mTransportRequest.sendAllRequest(registration, path, EXECUTE,
ContentFormat.TLV.getName(), null, this.config.getTimeout(), null);
}
/** /**
* Deregister session in transport * Deregister session in transport
* *
@ -558,7 +555,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override @Override
public void onAwakeDev(Registration registration) { public void onAwakeDev(Registration registration) {
log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId()); this.sendLogsToThingsboard(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LW2M_INFO + ": Client is awake!");
//TODO: associate endpointId with device information. //TODO: associate endpointId with device information.
} }
@ -567,13 +564,17 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param registrationId - Id of Registration LwM2M Client * @param registrationId - Id of Registration LwM2M Client
*/ */
@Override @Override
public void sendLogsToThingsboard(String logMsg, String registrationId) { public void sendLogsToThingsboard(String registrationId, String logMsg) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registrationId); sendLogsToThingsboard(clientContext.getClientByRegistrationId(registrationId), logMsg);
if (logMsg != null && sessionInfo != null) { }
@Override
public void sendLogsToThingsboard(LwM2mClient client, String logMsg) {
if (logMsg != null && client != null && client.getSession() != null) {
if (logMsg.length() > 1024) { if (logMsg.length() > 1024) {
logMsg = logMsg.substring(0, 1024); logMsg = logMsg.substring(0, 1024);
} }
this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo); this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), client.getSession());
} }
} }
@ -586,24 +587,23 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* - Read Request to the client after registration to read all resource values for all objects * - Read Request to the client after registration to read all resource values for all objects
* - then Observe Request to the client marked as observe from the profile configuration. * - then Observe Request to the client marked as observe from the profile configuration.
* *
* @param registration - Registration LwM2M Client * @param lwM2MClient - object with All parameters off client
* @param lwM2MClient - object with All parameters off client
*/ */
private void initLwM2mFromClientValue(Registration registration, LwM2mClient lwM2MClient) { private void initClientTelemetry(LwM2mClient lwM2MClient) {
LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration); LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(lwM2MClient.getProfileId());
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(registration); Set<String> clientObjects = clientContext.getSupportedIdVerInClient(lwM2MClient);
if (clientObjects != null && clientObjects.size() > 0) { if (clientObjects != null && clientObjects.size() > 0) {
if (LWM2M_STRATEGY_2 == LwM2mTransportUtil.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { if (LWM2M_STRATEGY_2 == LwM2mTransportUtil.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
// #2 // #2
lwM2MClient.getPendingReadRequests().addAll(clientObjects); lwM2MClient.getPendingReadRequests().addAll(clientObjects);
clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(), clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, READ,
null, this.config.getTimeout(), null)); null, this.config.getTimeout(), null));
} }
// #1 // #1
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, READ, clientObjects); this.initReadAttrTelemetryObserveToClient(lwM2MClient, READ, clientObjects);
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, OBSERVE, clientObjects); this.initReadAttrTelemetryObserveToClient(lwM2MClient, OBSERVE, clientObjects);
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, WRITE_ATTRIBUTES, clientObjects); this.initReadAttrTelemetryObserveToClient(lwM2MClient, WRITE_ATTRIBUTES, clientObjects);
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, DISCOVER, clientObjects); this.initReadAttrTelemetryObserveToClient(lwM2MClient, DISCOVER, clientObjects);
} }
} }
@ -645,7 +645,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param path - resource * @param path - resource
*/ */
private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) { private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
LwM2mClient lwM2MClient = clientContext.getOrRegister(registration); LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint());
if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) { if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) {
/** version != null /** version != null
* set setClient_fw_info... = value * set setClient_fw_info... = value
@ -740,15 +740,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
} }
/** private void initReadAttrTelemetryObserveToClient(LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, Set<String> clientObjects) {
* Start observe/read: Attr/Telemetry LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(lwM2MClient.getProfileId());
* #1 - Analyze: path in resource profile == client resource
*
* @param registration -
*/
private void initReadAttrTelemetryObserveToClient(Registration registration, LwM2mClient lwM2MClient,
LwM2mTypeOper typeOper, Set<String> clientObjects) {
LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration);
Set<String> result = null; Set<String> result = null;
ConcurrentHashMap<String, Object> params = null; ConcurrentHashMap<String, Object> params = null;
if (READ.equals(typeOper)) { if (READ.equals(typeOper)) {
@ -768,28 +761,42 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()); params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
result = params.keySet(); result = params.keySet();
} }
if (result != null && !result.isEmpty()) { sendRequestsToClient(lwM2MClient, typeOper, clientObjects, result, params);
// #1 }
Set<String> pathSend = result.stream().filter(target -> {
return target.split(LWM2M_SEPARATOR_PATH).length < 3 ? private void sendRequestsToClient(LwM2mClient lwM2MClient, LwM2mTypeOper operationType, Set<String> supportedObjectIds, Set<String> desiredObjectIds, ConcurrentHashMap<String, Object> params) {
clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) : if (desiredObjectIds != null && !desiredObjectIds.isEmpty()) {
clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]); Set<String> targetObjectIds = desiredObjectIds.stream().filter(target -> isSupportedTargetId(supportedObjectIds, target)
}
).collect(Collectors.toUnmodifiableSet()); ).collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) { if (!targetObjectIds.isEmpty()) {
lwM2MClient.getPendingReadRequests().addAll(pathSend); //TODO: remove this side effect?
ConcurrentHashMap<String, Object> finalParams = params; lwM2MClient.getPendingReadRequests().addAll(targetObjectIds);
pathSend.forEach(target -> { targetObjectIds.forEach(target -> {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(), Object additionalParams = params != null ? params.get(target) : null;
finalParams != null ? finalParams.get(target) : null, this.config.getTimeout(), null); lwM2mTransportRequest.sendAllRequest(lwM2MClient, target, operationType, additionalParams, this.config.getTimeout(), null);
}); });
if (OBSERVE.equals(typeOper)) { if (OBSERVE.equals(operationType)) {
lwM2MClient.initReadValue(this, null); lwM2MClient.initReadValue(this, null);
} }
} }
} }
} }
private boolean isSupportedTargetId(Set<String> supportedIds, String targetId) {
String[] targetIdParts = targetId.split(LWM2M_SEPARATOR_PATH);
if (targetIdParts.length <= 1) {
return false;
}
String targetIdSearch = targetIdParts[0];
for (int i = 1; i < targetIdParts.length; i++) {
targetIdSearch += "/" + targetIdParts[i];
if (supportedIds.contains(targetIdSearch)) {
return true;
}
}
return false;
}
private ConcurrentHashMap<String, Object> getPathForWriteAttributes(JsonObject objectJson) { private ConcurrentHashMap<String, Object> getPathForWriteAttributes(JsonObject objectJson) {
ConcurrentHashMap<String, Object> pathAttributes = new Gson().fromJson(objectJson.toString(), ConcurrentHashMap<String, Object> pathAttributes = new Gson().fromJson(objectJson.toString(),
new TypeToken<ConcurrentHashMap<String, Object>>() { new TypeToken<ConcurrentHashMap<String, Object>>() {
@ -798,7 +805,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
private void onDeviceUpdate(LwM2mClient lwM2MClient, Device device, Optional<DeviceProfile> deviceProfileOpt) { private void onDeviceUpdate(LwM2mClient lwM2MClient, Device device, Optional<DeviceProfile> deviceProfileOpt) {
deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singleton(lwM2MClient.getRegistration().getId()), deviceProfile)); deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singletonList(lwM2MClient), deviceProfile));
lwM2MClient.onDeviceUpdate(device, deviceProfileOpt); lwM2MClient.onDeviceUpdate(device, deviceProfileOpt);
} }
@ -843,7 +850,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
private TransportProtos.KeyValueProto getKvToThingsboard(String pathIdVer, Registration registration) { private TransportProtos.KeyValueProto getKvToThingsboard(String pathIdVer, Registration registration) {
LwM2mClient lwM2MClient = this.clientContext.getClientByRegistrationId(registration.getId()); LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint());
JsonObject names = clientContext.getProfiles().get(lwM2MClient.getProfileId()).getPostKeyNameProfile(); JsonObject names = clientContext.getProfiles().get(lwM2MClient.getProfileId()).getPostKeyNameProfile();
if (names != null && names.has(pathIdVer)) { if (names != null && names.has(pathIdVer)) {
String resourceName = names.get(pathIdVer).getAsString(); String resourceName = names.get(pathIdVer).getAsString();
@ -858,7 +865,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
valueKvProto = new JsonObject(); valueKvProto = new JsonObject();
Object finalvalueKvProto = valueKvProto; Object finalvalueKvProto = valueKvProto;
Gson gson = new GsonBuilder().create(); Gson gson = new GsonBuilder().create();
resourceValue.getValues().forEach((k, v) -> { resourceValue.getInstances().forEach((k, v) -> {
Object val = this.converter.convertValue(v, currentType, expectedType, Object val = this.converter.convertValue(v, currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
JsonElement element = gson.toJsonTree(val, val.getClass()); JsonElement element = gson.toJsonTree(val, val.getClass());
@ -892,9 +899,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
} } else {
else {
return null; return null;
} }
} }
@ -955,10 +960,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* #6.1 - update WriteAttribute * #6.1 - update WriteAttribute
* #6.2 - del WriteAttribute * #6.2 - del WriteAttribute
* *
* @param registrationIds - * @param clients -
* @param deviceProfile - * @param deviceProfile -
*/ */
private void onDeviceProfileUpdate(Set<String> registrationIds, DeviceProfile deviceProfile) { private void onDeviceProfileUpdate(List<LwM2mClient> clients, DeviceProfile deviceProfile) {
LwM2mClientProfile lwM2MClientProfileOld = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone(); LwM2mClientProfile lwM2MClientProfileOld = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone();
if (clientContext.profileUpdate(deviceProfile) != null) { if (clientContext.profileUpdate(deviceProfile) != null) {
// #1 // #1
@ -1009,15 +1014,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
// #3.4, #6 // #3.4, #6
if (!attributeLwm2mOld.equals(attributeLwm2mNew)) { if (!attributeLwm2mOld.equals(attributeLwm2mNew)) {
this.getAnalyzerAttributeLwm2m(registrationIds, attributeLwm2mOld, attributeLwm2mNew); this.getAnalyzerAttributeLwm2m(clients, attributeLwm2mOld, attributeLwm2mNew);
} }
// #4.1 add // #4.1 add
if (sendAttrToThingsboard.getPathPostParametersAdd().size() > 0) { if (sendAttrToThingsboard.getPathPostParametersAdd().size() > 0) {
// update value in Resources // update value in Resources
registrationIds.forEach(registrationId -> { clients.forEach(client -> {
Registration registration = clientContext.getRegistration(registrationId); this.readObserveFromProfile(client, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
}); });
} }
// #4.2 del // #4.2 del
@ -1041,15 +1045,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
// does not include oldObserve // does not include oldObserve
ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sendObserveToClientOld.getPathPostParametersAdd(), sendObserveToClientNew.getPathPostParametersAdd()); ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sendObserveToClientOld.getPathPostParametersAdd(), sendObserveToClientNew.getPathPostParametersAdd());
// send Request observe to Client // send Request observe to Client
registrationIds.forEach(registrationId -> { clients.forEach(client -> {
Registration registration = clientContext.getRegistration(registrationId); Registration registration = client.getRegistration();
if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) { if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) {
this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE); this.readObserveFromProfile(client, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
} }
// 5.3 del // 5.3 del
// send Request cancel observe to Client // send Request cancel observe to Client
if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) { if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) {
this.cancelObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersDel()); this.cancelObserveFromProfile(client, postObserveAnalyzer.getPathPostParametersDel());
} }
}); });
} }
@ -1086,19 +1090,18 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* Update Resource value after change RezAttrTelemetry in config Profile * Update Resource value after change RezAttrTelemetry in config Profile
* send response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue() * send response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue()
* *
* @param registration - Registration LwM2M Client * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
* @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
*/ */
private void readObserveFromProfile(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) { private void readObserveFromProfile(LwM2mClient client, Set<String> targets, LwM2mTypeOper typeOper) {
targets.forEach(target -> { targets.forEach(target -> {
LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target)); LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target));
if (pathIds.isResource()) { if (pathIds.isResource()) {
if (READ.equals(typeOper)) { if (READ.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, lwM2mTransportRequest.sendAllRequest(client, target, typeOper,
ContentFormat.TLV.getName(), null, this.config.getTimeout(), null); null, this.config.getTimeout(), null);
} else if (OBSERVE.equals(typeOper)) { } else if (OBSERVE.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, lwM2mTransportRequest.sendAllRequest(client, target, typeOper,
null, null, this.config.getTimeout(), null); null, this.config.getTimeout(), null);
} }
} }
}); });
@ -1124,7 +1127,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param attributeLwm2mNew - * @param attributeLwm2mNew -
* @return * @return
*/ */
private void getAnalyzerAttributeLwm2m(Set<String> registrationIds, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) { private void getAnalyzerAttributeLwm2m(List<LwM2mClient> clients, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) {
ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
ConcurrentHashMap<String, Object> lwm2mAttributesOld = new Gson().fromJson(attributeLwm2mOld.toString(), ConcurrentHashMap<String, Object> lwm2mAttributesOld = new Gson().fromJson(attributeLwm2mOld.toString(),
new TypeToken<ConcurrentHashMap<String, Object>>() { new TypeToken<ConcurrentHashMap<String, Object>>() {
@ -1146,23 +1149,22 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
// #6 // #6
// #6.2 // #6.2
if (analyzerParameters.getPathPostParametersAdd().size() > 0) { if (analyzerParameters.getPathPostParametersAdd().size() > 0) {
registrationIds.forEach(registrationId -> { clients.forEach(client -> {
Registration registration = this.clientContext.getRegistration(registrationId); Set<String> clientObjects = clientContext.getSupportedIdVerInClient(client);
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(registration);
Set<String> pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) Set<String> pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]))
.collect(Collectors.toUnmodifiableSet()); .collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) { if (!pathSend.isEmpty()) {
ConcurrentHashMap<String, Object> finalParams = lwm2mAttributesNew; ConcurrentHashMap<String, Object> finalParams = lwm2mAttributesNew;
pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(client, target, WRITE_ATTRIBUTES,
finalParams.get(target), this.config.getTimeout(), null)); finalParams.get(target), this.config.getTimeout(), null));
} }
}); });
} }
// #6.2 // #6.2
if (analyzerParameters.getPathPostParametersDel().size() > 0) { if (analyzerParameters.getPathPostParametersDel().size() > 0) {
registrationIds.forEach(registrationId -> { clients.forEach(client -> {
Registration registration = this.clientContext.getRegistration(registrationId); Registration registration = client.getRegistration();
Set<String> clientObjects = clientContext.getSupportedIdVerInClient(registration); Set<String> clientObjects = clientContext.getSupportedIdVerInClient(client);
Set<String> pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) Set<String> pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]))
.collect(Collectors.toUnmodifiableSet()); .collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) { if (!pathSend.isEmpty()) {
@ -1170,8 +1172,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
Map<String, Object> params = (Map<String, Object>) lwm2mAttributesOld.get(target); Map<String, Object> params = (Map<String, Object>) lwm2mAttributesOld.get(target);
params.clear(); params.clear();
params.put(OBJECT_VERSION, ""); params.put(OBJECT_VERSION, "");
lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), lwM2mTransportRequest.sendAllRequest(client, target, WRITE_ATTRIBUTES, params, this.config.getTimeout(), null);
params, this.config.getTimeout(), null);
}); });
} }
}); });
@ -1179,12 +1180,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
} }
private void cancelObserveFromProfile(Registration registration, Set<String> paramAnallyzer) { private void cancelObserveFromProfile(LwM2mClient lwM2mClient, Set<String> paramAnallyzer) {
LwM2mClient lwM2MClient = clientContext.getOrRegister(registration);
paramAnallyzer.forEach(pathIdVer -> { paramAnallyzer.forEach(pathIdVer -> {
if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) { if (this.getResourceValueFromLwM2MClient(lwM2mClient, pathIdVer) != null) {
lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, OBSERVE_CANCEL, null, lwM2mTransportRequest.sendAllRequest(lwM2mClient, pathIdVer, OBSERVE_CANCEL, null, this.config.getTimeout(), null);
null, this.config.getTimeout(), null);
} }
} }
); );
@ -1192,14 +1191,12 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) { private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) {
if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) { if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) {
lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, WRITE_REPLACE, lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, WRITE_REPLACE, valueNew, this.config.getTimeout(), null);
ContentFormat.TLV.getName(), valueNew,
this.config.getTimeout(), null);
} else { } else {
log.error("Failed update resource [{}] [{}]", path, valueNew); log.error("Failed update resource [{}] [{}]", path, valueNew);
String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad", String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad",
LOG_LW2M_ERROR, path, valueNew); LOG_LW2M_ERROR, path, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); this.sendLogsToThingsboard(lwM2MClient, logMsg);
log.info("Failed update resource [{}] [{}]", path, valueNew); log.info("Failed update resource [{}] [{}]", path, valueNew);
} }
} }
@ -1221,7 +1218,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/ */
public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) { public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
LwM2mClientProfile profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); LwM2mClientProfile profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
LwM2mClient lwM2mClient = clientContext.getClient(sessionInfo); LwM2mClient lwM2mClient = clientContext.getClientBySessionInfo(sessionInfo);
return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream() return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
.filter(e -> e.getValue().getAsString().equals(name) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().map(Map.Entry::getKey) .filter(e -> e.getValue().getAsString().equals(name) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().map(Map.Entry::getKey)
.orElse(null); .orElse(null);
@ -1256,7 +1253,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param sessionInfo * @param sessionInfo
*/ */
public void updateAttributeFromThingsboard(List<TransportProtos.TsKvProto> tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) { public void updateAttributeFromThingsboard(List<TransportProtos.TsKvProto> tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo); LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo);
if (lwM2MClient != null) { if (lwM2MClient != null) {
log.warn("1) UpdateAttributeFromThingsboard, tsKvProtos [{}]", tsKvProtos); log.warn("1) UpdateAttributeFromThingsboard, tsKvProtos [{}]", tsKvProtos);
tsKvProtos.forEach(tsKvProto -> { tsKvProtos.forEach(tsKvProto -> {
@ -1275,8 +1272,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer),
getValueFromKvProto(tsKvProto.getKv()), pathIdVer); getValueFromKvProto(tsKvProto.getKv()), pathIdVer);
}); });
} } else {
else {
log.error("UpdateAttributeFromThingsboard, lwM2MClient is null"); log.error("UpdateAttributeFromThingsboard, lwM2MClient is null");
} }
} }
@ -1285,14 +1281,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param lwM2MClient - * @param lwM2MClient -
* @return SessionInfoProto - * @return SessionInfoProto -
*/ */
private SessionInfoProto getSessionInfoOrCloseSession(LwM2mClient lwM2MClient) { private SessionInfoProto getSessionInfo(LwM2mClient lwM2MClient) {
if (lwM2MClient != null) { if (lwM2MClient != null && lwM2MClient.getSession() != null) {
SessionInfoProto sessionInfoProto = lwM2MClient.getSession(); return lwM2MClient.getSession();
if (sessionInfoProto == null) {
log.info("[{}] [{}]", lwM2MClient.getEndpoint(), CLIENT_NOT_AUTHORIZED);
this.closeClientSession(lwM2MClient.getRegistration());
}
return sessionInfoProto;
} }
return null; return null;
} }
@ -1302,15 +1293,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @return - sessionInfo after access connect client * @return - sessionInfo after access connect client
*/ */
public SessionInfoProto getSessionInfoOrCloseSession(Registration registration) { public SessionInfoProto getSessionInfoOrCloseSession(Registration registration) {
return getSessionInfoOrCloseSession(clientContext.getOrRegister(registration)); return getSessionInfo(clientContext.getClientByEndpoint(registration.getEndpoint()));
}
/**
* @param registrationId -
* @return -
*/
private SessionInfoProto getSessionInfoOrCloseSession(String registrationId) {
return getSessionInfoOrCloseSession(clientContext.getClientByRegistrationId(registrationId));
} }
/** /**
@ -1340,7 +1323,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param lwM2MClient - LwM2M Client * @param lwM2MClient - LwM2M Client
*/ */
public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) { public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient);
if (sessionInfo != null) { if (sessionInfo != null) {
//#1.1 //#1.1
ConcurrentMap<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient); ConcurrentMap<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient);
@ -1359,7 +1342,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) { public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) {
if (lwM2MClient.getRegistration().getSupportedVersion(FW_ID) != null) { if (lwM2MClient.getRegistration().getSupportedVersion(FW_ID) != null) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient);
if (sessionInfo != null) { if (sessionInfo != null) {
DefaultLwM2MTransportMsgHandler handler = this; DefaultLwM2MTransportMsgHandler handler = this;
this.transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.FIRMWARE.name()), this.transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.FIRMWARE.name()),
@ -1368,16 +1351,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) {
if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())
&& response.getType().equals(OtaPackageType.FIRMWARE.name())) { && response.getType().equals(OtaPackageType.FIRMWARE.name())) {
log.warn ("7) firmware start with ver: [{}]", response.getVersion()); log.warn("7) firmware start with ver: [{}]", response.getVersion());
lwM2MClient.getFwUpdate().setRpcRequest(rpcRequest); lwM2MClient.getFwUpdate().setRpcRequest(rpcRequest);
lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion());
lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle()); lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle());
lwM2MClient.getFwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); lwM2MClient.getFwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId());
if (rpcRequest == null) { if (rpcRequest == null) {
lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest); lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
} } else {
else { lwM2MClient.getFwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
lwM2MClient.getFwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
} }
} else { } else {
log.trace("OtaPackage [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); log.trace("OtaPackage [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
@ -1395,7 +1377,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) { public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) {
if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) { if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient);
if (sessionInfo != null) { if (sessionInfo != null) {
DefaultLwM2MTransportMsgHandler handler = this; DefaultLwM2MTransportMsgHandler handler = this;
transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()), transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()),
@ -1411,9 +1393,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
if (rpcRequest == null) { if (rpcRequest == null) {
lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
} } else {
else { lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
} }
} else { } else {
log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());

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

@ -36,6 +36,8 @@ import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -83,7 +85,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
private final LwM2mTransportServerHelper helper; private final LwM2mTransportServerHelper helper;
private final LwM2mTransportMsgHandler handler; private final LwM2mTransportMsgHandler handler;
private final CaliforniumRegistrationStore registrationStore; private final CaliforniumRegistrationStore registrationStore;
private final EditableSecurityStore securityStore; private final TbSecurityStore securityStore;
private final LwM2mClientContext lwM2mClientContext; private final LwM2mClientContext lwM2mClientContext;
private final TbLwM2MDtlsCertificateVerifier certificateVerifier; private final TbLwM2MDtlsCertificateVerifier certificateVerifier;
private final TbLwM2MAuthorizer authorizer; private final TbLwM2MAuthorizer authorizer;

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

@ -87,7 +87,7 @@ public class LwM2mServerListener {
@Override @Override
public void cancelled(Observation observation) { public void cancelled(Observation observation) {
String msg = String.format("%s: Canceled Observation %s.", LOG_LW2M_INFO, observation.getPath()); String msg = String.format("%s: Canceled Observation %s.", LOG_LW2M_INFO, observation.getPath());
service.sendLogsToThingsboard(msg, observation.getRegistrationId()); service.sendLogsToThingsboard(observation.getRegistrationId(), msg);
log.warn(msg); log.warn(msg);
} }
@ -109,7 +109,7 @@ public class LwM2mServerListener {
String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO, String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO,
observation.getPath()); observation.getPath());
log.warn(msg); log.warn(msg);
service.sendLogsToThingsboard(msg, registration.getId()); service.sendLogsToThingsboard(registration.getId(), msg);
} }
}; };
} }

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

@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; 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.Lwm2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
import java.util.Collection; import java.util.Collection;
@ -57,13 +58,13 @@ public interface LwM2mTransportMsgHandler {
void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse); void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse);
void doTrigger(Registration registration, String path);
void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
void onAwakeDev(Registration registration); void onAwakeDev(Registration registration);
void sendLogsToThingsboard(String msg, String registrationId); void sendLogsToThingsboard(LwM2mClient client, String msg);
void sendLogsToThingsboard(String registrationId, String msg);
LwM2MTransportServerConfig getConfig(); LwM2MTransportServerConfig getConfig();
} }

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

@ -32,10 +32,10 @@ import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.request.DeleteRequest; import org.eclipse.leshan.core.request.DeleteRequest;
import org.eclipse.leshan.core.request.DiscoverRequest; import org.eclipse.leshan.core.request.DiscoverRequest;
import org.eclipse.leshan.core.request.DownlinkRequest;
import org.eclipse.leshan.core.request.ExecuteRequest; import org.eclipse.leshan.core.request.ExecuteRequest;
import org.eclipse.leshan.core.request.ObserveRequest; import org.eclipse.leshan.core.request.ObserveRequest;
import org.eclipse.leshan.core.request.ReadRequest; import org.eclipse.leshan.core.request.ReadRequest;
import org.eclipse.leshan.core.request.SimpleDownlinkRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.request.WriteRequest;
import org.eclipse.leshan.core.request.exception.ClientSleepingException; import org.eclipse.leshan.core.request.exception.ClientSleepingException;
@ -116,32 +116,30 @@ public class LwM2mTransportRequest {
new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL))); new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL)));
} }
/** public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) {
* Device management and service enablement, including Read, Write, Execute, Discover, Create, Delete and Write-Attributes sendAllRequest(lwM2MClient, targetIdVer, typeOper, lwM2MClient.getDefaultContentFormat(), params, timeoutInMs, lwm2mClientRpcRequest);
* }
* @param registration -
* @param targetIdVer -
* @param typeOper -
* @param contentFormatName -
*/
public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper, public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper,
String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) { ContentFormat contentFormat, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) {
Registration registration = lwM2MClient.getRegistration();
try { try {
String target = convertPathFromIdVerToObjectId(targetIdVer); String target = convertPathFromIdVerToObjectId(targetIdVer);
ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT; if(contentFormat == null){
LwM2mClient lwM2MClient = this.lwM2mClientContext.getOrRegister(registration); contentFormat = ContentFormat.DEFAULT;
}
LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null; LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null;
if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) { if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) {
if (lwM2MClient.isValidObjectVersion(targetIdVer)) { if (lwM2MClient.isValidObjectVersion(targetIdVer)) {
timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
DownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target, SimpleDownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target,
targetIdVer, resultIds, params, lwm2mClientRpcRequest); targetIdVer, resultIds, params, lwm2mClientRpcRequest);
if (request != null) { if (request != null) {
try { try {
this.sendRequest(registration, lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest); this.sendRequest(registration, lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest);
} catch (ClientSleepingException e) { } catch (ClientSleepingException e) {
DownlinkRequest finalRequest = request; SimpleDownlinkRequest finalRequest = request;
long finalTimeoutInMs = timeoutInMs; long finalTimeoutInMs = timeoutInMs;
Lwm2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest; Lwm2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest;
lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest)); lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest));
@ -185,7 +183,7 @@ public class LwM2mTransportRequest {
} }
String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO,
typeOper.name(), paths); typeOper.name(), paths);
this.handler.sendLogsToThingsboard(msg, registration.getId()); this.handler.sendLogsToThingsboard(lwM2MClient, msg);
if (lwm2mClientRpcRequest != null) { if (lwm2mClientRpcRequest != null) {
String valueMsg = String.format("Paths - %s", paths); String valueMsg = String.format("Paths - %s", paths);
this.handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); this.handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
@ -204,7 +202,7 @@ public class LwM2mTransportRequest {
observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO,
OBSERVE_CANCEL.name(), observeCancelCnt); OBSERVE_CANCEL.name(), observeCancelCnt);
} }
this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest); this.afterObserveCancel(lwM2MClient, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest);
break; break;
// lwm2mClientRpcRequest != null // lwm2mClientRpcRequest != null
case FW_UPDATE: case FW_UPDATE:
@ -215,7 +213,7 @@ public class LwM2mTransportRequest {
} catch (Exception e) { } catch (Exception e) {
String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
typeOper.name(), e.getMessage()); typeOper.name(), e.getMessage());
handler.sendLogsToThingsboard(msg, registration.getId()); handler.sendLogsToThingsboard(lwM2MClient, msg);
if (lwm2mClientRpcRequest != null) { if (lwm2mClientRpcRequest != null) {
String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage()); String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage());
handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
@ -223,10 +221,10 @@ public class LwM2mTransportRequest {
} }
} }
private DownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, private SimpleDownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper,
ContentFormat contentFormat, String target, String targetIdVer, ContentFormat contentFormat, String target, String targetIdVer,
LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) { LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) {
DownlinkRequest request = null; SimpleDownlinkRequest request = null;
switch (typeOper) { switch (typeOper) {
case READ: case READ:
request = new ReadRequest(contentFormat, target); request = new ReadRequest(contentFormat, target);
@ -273,7 +271,7 @@ public class LwM2mTransportRequest {
contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat); contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat);
request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type, resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type,
registration, rpcRequest); lwM2MClient, rpcRequest);
} }
break; break;
case WRITE_UPDATE: case WRITE_UPDATE:
@ -329,7 +327,7 @@ public class LwM2mTransportRequest {
*/ */
@SuppressWarnings({"error sendRequest"}) @SuppressWarnings({"error sendRequest"})
private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, private void sendRequest(Registration registration, LwM2mClient lwM2MClient, SimpleDownlinkRequest request,
long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
context.getServer().send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> { context.getServer().send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
@ -337,11 +335,11 @@ public class LwM2mTransportRequest {
lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
} }
if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest); this.handleResponse(lwM2MClient, request.getPath().toString(), response, request, rpcRequest);
} else { } else {
String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(), String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(),
((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
handler.sendLogsToThingsboard(msg, registration.getId()); handler.sendLogsToThingsboard(lwM2MClient, msg);
log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(), log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(),
((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
if (!lwM2MClient.isInit()) { if (!lwM2MClient.isInit()) {
@ -388,7 +386,7 @@ public class LwM2mTransportRequest {
} }
String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s", String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s",
LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage()); LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage());
handler.sendLogsToThingsboard(msg, registration.getId()); handler.sendLogsToThingsboard(lwM2MClient, msg);
log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
if (rpcRequest != null) { if (rpcRequest != null) {
handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
@ -398,7 +396,7 @@ public class LwM2mTransportRequest {
private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId,
Integer resourceId, Object value, ResourceModel.Type type, Integer resourceId, Object value, ResourceModel.Type type,
Registration registration, Lwm2mClientRpcRequest rpcRequest) { LwM2mClient client, Lwm2mClientRpcRequest rpcRequest) {
try { try {
if (type != null) { if (type != null) {
switch (type) { switch (type) {
@ -433,7 +431,7 @@ public class LwM2mTransportRequest {
String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
patn, type, value, e.toString()); patn, type, value, e.toString());
handler.sendLogsToThingsboard(msg, registration.getId()); handler.sendLogsToThingsboard(client, msg);
log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
if (rpcRequest != null) { if (rpcRequest != null) {
String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
@ -443,13 +441,13 @@ public class LwM2mTransportRequest {
} }
} }
private void handleResponse(Registration registration, final String path, LwM2mResponse response, private void handleResponse(LwM2mClient lwM2mClient, final String path, LwM2mResponse response,
DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { SimpleDownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
responseRequestExecutor.submit(() -> { responseRequestExecutor.submit(() -> {
try { try {
this.sendResponse(registration, path, response, request, rpcRequest); this.sendResponse(lwM2mClient, path, response, request, rpcRequest);
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", registration.getEndpoint(), path, e); log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", lwM2mClient.getRegistration().getEndpoint(), path, e);
} }
}); });
} }
@ -461,8 +459,9 @@ public class LwM2mTransportRequest {
* @param path - * @param path -
* @param response - * @param response -
*/ */
private void sendResponse(Registration registration, String path, LwM2mResponse response, private void sendResponse(LwM2mClient lwM2mClient, String path, LwM2mResponse response,
DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { SimpleDownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
Registration registration = lwM2mClient.getRegistration();
String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
String msgLog = ""; String msgLog = "";
if (response instanceof ReadResponse) { if (response instanceof ReadResponse) {
@ -477,7 +476,7 @@ public class LwM2mTransportRequest {
String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks()); String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks());
msgLog = String.format("%s: type operation: %s path: %s value: %s", msgLog = String.format("%s: type operation: %s path: %s value: %s",
LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue); LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue);
handler.sendLogsToThingsboard(msgLog, registration.getId()); handler.sendLogsToThingsboard(lwM2mClient, msgLog);
log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response); log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response);
if (rpcRequest != null) { if (rpcRequest != null) {
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE); handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE);
@ -486,7 +485,7 @@ public class LwM2mTransportRequest {
msgLog = String.format("%s: type operation: %s path: %s", msgLog = String.format("%s: type operation: %s path: %s",
LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString()); LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString());
log.warn("9) [{}] ", msgLog); log.warn("9) [{}] ", msgLog);
handler.sendLogsToThingsboard(msgLog, registration.getId()); handler.sendLogsToThingsboard(lwM2mClient, msgLog);
if (rpcRequest != null) { if (rpcRequest != null) {
msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg()); msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg());
rpcRequest.setInfoMsg(msgLog); rpcRequest.setInfoMsg(msgLog);
@ -496,7 +495,7 @@ public class LwM2mTransportRequest {
} else if (response instanceof WriteAttributesResponse) { } else if (response instanceof WriteAttributesResponse) {
msgLog = String.format("%s: type operation: %s path: %s value: %s", msgLog = String.format("%s: type operation: %s path: %s value: %s",
LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString()); LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString());
handler.sendLogsToThingsboard(msgLog, registration.getId()); handler.sendLogsToThingsboard(lwM2mClient, msgLog);
log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response); log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response);
if (rpcRequest != null) { if (rpcRequest != null) {
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE); handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE);
@ -504,13 +503,14 @@ public class LwM2mTransportRequest {
} else if (response instanceof WriteResponse) { } else if (response instanceof WriteResponse) {
msgLog = String.format("Type operation: Write path: %s", pathIdVer); msgLog = String.format("Type operation: Write path: %s", pathIdVer);
log.warn("10) [{}] response: [{}]", msgLog, response); log.warn("10) [{}] response: [{}]", msgLog, response);
this.infoWriteResponse(registration, response, request, rpcRequest); this.infoWriteResponse(lwM2mClient, response, request, rpcRequest);
handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
} }
} }
private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { private void infoWriteResponse(LwM2mClient lwM2mClient, LwM2mResponse response, SimpleDownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
try { try {
Registration registration = lwM2mClient.getRegistration();
LwM2mNode node = ((WriteRequest) request).getNode(); LwM2mNode node = ((WriteRequest) request).getNode();
String msg = null; String msg = null;
Object value; Object value;
@ -545,7 +545,7 @@ public class LwM2mTransportRequest {
} }
} }
if (msg != null) { if (msg != null) {
handler.sendLogsToThingsboard(msg, registration.getId()); handler.sendLogsToThingsboard(lwM2mClient, msg);
if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
this.afterWriteSuccessFwSwUpdate(registration, request); this.afterWriteSuccessFwSwUpdate(registration, request);
if (rpcRequest != null) { if (rpcRequest != null) {
@ -566,7 +566,7 @@ public class LwM2mTransportRequest {
* fw_state/sw_state = DOWNLOADED * fw_state/sw_state = DOWNLOADED
* send operation Execute * send operation Execute
*/ */
private void afterWriteSuccessFwSwUpdate(Registration registration, DownlinkRequest request) { private void afterWriteSuccessFwSwUpdate(Registration registration, SimpleDownlinkRequest request) {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) { if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name()); lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name());
@ -581,7 +581,7 @@ public class LwM2mTransportRequest {
/** /**
* After finish operation FwSwUpdate Write (error): fw_state = FAILED * After finish operation FwSwUpdate Write (error): fw_state = FAILED
*/ */
private void afterWriteFwSWUpdateError(Registration registration, DownlinkRequest request, String msgError) { private void afterWriteFwSWUpdateError(Registration registration, SimpleDownlinkRequest request, String msgError) {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) { if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name()); lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name());
@ -593,7 +593,7 @@ public class LwM2mTransportRequest {
} }
} }
private void afterExecuteFwSwUpdateError(Registration registration, DownlinkRequest request, String msgError) { private void afterExecuteFwSwUpdateError(Registration registration, SimpleDownlinkRequest request, String msgError) {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) { if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); lwM2MClient.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
@ -603,8 +603,8 @@ public class LwM2mTransportRequest {
} }
} }
private void afterObserveCancel(Registration registration, int observeCancelCnt, String observeCancelMsg, Lwm2mClientRpcRequest rpcRequest) { private void afterObserveCancel(LwM2mClient lwM2mClient, int observeCancelCnt, String observeCancelMsg, Lwm2mClientRpcRequest rpcRequest) {
handler.sendLogsToThingsboard(observeCancelMsg, registration.getId()); handler.sendLogsToThingsboard(lwM2mClient, observeCancelMsg);
log.warn("[{}]", observeCancelMsg); log.warn("[{}]", observeCancelMsg);
if (rpcRequest != null) { if (rpcRequest != null) {
rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt)); rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt));

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java

@ -137,7 +137,7 @@ public class LwM2mTransportServerHelper {
public ObjectModel parseFromXmlToObjectModel(byte[] xmlByte, String streamName, DefaultDDFFileValidator ddfValidator) { public ObjectModel parseFromXmlToObjectModel(byte[] xmlByte, String streamName, DefaultDDFFileValidator ddfValidator) {
try { try {
DDFFileParser ddfFileParser = new DDFFileParser(ddfValidator); DDFFileParser ddfFileParser = new DDFFileParser(ddfValidator);
return ddfFileParser.parseEx(new ByteArrayInputStream(xmlByte), streamName).get(0); return ddfFileParser.parse(new ByteArrayInputStream(xmlByte), streamName).get(0);
} catch (IOException | InvalidDDFFileException e) { } catch (IOException | InvalidDDFFileException e) {
log.error("Could not parse the XML file [{}]", streamName, e); log.error("Could not parse the XML file [{}]", streamName, e);
return null; return null;

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

@ -36,7 +36,7 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.request.DownlinkRequest; import org.eclipse.leshan.core.request.SimpleDownlinkRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
@ -839,7 +839,7 @@ public class LwM2mTransportUtil {
* Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60"); * Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60");
* Attribute [] attrs = {gt, st}; * Attribute [] attrs = {gt, st};
*/ */
public static DownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MTransportMsgHandler serviceImpl) { public static SimpleDownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MTransportMsgHandler serviceImpl) {
AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target)); AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target));
return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null; return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null;
} }

22
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2MClientState.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.client;
public enum LwM2MClientState {
CREATED, REGISTERED, UNREGISTERED
}

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

@ -0,0 +1,31 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.server.client;
import lombok.Getter;
public class LwM2MClientStateException extends Exception {
private static final long serialVersionUID = 3307690997951364046L;
@Getter
private final LwM2MClientState state;
public LwM2MClientStateException(LwM2MClientState state, String message) {
super(message);
this.state = state;
}
}

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

@ -26,6 +26,7 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.SecurityInfo; import org.eclipse.leshan.server.security.SecurityInfo;
@ -49,6 +50,8 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE;
@ -62,12 +65,28 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.g
@Slf4j @Slf4j
public class LwM2mClient implements Cloneable { public class LwM2mClient implements Cloneable {
private final String nodeId;
@Getter
private final String endpoint;
private final Lock lock;
@Getter
@Setter
private LwM2MClientState state;
@Getter
private final Map<String, ResourceValue> resources;
@Getter
private final Map<String, TsKvProto> delayedRequests;
@Getter
private final List<String> pendingReadRequests;
@Getter
private final Queue<LwM2mQueuedRequest> queuedRequests;
@Getter @Getter
private String deviceName; private String deviceName;
@Getter @Getter
private String deviceProfileName; private String deviceProfileName;
@Getter
private String endpoint;
@Getter @Getter
private String identity; private String identity;
@Getter @Getter
@ -92,15 +111,6 @@ public class LwM2mClient implements Cloneable {
private ValidateDeviceCredentialsResponse credentials; private ValidateDeviceCredentialsResponse credentials;
@Getter
private final Map<String, ResourceValue> resources;
@Getter
private final Map<String, TsKvProto> delayedRequests;
@Getter
@Setter
private final List<String> pendingReadRequests;
@Getter
private final Queue<LwM2mQueuedRequest> queuedRequests;
@Getter @Getter
private boolean init; private boolean init;
@ -108,18 +118,23 @@ public class LwM2mClient implements Cloneable {
return super.clone(); return super.clone();
} }
public LwM2mClient(String nodeId, String endpoint, String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponse credentials, UUID profileId, UUID sessionId) { public LwM2mClient(String nodeId, String endpoint) {
this.nodeId = nodeId;
this.endpoint = endpoint; this.endpoint = endpoint;
this.identity = identity; this.lock = new ReentrantLock();
this.securityInfo = securityInfo;
this.credentials = credentials;
this.delayedRequests = new ConcurrentHashMap<>(); this.delayedRequests = new ConcurrentHashMap<>();
this.pendingReadRequests = new CopyOnWriteArrayList<>(); this.pendingReadRequests = new CopyOnWriteArrayList<>();
this.resources = new ConcurrentHashMap<>(); this.resources = new ConcurrentHashMap<>();
this.profileId = profileId;
this.init = false;
this.queuedRequests = new ConcurrentLinkedQueue<>(); this.queuedRequests = new ConcurrentLinkedQueue<>();
this.state = LwM2MClientState.CREATED;
}
public void init(String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponse credentials, UUID profileId, UUID sessionId) {
this.identity = identity;
this.securityInfo = securityInfo;
this.credentials = credentials;
this.profileId = profileId;
this.init = false;
this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE); this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE);
this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE); this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE);
if (this.credentials != null && this.credentials.hasDeviceInfo()) { if (this.credentials != null && this.credentials.hasDeviceInfo()) {
@ -131,6 +146,14 @@ public class LwM2mClient implements Cloneable {
} }
} }
public void lock() {
lock.lock();
}
public void unlock() {
lock.unlock();
}
public void onDeviceUpdate(Device device, Optional<DeviceProfile> deviceProfileOpt) { public void onDeviceUpdate(Device device, Optional<DeviceProfile> deviceProfileOpt) {
SessionInfoProto.Builder builder = SessionInfoProto.newBuilder().mergeFrom(session); SessionInfoProto.Builder builder = SessionInfoProto.newBuilder().mergeFrom(session);
this.deviceId = device.getUuidId(); this.deviceId = device.getUuidId();
@ -193,9 +216,7 @@ public class LwM2mClient implements Cloneable {
public Object getResourceValue(String pathRezIdVer, String pathRezId) { public Object getResourceValue(String pathRezIdVer, String pathRezId) {
String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer; String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
if (this.resources.get(pathRez) != null) { if (this.resources.get(pathRez) != null) {
return this.resources.get(pathRez).getLwM2mResource().isMultiInstances() ? return this.resources.get(pathRez).getLwM2mResource().getValue();
this.resources.get(pathRez).getLwM2mResource().getValues() :
this.resources.get(pathRez).getLwM2mResource().getValue();
} }
return null; return null;
} }
@ -366,5 +387,14 @@ public class LwM2mClient implements Cloneable {
} }
} }
public ContentFormat getDefaultContentFormat() {
if (registration == null) {
return ContentFormat.DEFAULT;
} else if (registration.getLwM2mVersion().equals("1.0")) {
return ContentFormat.TLV;
} else {
return ContentFormat.TEXT;
}
}
} }

19
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.lwm2m.server.client; package org.thingsboard.server.transport.lwm2m.server.client;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
@ -27,21 +28,17 @@ import java.util.UUID;
public interface LwM2mClientContext { public interface LwM2mClientContext {
void removeClientByRegistrationId(String registrationId);
LwM2mClient getClientByEndpoint(String endpoint);
LwM2mClient getClientByRegistrationId(String registrationId); LwM2mClient getClientByRegistrationId(String registrationId);
LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo); LwM2mClient getClientByEndpoint(String endpoint);
LwM2mClient getOrRegister(Registration registration); LwM2mClient getClientBySessionInfo(TransportProtos.SessionInfoProto sessionInfo);
LwM2mClient registerOrUpdate(Registration registration); void register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException;
LwM2mClient fetchClientByEndpoint(String endpoint); void updateRegistration(LwM2mClient client, Registration registration) throws LwM2MClientStateException;
Registration getRegistration(String registrationId); void unregister(LwM2mClient client, Registration registration) throws LwM2MClientStateException;
Collection<LwM2mClient> getLwM2mClients(); Collection<LwM2mClient> getLwM2mClients();
@ -55,9 +52,11 @@ public interface LwM2mClientContext {
LwM2mClientProfile profileUpdate(DeviceProfile deviceProfile); LwM2mClientProfile profileUpdate(DeviceProfile deviceProfile);
Set<String> getSupportedIdVerInClient(Registration registration); Set<String> getSupportedIdVerInClient(LwM2mClient registration);
LwM2mClient getClientByDeviceId(UUID deviceId); LwM2mClient getClientByDeviceId(UUID deviceId);
void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials); void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials);
} }

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

@ -19,16 +19,15 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -48,46 +47,107 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.c
public class LwM2mClientContextImpl implements LwM2mClientContext { public class LwM2mClientContextImpl implements LwM2mClientContext {
private final LwM2mTransportContext context; private final LwM2mTransportContext context;
private final TbEditableSecurityStore securityStore;
private final Map<String, LwM2mClient> lwM2mClientsByEndpoint = new ConcurrentHashMap<>(); private final Map<String, LwM2mClient> lwM2mClientsByEndpoint = new ConcurrentHashMap<>();
private final Map<String, LwM2mClient> lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); private final Map<String, LwM2mClient> lwM2mClientsByRegistrationId = new ConcurrentHashMap<>();
private Map<UUID, LwM2mClientProfile> profiles = new ConcurrentHashMap<>(); private Map<UUID, LwM2mClientProfile> profiles = new ConcurrentHashMap<>();
private final LwM2mCredentialsSecurityInfoValidator lwM2MCredentialsSecurityInfoValidator;
private final EditableSecurityStore securityStore;
@Override @Override
public LwM2mClient getClientByEndpoint(String endpoint) { public LwM2mClient getClientByEndpoint(String endpoint) {
return lwM2mClientsByEndpoint.get(endpoint); return lwM2mClientsByEndpoint.computeIfAbsent(endpoint, ep -> new LwM2mClient(context.getNodeId(), ep));
} }
@Override @Override
public LwM2mClient getClientByRegistrationId(String registrationId) { public void register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException {
return lwM2mClientsByRegistrationId.get(registrationId); lwM2MClient.lock();
try {
if (LwM2MClientState.UNREGISTERED.equals(lwM2MClient.getState())) {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client is in invalid state.");
}
TbLwM2MSecurityInfo securityInfo = securityStore.getTbLwM2MSecurityInfoByEndpoint(lwM2MClient.getEndpoint());
if (securityInfo.getSecurityMode() != null) {
if (securityInfo.getDeviceProfile() != null) {
UUID profileUuid = profileUpdate(securityInfo.getDeviceProfile()) != null ? securityInfo.getDeviceProfile().getUuidId() : null;
if (securityInfo.getSecurityInfo() != null) {
lwM2MClient.init(securityInfo.getSecurityInfo().getIdentity(), securityInfo.getSecurityInfo(), securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else if (NO_SEC.equals(securityInfo.getSecurityMode())) {
lwM2MClient.init(null, null, securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", lwM2MClient.getEndpoint()));
}
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", lwM2MClient.getEndpoint()));
}
} else {
throw new RuntimeException(String.format("Registration failed: FORBIDDEN, endpointId: %s", lwM2MClient.getEndpoint()));
}
lwM2MClient.setRegistration(registration);
this.lwM2mClientsByRegistrationId.put(registration.getId(), lwM2MClient);
lwM2MClient.setState(LwM2MClientState.REGISTERED);
} finally {
lwM2MClient.unlock();
}
} }
@Override @Override
public LwM2mClient getOrRegister(Registration registration) { public void updateRegistration(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException {
if (registration == null) { lwM2MClient.lock();
return null; try {
if (!LwM2MClientState.REGISTERED.equals(lwM2MClient.getState())) {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client is in invalid state.");
}
Registration currentRegistration = lwM2MClient.getRegistration();
if (currentRegistration.getId().equals(registration.getId())) {
lwM2MClient.setRegistration(registration);
} else {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client has different registration.");
}
} finally {
lwM2MClient.unlock();
} }
LwM2mClient client = lwM2mClientsByRegistrationId.get(registration.getId()); }
if (client == null) {
client = lwM2mClientsByEndpoint.get(registration.getEndpoint()); @Override
if (client == null) { public void unregister(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException {
client = registerOrUpdate(registration); lwM2MClient.lock();
try {
if (!LwM2MClientState.REGISTERED.equals(lwM2MClient.getState())) {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client is in invalid state.");
} }
lwM2mClientsByRegistrationId.remove(registration.getId());
Registration currentRegistration = lwM2MClient.getRegistration();
if (currentRegistration.getId().equals(registration.getId())) {
lwM2MClient.setState(LwM2MClientState.UNREGISTERED);
lwM2mClientsByEndpoint.remove(lwM2MClient.getEndpoint());
this.securityStore.remove(lwM2MClient.getEndpoint());
this.lwM2mClientsByRegistrationId.remove(registration.getId());
UUID profileId = lwM2MClient.getProfileId();
if (profileId != null) {
Optional<LwM2mClient> otherClients = lwM2mClientsByRegistrationId.values().stream().filter(e -> e.getProfileId().equals(profileId)).findFirst();
if (otherClients.isEmpty()) {
profiles.remove(profileId);
}
}
} else {
throw new LwM2MClientStateException(lwM2MClient.getState(), "Client has different registration.");
}
} finally {
lwM2MClient.unlock();
} }
return client;
} }
@Override @Override
public LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo) { public LwM2mClient getClientByRegistrationId(String registrationId) {
LwM2mClient lwM2mClient = lwM2mClientsByEndpoint.values().stream().filter(c -> return lwM2mClientsByRegistrationId.get(registrationId);
}
@Override
public LwM2mClient getClientBySessionInfo(TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2mClient = lwM2mClientsByEndpoint.values().stream().filter(c ->
(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())) (new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()))
.equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB()))) .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB())))
).findAny().get(); ).findAny().orElse(null);
if (lwM2mClient == null) { if (lwM2mClient == null) {
log.warn("Device TimeOut? lwM2mClient is null."); log.warn("Device TimeOut? lwM2mClient is null.");
log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}]", sessionInfo, lwM2mClientsByEndpoint.values().size()); log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}]", sessionInfo, lwM2mClientsByEndpoint.values().size());
@ -96,60 +156,14 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
return lwM2mClient; return lwM2mClient;
} }
@Override
public LwM2mClient registerOrUpdate(Registration registration) {
LwM2mClient lwM2MClient = lwM2mClientsByEndpoint.get(registration.getEndpoint());
if (lwM2MClient == null) {
lwM2MClient = this.fetchClientByEndpoint(registration.getEndpoint());
}
lwM2MClient.setRegistration(registration);
// TODO: this remove is probably redundant. We should remove it.
// this.lwM2mClientsByEndpoint.remove(registration.getEndpoint());
this.lwM2mClientsByRegistrationId.put(registration.getId(), lwM2MClient);
return lwM2MClient;
}
public Registration getRegistration(String registrationId) { public Registration getRegistration(String registrationId) {
return this.lwM2mClientsByRegistrationId.get(registrationId).getRegistration(); return this.lwM2mClientsByRegistrationId.get(registrationId).getRegistration();
} }
@Override
public LwM2mClient fetchClientByEndpoint(String endpoint) {
EndpointSecurityInfo securityInfo = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(endpoint, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
if (securityInfo.getSecurityMode() != null) {
if (securityInfo.getDeviceProfile() != null) {
UUID profileUuid = profileUpdate(securityInfo.getDeviceProfile())!= null ?
securityInfo.getDeviceProfile().getUuidId() : null;
// TODO: for tests bug.
if (profileUuid== null) {
log.trace("input parameters toClientProfile if the result is null: [{}]", securityInfo.getDeviceProfile());
}
LwM2mClient client;
if (securityInfo.getSecurityInfo() != null) {
client = new LwM2mClient(context.getNodeId(), securityInfo.getSecurityInfo().getEndpoint(),
securityInfo.getSecurityInfo().getIdentity(), securityInfo.getSecurityInfo(),
securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else if (NO_SEC.equals(securityInfo.getSecurityMode())) {
client = new LwM2mClient(context.getNodeId(), endpoint,
null, null,
securityInfo.getMsg(), profileUuid, UUID.randomUUID());
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", endpoint));
}
lwM2mClientsByEndpoint.put(client.getEndpoint(), client);
return client;
} else {
throw new RuntimeException(String.format("Registration failed: device %s not found.", endpoint));
}
} else {
throw new RuntimeException(String.format("Registration failed: FORBIDDEN, endpointId: %s", endpoint));
}
}
@Override @Override
public void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials) { public void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials) {
LwM2mClient client = new LwM2mClient(context.getNodeId(), registration.getEndpoint(), null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID()); LwM2mClient client = getClientByEndpoint(registration.getEndpoint());
lwM2mClientsByEndpoint.put(registration.getEndpoint(), client); client.init(null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID());
lwM2mClientsByRegistrationId.put(registration.getId(), client); lwM2mClientsByRegistrationId.put(registration.getId(), client);
profileUpdate(credentials.getDeviceProfile()); profileUpdate(credentials.getDeviceProfile());
} }
@ -171,7 +185,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
@Override @Override
public LwM2mClientProfile getProfile(Registration registration) { public LwM2mClientProfile getProfile(Registration registration) {
return this.getProfiles().get(getOrRegister(registration).getProfileId()); return this.getProfiles().get(getClientByEndpoint(registration.getEndpoint()).getProfileId());
} }
@Override @Override
@ -186,25 +200,18 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
if (lwM2MClientProfile != null) { if (lwM2MClientProfile != null) {
profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile); profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile);
return lwM2MClientProfile; return lwM2MClientProfile;
} } else {
else {
return null; return null;
} }
} }
/**
* if isVer - ok or default ver=DEFAULT_LWM2M_VERSION
*
* @param registration -
* @return - all objectIdVer in client
*/
@Override @Override
public Set<String> getSupportedIdVerInClient(Registration registration) { public Set<String> getSupportedIdVerInClient(LwM2mClient client) {
Set<String> clientObjects = ConcurrentHashMap.newKeySet(); Set<String> clientObjects = ConcurrentHashMap.newKeySet();
Arrays.stream(registration.getObjectLinks()).forEach(url -> { Arrays.stream(client.getRegistration().getObjectLinks()).forEach(link -> {
LwM2mPath pathIds = new LwM2mPath(url.getUrl()); LwM2mPath pathIds = new LwM2mPath(link.getUrl());
if (!pathIds.isRoot()) { if (!pathIds.isRoot()) {
clientObjects.add(convertPathFromObjectIdToIdVer(url.getUrl(), registration)); clientObjects.add(convertPathFromObjectIdToIdVer(link.getUrl(), client.getRegistration()));
} }
}); });
return (clientObjects.size() > 0) ? clientObjects : null; return (clientObjects.size() > 0) ? clientObjects : null;
@ -215,20 +222,4 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null); return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null);
} }
@Override
public void removeClientByRegistrationId(String registrationId) {
LwM2mClient lwM2MClient = this.lwM2mClientsByRegistrationId.get(registrationId);
if (lwM2MClient != null) {
this.securityStore.remove(lwM2MClient.getEndpoint(), false);
this.lwM2mClientsByEndpoint.remove(lwM2MClient.getEndpoint());
this.lwM2mClientsByRegistrationId.remove(registrationId);
UUID profileId = lwM2MClient.getProfileId();
if (profileId != null) {
Optional<LwM2mClient> otherClients = lwM2mClientsByRegistrationId.values().stream().filter(e -> e.getProfileId().equals(profileId)).findFirst();
if (otherClients.isEmpty()) {
profiles.remove(profileId);
}
}
}
}
} }

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

@ -167,9 +167,9 @@ public class LwM2mFwSwUpdate {
String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration());
String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LW2M_INFO, String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LW2M_INFO,
LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE.name(), FW_PACKAGE_ID); LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE.name(), FW_PACKAGE_ID);
handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId()); handler.sendLogsToThingsboard(lwM2MClient, fwMsg);
log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer); log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer);
request.sendAllRequest(this.lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE,
firmwareChunk, handler.config.getTimeout(), this.rpcRequest); firmwareChunk, handler.config.getTimeout(), this.rpcRequest);
} }
else { else {
@ -190,7 +190,7 @@ public class LwM2mFwSwUpdate {
if (LOG_LW2M_ERROR.equals(typeInfo)) { if (LOG_LW2M_ERROR.equals(typeInfo)) {
msg = String.format("%s Error: %s", msg, msgError); msg = String.format("%s Error: %s", msg, msgError);
} }
handler.sendLogsToThingsboard(msg, lwM2MClient.getRegistration().getId()); handler.sendLogsToThingsboard(lwM2MClient, msg);
} }
@ -202,8 +202,7 @@ public class LwM2mFwSwUpdate {
public void executeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { public void executeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
this.setStateUpdate(UPDATING.name()); this.setStateUpdate(UPDATING.name());
this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null);
request.sendAllRequest(this.lwM2MClient.getRegistration(), this.pathInstallId, EXECUTE, ContentFormat.TLV.getName(), request.sendAllRequest(this.lwM2MClient, this.pathInstallId, EXECUTE, null, 0, this.rpcRequest);
null, 0, this.rpcRequest);
} }
/** /**
@ -334,10 +333,10 @@ public class LwM2mFwSwUpdate {
} }
private void observeStateUpdate(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { private void observeStateUpdate(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
request.sendAllRequest(lwM2MClient.getRegistration(), request.sendAllRequest(lwM2MClient,
convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE, convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE,
null, null, 0, null); null, null, 0, null);
request.sendAllRequest(lwM2MClient.getRegistration(), request.sendAllRequest(lwM2MClient,
convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE, convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE,
null, null, 0, null); null, null, 0, null);
} }
@ -364,8 +363,7 @@ public class LwM2mFwSwUpdate {
this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
this.pathResultId, this.lwM2MClient.getRegistration())); this.pathResultId, this.lwM2MClient.getRegistration()));
this.pendingInfoRequestsStart.forEach(pathIdVer -> { this.pendingInfoRequestsStart.forEach(pathIdVer -> {
request.sendAllRequest(this.lwM2MClient.getRegistration(), pathIdVer, OBSERVE, ContentFormat.TLV.getName(), request.sendAllRequest(this.lwM2MClient, pathIdVer, OBSERVE, null, 0, this.rpcRequest);
null, 0, this.rpcRequest);
}); });
} }

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

@ -273,7 +273,7 @@ public class Lwm2mClientRpcRequest {
} }
private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MTransportMsgHandler handler) { private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MTransportMsgHandler handler) {
LwM2mClient lwM2mClient = handler.clientContext.getClient(this.sessionInfo); LwM2mClient lwM2mClient = handler.clientContext.getClientBySessionInfo(this.sessionInfo);
return lwM2mClient != null ? return lwM2mClient != null ?
lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) : lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) :
null; null;

27
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbEditableSecurityStore.java

@ -0,0 +1,27 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
public interface TbEditableSecurityStore extends TbSecurityStore {
void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException;
void remove(String endpoint);
}

130
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbInMemorySecurityStore.java

@ -0,0 +1,130 @@
/**
* 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.store;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class TbInMemorySecurityStore implements TbEditableSecurityStore {
// lock for the two maps
protected final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
protected final Lock readLock = readWriteLock.readLock();
protected final Lock writeLock = readWriteLock.writeLock();
// by client end-point
protected Map<String, TbLwM2MSecurityInfo> securityByEp = new HashMap<>();
// by PSK identity
protected Map<String, TbLwM2MSecurityInfo> securityByIdentity = new HashMap<>();
public TbInMemorySecurityStore() {
}
/**
* {@inheritDoc}
*/
@Override
public SecurityInfo getByEndpoint(String endpoint) {
readLock.lock();
try {
TbLwM2MSecurityInfo securityInfo = securityByEp.get(endpoint);
if (securityInfo != null) {
return securityInfo.getSecurityInfo();
} else {
return null;
}
} finally {
readLock.unlock();
}
}
/**
* {@inheritDoc}
*/
@Override
public SecurityInfo getByIdentity(String identity) {
readLock.lock();
try {
TbLwM2MSecurityInfo securityInfo = securityByIdentity.get(identity);
if (securityInfo != null) {
return securityInfo.getSecurityInfo();
} else {
return null;
}
} finally {
readLock.unlock();
}
}
@Override
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
writeLock.lock();
try {
String identity = null;
if (tbSecurityInfo.getSecurityInfo() != null) {
identity = tbSecurityInfo.getSecurityInfo().getIdentity();
if (identity != null) {
TbLwM2MSecurityInfo infoByIdentity = securityByIdentity.get(identity);
if (infoByIdentity != null && !tbSecurityInfo.getSecurityInfo().getEndpoint().equals(infoByIdentity.getEndpoint())) {
throw new NonUniqueSecurityInfoException("PSK Identity " + identity + " is already used");
}
securityByIdentity.put(tbSecurityInfo.getSecurityInfo().getIdentity(), tbSecurityInfo);
}
}
TbLwM2MSecurityInfo previous = securityByEp.put(tbSecurityInfo.getEndpoint(), tbSecurityInfo);
if (previous != null && previous.getSecurityInfo() != null) {
String previousIdentity = previous.getSecurityInfo().getIdentity();
if (previousIdentity != null && !previousIdentity.equals(identity)) {
securityByIdentity.remove(previousIdentity);
}
}
} finally {
writeLock.unlock();
}
}
@Override
public void remove(String endpoint) {
writeLock.lock();
try {
TbLwM2MSecurityInfo securityInfo = securityByEp.remove(endpoint);
if (securityInfo != null && securityInfo.getSecurityInfo() != null && securityInfo.getSecurityInfo().getIdentity() != null) {
securityByIdentity.remove(securityInfo.getSecurityInfo().getIdentity());
}
} finally {
writeLock.unlock();
}
}
@Override
public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) {
readLock.lock();
try {
return securityByEp.get(endpoint);
} finally {
readLock.unlock();
}
}
}

54
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java

@ -22,11 +22,13 @@ import org.eclipse.leshan.core.Destroyable;
import org.eclipse.leshan.core.Startable; import org.eclipse.leshan.core.Startable;
import org.eclipse.leshan.core.Stoppable; import org.eclipse.leshan.core.Stoppable;
import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.core.util.NamedThreadFactory;
import org.eclipse.leshan.core.util.Validate; import org.eclipse.leshan.core.util.Validate;
import org.eclipse.leshan.server.californium.observation.ObserveUtil; import org.eclipse.leshan.server.californium.observation.ObserveUtil;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
import org.eclipse.leshan.server.redis.RedisRegistrationStore; import org.eclipse.leshan.server.redis.RedisRegistrationStore;
import org.eclipse.leshan.server.redis.serialization.IdentitySerDes;
import org.eclipse.leshan.server.redis.serialization.ObservationSerDes; import org.eclipse.leshan.server.redis.serialization.ObservationSerDes;
import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes; import org.eclipse.leshan.server.redis.serialization.RegistrationSerDes;
import org.eclipse.leshan.server.registration.Deregistration; import org.eclipse.leshan.server.registration.Deregistration;
@ -73,6 +75,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
private static final String REG_EP = "REG:EP:"; // (Endpoint => Registration) private static final String REG_EP = "REG:EP:"; // (Endpoint => Registration)
private static final String REG_EP_REGID_IDX = "EP:REGID:"; // secondary index key (Registration ID => Endpoint) private static final String REG_EP_REGID_IDX = "EP:REGID:"; // secondary index key (Registration ID => Endpoint)
private static final String REG_EP_ADDR_IDX = "EP:ADDR:"; // secondary index key (Socket Address => Endpoint) private static final String REG_EP_ADDR_IDX = "EP:ADDR:"; // secondary index key (Socket Address => Endpoint)
private static final String REG_EP_IDENTITY = "EP:IDENTITY:"; // secondary index key (Identity => Endpoint)
private static final String LOCK_EP = "LOCK:EP:"; private static final String LOCK_EP = "LOCK:EP:";
private static final byte[] OBS_TKN = "OBS:TKN:".getBytes(UTF_8); private static final byte[] OBS_TKN = "OBS:TKN:".getBytes(UTF_8);
private static final String OBS_TKNS_REGID_IDX = "TKNS:REGID:"; // secondary index (token list by registration) private static final String OBS_TKNS_REGID_IDX = "TKNS:REGID:"; // secondary index (token list by registration)
@ -155,6 +158,8 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
connection.set(regid_idx, registration.getEndpoint().getBytes(UTF_8)); connection.set(regid_idx, registration.getEndpoint().getBytes(UTF_8));
byte[] addr_idx = toRegAddrKey(registration.getSocketAddress()); byte[] addr_idx = toRegAddrKey(registration.getSocketAddress());
connection.set(addr_idx, registration.getEndpoint().getBytes(UTF_8)); connection.set(addr_idx, registration.getEndpoint().getBytes(UTF_8));
byte[] identity_idx = toRegIdentityKey(registration.getIdentity());
connection.set(identity_idx, registration.getEndpoint().getBytes(UTF_8));
// Add or update expiration // Add or update expiration
addOrUpdateExpiration(connection, registration); addOrUpdateExpiration(connection, registration);
@ -167,6 +172,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (!oldRegistration.getSocketAddress().equals(registration.getSocketAddress())) { if (!oldRegistration.getSocketAddress().equals(registration.getSocketAddress())) {
removeAddrIndex(connection, oldRegistration); removeAddrIndex(connection, oldRegistration);
} }
if (!oldRegistration.getIdentity().equals(registration.getIdentity())) {
removeIdentityIndex(connection, oldRegistration);
}
// remove old observation // remove old observation
Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, oldRegistration.getId()); Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, oldRegistration.getId());
@ -222,6 +230,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
if (!r.getSocketAddress().equals(updatedRegistration.getSocketAddress())) { if (!r.getSocketAddress().equals(updatedRegistration.getSocketAddress())) {
removeAddrIndex(connection, r); removeAddrIndex(connection, r);
} }
if (!r.getIdentity().equals(updatedRegistration.getIdentity())) {
removeIdentityIndex(connection, r);
}
return new UpdatedRegistration(r, updatedRegistration); return new UpdatedRegistration(r, updatedRegistration);
@ -268,6 +279,22 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
} }
} }
@Override
public Registration getRegistrationByIdentity(Identity identity) {
Validate.notNull(identity);
try (var connection = connectionFactory.getConnection()) {
byte[] ep = connection.get(toRegIdentityKey(identity));
if (ep == null) {
return null;
}
byte[] data = connection.get(toEndpointKey(ep));
if (data == null) {
return null;
}
return deserializeReg(data);
}
}
@Override @Override
public Iterator<Registration> getAllRegistrations() { public Iterator<Registration> getAllRegistrations() {
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
@ -325,6 +352,7 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
connection.del(toEndpointKey(r.getEndpoint())); connection.del(toEndpointKey(r.getEndpoint()));
Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, r.getId()); Collection<Observation> obsRemoved = unsafeRemoveAllObservations(connection, r.getId());
removeAddrIndex(connection, r); removeAddrIndex(connection, r);
removeIdentityIndex(connection, r);
removeExpiration(connection, r); removeExpiration(connection, r);
return new Deregistration(r, obsRemoved); return new Deregistration(r, obsRemoved);
} }
@ -337,20 +365,27 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
} }
} }
private void removeAddrIndex(RedisConnection connection, Registration r) {
removeSecondaryIndex(connection, toRegAddrKey(r.getSocketAddress()), r.getEndpoint());
}
private void removeIdentityIndex(RedisConnection connection, Registration r) {
removeSecondaryIndex(connection, toRegIdentityKey(r.getIdentity()), r.getEndpoint());
}
//TODO: JedisCluster didn't implement Transaction, maybe should use some advanced key creation strategies //TODO: JedisCluster didn't implement Transaction, maybe should use some advanced key creation strategies
private void removeAddrIndex(RedisConnection connection, Registration registration) { private void removeSecondaryIndex(RedisConnection connection, byte[] indexKey, String endpointName) {
// Watch the key to remove. // Watch the key to remove.
byte[] regAddrKey = toRegAddrKey(registration.getSocketAddress()); // connection.watch(indexKey);
// connection.watch(regAddrKey);
byte[] epFromAddr = connection.get(regAddrKey); byte[] epFromAddr = connection.get(indexKey);
// Delete the key if needed. // Delete the key if needed.
if (Arrays.equals(epFromAddr, registration.getEndpoint().getBytes(UTF_8))) { if (Arrays.equals(epFromAddr, endpointName.getBytes(UTF_8))) {
// Try to delete the key // Try to delete the key
// connection.multi(); // connection.multi();
connection.del(regAddrKey); connection.del(indexKey);
// connection.exec(); // connection.exec();
// if transaction failed this is not an issue as the socket address is probably reused and we don't neeed to // if transaction failed this is not an issue as the index is probably reused and we don't need to
// delete it anymore. // delete it anymore.
} else { } else {
// the key must not be deleted. // the key must not be deleted.
@ -374,6 +409,10 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
return toKey(REG_EP_ADDR_IDX, addr.getAddress().toString() + ":" + addr.getPort()); return toKey(REG_EP_ADDR_IDX, addr.getAddress().toString() + ":" + addr.getPort());
} }
private byte[] toRegIdentityKey(Identity identity) {
return toKey(REG_EP_IDENTITY, IdentitySerDes.serialize(identity).toString());
}
private byte[] toEndpointKey(String endpoint) { private byte[] toEndpointKey(String endpoint) {
return toKey(REG_EP, endpoint); return toKey(REG_EP, endpoint);
} }
@ -723,7 +762,6 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
@Override @Override
public void run() { public void run() {
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
Set<byte[]> endpointsExpired = connection.zRangeByScore(EXP_EP, Double.NEGATIVE_INFINITY, Set<byte[]> endpointsExpired = connection.zRangeByScore(EXP_EP, Double.NEGATIVE_INFINITY,
System.currentTimeMillis(), 0, cleanLimit); System.currentTimeMillis(), 0, cleanLimit);

141
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java

@ -24,13 +24,14 @@ import org.springframework.data.redis.connection.RedisClusterConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.ScanOptions;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class TbLwM2mRedisSecurityStore implements EditableSecurityStore { public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore {
private static final String SEC_EP = "SEC#EP#"; private static final String SEC_EP = "SEC#EP#";
private static final String PSKID_SEC = "PSKID#SEC"; private static final String PSKID_SEC = "PSKID#SEC";
@ -72,73 +73,89 @@ public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
} }
@Override @Override
public Collection<SecurityInfo> getAll() { public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
try (var connection = connectionFactory.getConnection()) { //TODO: implement
Collection<SecurityInfo> list = new LinkedList<>();
ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(SEC_EP + "*").build();
List<Cursor<byte[]>> scans = new ArrayList<>();
if (connection instanceof RedisClusterConnection) {
((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> {
scans.add(((RedisClusterConnection) connection).scan(node, scanOptions));
});
} else {
scans.add(connection.scan(scanOptions));
}
scans.forEach(scan -> {
scan.forEachRemaining(key -> {
byte[] element = connection.get(key);
list.add(deserialize(element));
});
});
return list;
}
} }
@Override @Override
public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException { public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) {
byte[] data = serialize(info); //TODO: implement
try (var connection = connectionFactory.getConnection()) { return null;
if (info.getIdentity() != null) {
// populate the secondary index (security info by PSK id)
String oldEndpoint = new String(connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes()));
if (!oldEndpoint.equals(info.getEndpoint())) {
throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used");
}
connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());
}
byte[] previousData = connection.getSet((SEC_EP + info.getEndpoint()).getBytes(), data);
SecurityInfo previous = previousData == null ? null : deserialize(previousData);
String previousIdentity = previous == null ? null : previous.getIdentity();
if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {
connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
}
return previous;
}
} }
@Override @Override
public SecurityInfo remove(String endpoint, boolean infosAreCompromised) { public void remove(String endpoint) {
try (var connection = connectionFactory.getConnection()) { //TODO: implement
byte[] data = connection.get((SEC_EP + endpoint).getBytes());
if (data != null) {
SecurityInfo info = deserialize(data);
if (info.getIdentity() != null) {
connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
}
connection.del((SEC_EP + endpoint).getBytes());
if (listener != null) {
listener.securityInfoRemoved(infosAreCompromised, info);
}
return info;
}
}
return null;
} }
// @Override
// public Collection<SecurityInfo> getAll() {
// try (var connection = connectionFactory.getConnection()) {
// Collection<SecurityInfo> list = new LinkedList<>();
// ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(SEC_EP + "*").build();
// List<Cursor<byte[]>> scans = new ArrayList<>();
// if (connection instanceof RedisClusterConnection) {
// ((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> {
// scans.add(((RedisClusterConnection) connection).scan(node, scanOptions));
// });
// } else {
// scans.add(connection.scan(scanOptions));
// }
//
// scans.forEach(scan -> {
// scan.forEachRemaining(key -> {
// byte[] element = connection.get(key);
// list.add(deserialize(element));
// });
// });
// return list;
// }
// }
//
// @Override
// public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException {
// byte[] data = serialize(info);
// try (var connection = connectionFactory.getConnection()) {
// if (info.getIdentity() != null) {
// // populate the secondary index (security info by PSK id)
// String oldEndpoint = new String(connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes()));
// if (!oldEndpoint.equals(info.getEndpoint())) {
// throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used");
// }
// connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes());
// }
//
// byte[] previousData = connection.getSet((SEC_EP + info.getEndpoint()).getBytes(), data);
// SecurityInfo previous = previousData == null ? null : deserialize(previousData);
// String previousIdentity = previous == null ? null : previous.getIdentity();
// if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) {
// connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes());
// }
//
// return previous;
// }
// }
//
// @Override
// public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
// try (var connection = connectionFactory.getConnection()) {
// byte[] data = connection.get((SEC_EP + endpoint).getBytes());
//
// if (data != null) {
// SecurityInfo info = deserialize(data);
// if (info.getIdentity() != null) {
// connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes());
// }
// connection.del((SEC_EP + endpoint).getBytes());
// if (listener != null) {
// listener.securityInfoRemoved(infosAreCompromised, info);
// }
// return info;
// }
// }
// return null;
// }
private byte[] serialize(SecurityInfo secInfo) { private byte[] serialize(SecurityInfo secInfo) {
return SecurityInfoSerDes.serialize(secInfo); return SecurityInfoSerDes.serialize(secInfo);
} }
@ -147,8 +164,4 @@ public class TbLwM2mRedisSecurityStore implements EditableSecurityStore {
return SecurityInfoSerDes.deserialize(data); return SecurityInfoSerDes.deserialize(data);
} }
@Override
public void setListener(SecurityStoreListener listener) {
this.listener = listener;
}
} }

82
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java

@ -19,63 +19,39 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.server.security.EditableSecurityStore; import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo; import org.eclipse.leshan.server.security.SecurityInfo;
import org.eclipse.leshan.server.security.SecurityStore;
import org.eclipse.leshan.server.security.SecurityStoreListener; import org.eclipse.leshan.server.security.SecurityStoreListener;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import java.util.Collection; import java.util.Collection;
@Slf4j @Slf4j
@Component public class TbLwM2mSecurityStore implements TbEditableSecurityStore {
@TbLwM2mTransportComponent
public class TbLwM2mSecurityStore implements EditableSecurityStore {
private final LwM2mClientContext clientContext; private final TbEditableSecurityStore securityStore;
private final EditableSecurityStore securityStore; private final LwM2mCredentialsSecurityInfoValidator validator;
public TbLwM2mSecurityStore(LwM2mClientContext clientContext, EditableSecurityStore securityStore) { public TbLwM2mSecurityStore(TbEditableSecurityStore securityStore, LwM2mCredentialsSecurityInfoValidator validator) {
this.clientContext = clientContext;
this.securityStore = securityStore; this.securityStore = securityStore;
this.validator = validator;
} }
@Override @Override
public Collection<SecurityInfo> getAll() { public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) {
return securityStore.getAll(); return securityStore.getTbLwM2MSecurityInfoByEndpoint(endpoint);
}
@Override
public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException {
return securityStore.add(info);
}
@Override
public SecurityInfo remove(String endpoint, boolean infosAreCompromised) {
return securityStore.remove(endpoint, infosAreCompromised);
}
@Override
public void setListener(SecurityStoreListener listener) {
securityStore.setListener(listener);
} }
@Override @Override
public SecurityInfo getByEndpoint(String endpoint) { public SecurityInfo getByEndpoint(String endpoint) {
SecurityInfo securityInfo = securityStore.getByEndpoint(endpoint); SecurityInfo securityInfo = securityStore.getByEndpoint(endpoint);
if (securityInfo == null) { if (securityInfo == null) {
LwM2mClient lwM2mClient = clientContext.getClientByEndpoint(endpoint); securityInfo = fetchAndPutSecurityInfo(endpoint);
if (lwM2mClient != null && lwM2mClient.getRegistration() != null && !lwM2mClient.getRegistration().getIdentity().isSecure()) {
return null;
}
securityInfo = clientContext.fetchClientByEndpoint(endpoint).getSecurityInfo();
try {
if (securityInfo != null) {
add(securityInfo);
}
} catch (NonUniqueSecurityInfoException e) {
log.trace("Failed to add security info: {}", securityInfo, e);
}
} }
return securityInfo; return securityInfo;
} }
@ -84,15 +60,31 @@ public class TbLwM2mSecurityStore implements EditableSecurityStore {
public SecurityInfo getByIdentity(String pskIdentity) { public SecurityInfo getByIdentity(String pskIdentity) {
SecurityInfo securityInfo = securityStore.getByIdentity(pskIdentity); SecurityInfo securityInfo = securityStore.getByIdentity(pskIdentity);
if (securityInfo == null) { if (securityInfo == null) {
securityInfo = clientContext.fetchClientByEndpoint(pskIdentity).getSecurityInfo(); securityInfo = fetchAndPutSecurityInfo(pskIdentity);
try {
if (securityInfo != null) {
add(securityInfo);
}
} catch (NonUniqueSecurityInfoException e) {
log.trace("Failed to add security info: {}", securityInfo, e);
}
} }
return securityInfo; return securityInfo;
} }
@Nullable
public SecurityInfo fetchAndPutSecurityInfo(String credentialsId) {
TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
try {
if (securityInfo != null) {
securityStore.put(securityInfo);
}
} catch (NonUniqueSecurityInfoException e) {
log.trace("Failed to add security info: {}", securityInfo, e);
}
return securityInfo != null ? securityInfo.getSecurityInfo() : null;
}
@Override
public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {
securityStore.put(tbSecurityInfo);
}
@Override
public void remove(String endpoint) {
securityStore.remove(endpoint);
}
} }

14
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mStoreFactory.java

@ -17,17 +17,14 @@ package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
import org.eclipse.leshan.server.californium.registration.InMemoryRegistrationStore; import org.eclipse.leshan.server.californium.registration.InMemoryRegistrationStore;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.InMemorySecurityStore;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.cache.TBRedisCacheConfiguration; import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
import java.util.Optional; import java.util.Optional;
@ -42,8 +39,7 @@ public class TbLwM2mStoreFactory {
private LwM2MTransportServerConfig config; private LwM2MTransportServerConfig config;
@Autowired @Autowired
@Lazy private LwM2mCredentialsSecurityInfoValidator validator;
private LwM2mClientContext clientContext;
@Value("${transport.lwm2m.redis.enabled:false}") @Value("${transport.lwm2m.redis.enabled:false}")
private boolean useRedis; private boolean useRedis;
@ -55,9 +51,9 @@ public class TbLwM2mStoreFactory {
} }
@Bean @Bean
private EditableSecurityStore securityStore() { private TbEditableSecurityStore securityStore() {
return new TbLwM2mSecurityStore(clientContext, redisConfiguration.isPresent() && useRedis ? return new TbLwM2mSecurityStore(redisConfiguration.isPresent() && useRedis ?
new TbLwM2mRedisSecurityStore(redisConfiguration.get().redisConnectionFactory()) : new InMemorySecurityStore()); new TbLwM2mRedisSecurityStore(redisConfiguration.get().redisConnectionFactory()) : new TbInMemorySecurityStore(), validator);
} }
@Bean @Bean

25
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbSecurityStore.java

@ -0,0 +1,25 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.lwm2m.server.store;
import org.eclipse.leshan.server.security.SecurityStore;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
public interface TbSecurityStore extends SecurityStore {
TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint);
}

12
pom.xml

@ -69,9 +69,7 @@
<jackson-core.version>2.12.1</jackson-core.version> <jackson-core.version>2.12.1</jackson-core.version>
<json-schema-validator.version>2.2.6</json-schema-validator.version> <json-schema-validator.version>2.2.6</json-schema-validator.version>
<californium.version>2.6.1</californium.version> <californium.version>2.6.1</californium.version>
<leshan-server.version>1.3.1</leshan-server.version> <leshan.version>2.0.0-M3</leshan.version>
<leshan-core.version>1.3.1</leshan-core.version>
<leshan-client.version>1.3.1</leshan-client.version>
<gson.version>2.6.2</gson.version> <gson.version>2.6.2</gson.version>
<freemarker.version>2.3.30</freemarker.version> <freemarker.version>2.3.30</freemarker.version>
<mail.version>1.6.2</mail.version> <mail.version>1.6.2</mail.version>
@ -1222,22 +1220,22 @@
<dependency> <dependency>
<groupId>org.eclipse.leshan</groupId> <groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-server-cf</artifactId> <artifactId>leshan-server-cf</artifactId>
<version>${leshan-server.version}</version> <version>${leshan.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.leshan</groupId> <groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-client-cf</artifactId> <artifactId>leshan-client-cf</artifactId>
<version>${leshan-client.version}</version> <version>${leshan.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.leshan</groupId> <groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-server-redis</artifactId> <artifactId>leshan-server-redis</artifactId>
<version>${leshan-server.version}</version> <version>${leshan.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.leshan</groupId> <groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-core</artifactId> <artifactId>leshan-core</artifactId>
<version>${leshan-core.version}</version> <version>${leshan.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.californium</groupId> <groupId>org.eclipse.californium</groupId>

Loading…
Cancel
Save