Commit 34aa44d2cb067c645c5b1411637012de3e76379b

Authored by Andrii Shvaika
Committed by Andrew Shvayka
1 parent 6fe0aee7

Added deduplication of transport sessions for CoAP. Fix response codes

Showing 23 changed files with 1005 additions and 426 deletions
@@ -109,7 +109,7 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr @@ -109,7 +109,7 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr
109 protected void validateCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { 109 protected void validateCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
110 assertNotNull(callback.getPayloadBytes()); 110 assertNotNull(callback.getPayloadBytes());
111 assertNotNull(callback.getObserve()); 111 assertNotNull(callback.getObserve());
112 - assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 112 + assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
113 assertEquals(0, callback.getObserve().intValue()); 113 assertEquals(0, callback.getObserve().intValue());
114 String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); 114 String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
115 assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD_ON_CURRENT_STATE_NOTIFICATION), JacksonUtil.toJsonNode(response)); 115 assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD_ON_CURRENT_STATE_NOTIFICATION), JacksonUtil.toJsonNode(response));
@@ -118,7 +118,7 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr @@ -118,7 +118,7 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr
118 protected void validateEmptyCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { 118 protected void validateEmptyCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
119 assertNotNull(callback.getPayloadBytes()); 119 assertNotNull(callback.getPayloadBytes());
120 assertNotNull(callback.getObserve()); 120 assertNotNull(callback.getObserve());
121 - assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 121 + assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
122 assertEquals(0, callback.getObserve().intValue()); 122 assertEquals(0, callback.getObserve().intValue());
123 String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); 123 String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
124 assertEquals("{}", response); 124 assertEquals("{}", response);
@@ -127,7 +127,7 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr @@ -127,7 +127,7 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr
127 protected void validateUpdateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { 127 protected void validateUpdateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
128 assertNotNull(callback.getPayloadBytes()); 128 assertNotNull(callback.getPayloadBytes());
129 assertNotNull(callback.getObserve()); 129 assertNotNull(callback.getObserve());
130 - assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 130 + assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
131 assertEquals(1, callback.getObserve().intValue()); 131 assertEquals(1, callback.getObserve().intValue());
132 String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); 132 String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
133 assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD), JacksonUtil.toJsonNode(response)); 133 assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD), JacksonUtil.toJsonNode(response));
@@ -136,7 +136,7 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr @@ -136,7 +136,7 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr
136 protected void validateDeleteAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { 136 protected void validateDeleteAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
137 assertNotNull(callback.getPayloadBytes()); 137 assertNotNull(callback.getPayloadBytes());
138 assertNotNull(callback.getObserve()); 138 assertNotNull(callback.getObserve());
139 - assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 139 + assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
140 assertEquals(2, callback.getObserve().intValue()); 140 assertEquals(2, callback.getObserve().intValue());
141 String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); 141 String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
142 assertEquals(JacksonUtil.toJsonNode(RESPONSE_ATTRIBUTES_PAYLOAD_DELETED), JacksonUtil.toJsonNode(response)); 142 assertEquals(JacksonUtil.toJsonNode(RESPONSE_ATTRIBUTES_PAYLOAD_DELETED), JacksonUtil.toJsonNode(response));
@@ -62,7 +62,7 @@ public abstract class AbstractCoapAttributesUpdatesProtoIntegrationTest extends @@ -62,7 +62,7 @@ public abstract class AbstractCoapAttributesUpdatesProtoIntegrationTest extends
62 protected void validateCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { 62 protected void validateCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
63 assertNotNull(callback.getPayloadBytes()); 63 assertNotNull(callback.getPayloadBytes());
64 assertNotNull(callback.getObserve()); 64 assertNotNull(callback.getObserve());
65 - assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 65 + assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
66 assertEquals(0, callback.getObserve().intValue()); 66 assertEquals(0, callback.getObserve().intValue());
67 TransportProtos.AttributeUpdateNotificationMsg.Builder expectedCurrentStateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); 67 TransportProtos.AttributeUpdateNotificationMsg.Builder expectedCurrentStateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
68 TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("attribute1", "value", TransportProtos.KeyValueType.STRING_V); 68 TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("attribute1", "value", TransportProtos.KeyValueType.STRING_V);
@@ -90,14 +90,14 @@ public abstract class AbstractCoapAttributesUpdatesProtoIntegrationTest extends @@ -90,14 +90,14 @@ public abstract class AbstractCoapAttributesUpdatesProtoIntegrationTest extends
90 protected void validateEmptyCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { 90 protected void validateEmptyCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
91 assertNull(callback.getPayloadBytes()); 91 assertNull(callback.getPayloadBytes());
92 assertNotNull(callback.getObserve()); 92 assertNotNull(callback.getObserve());
93 - assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 93 + assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
94 assertEquals(0, callback.getObserve().intValue()); 94 assertEquals(0, callback.getObserve().intValue());
95 } 95 }
96 96
97 protected void validateUpdateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { 97 protected void validateUpdateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
98 assertNotNull(callback.getPayloadBytes()); 98 assertNotNull(callback.getPayloadBytes());
99 assertNotNull(callback.getObserve()); 99 assertNotNull(callback.getObserve());
100 - assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 100 + assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
101 assertEquals(1, callback.getObserve().intValue()); 101 assertEquals(1, callback.getObserve().intValue());
102 TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); 102 TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
103 List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); 103 List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList();
@@ -117,7 +117,7 @@ public abstract class AbstractCoapAttributesUpdatesProtoIntegrationTest extends @@ -117,7 +117,7 @@ public abstract class AbstractCoapAttributesUpdatesProtoIntegrationTest extends
117 protected void validateDeleteAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { 117 protected void validateDeleteAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
118 assertNotNull(callback.getPayloadBytes()); 118 assertNotNull(callback.getPayloadBytes());
119 assertNotNull(callback.getObserve()); 119 assertNotNull(callback.getObserve());
120 - assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 120 + assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
121 assertEquals(2, callback.getObserve().intValue()); 121 assertEquals(2, callback.getObserve().intValue());
122 TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); 122 TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
123 attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5"); 123 attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5");
@@ -196,7 +196,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC @@ -196,7 +196,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
196 assertTrue(StringUtils.isEmpty(result)); 196 assertTrue(StringUtils.isEmpty(result));
197 assertNotNull(callback.getPayloadBytes()); 197 assertNotNull(callback.getPayloadBytes());
198 assertNotNull(callback.getObserve()); 198 assertNotNull(callback.getObserve());
199 - assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 199 + assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
200 assertEquals(1, callback.getObserve().intValue()); 200 assertEquals(1, callback.getObserve().intValue());
201 } 201 }
202 202
@@ -204,7 +204,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC @@ -204,7 +204,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
204 assertEquals(expectedResult, actualResult); 204 assertEquals(expectedResult, actualResult);
205 assertNotNull(callback.getPayloadBytes()); 205 assertNotNull(callback.getPayloadBytes());
206 assertNotNull(callback.getObserve()); 206 assertNotNull(callback.getObserve());
207 - assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 207 + assertEquals(CoAP.ResponseCode.CONTENT, callback.getResponseCode());
208 assertEquals(expectedObserveNumber, callback.getObserve().intValue()); 208 assertEquals(expectedObserveNumber, callback.getObserve().intValue());
209 } 209 }
210 210
@@ -111,8 +111,7 @@ public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVeri @@ -111,8 +111,7 @@ public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVeri
111 if (msg != null && strCert.equals(msg.getCredentials())) { 111 if (msg != null && strCert.equals(msg.getCredentials())) {
112 DeviceProfile deviceProfile = msg.getDeviceProfile(); 112 DeviceProfile deviceProfile = msg.getDeviceProfile();
113 if (msg.hasDeviceInfo() && deviceProfile != null) { 113 if (msg.hasDeviceInfo() && deviceProfile != null) {
114 - TransportProtos.SessionInfoProto sessionInfoProto = SessionInfoCreator.create(msg, serviceInfoProvider.getServiceId(), UUID.randomUUID());  
115 - tbCoapDtlsSessionInMemoryStorage.put(session.getSessionIdentifier().toString(), new TbCoapDtlsSessionInfo(sessionInfoProto, deviceProfile)); 114 + tbCoapDtlsSessionInMemoryStorage.put(session.getSessionIdentifier().toString(), new TbCoapDtlsSessionInfo(msg, deviceProfile));
116 } 115 }
117 break; 116 break;
118 } 117 }
@@ -17,18 +17,19 @@ package org.thingsboard.server.coapserver; @@ -17,18 +17,19 @@ package org.thingsboard.server.coapserver;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 import org.thingsboard.server.common.data.DeviceProfile; 19 import org.thingsboard.server.common.data.DeviceProfile;
  20 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
20 import org.thingsboard.server.gen.transport.TransportProtos; 21 import org.thingsboard.server.gen.transport.TransportProtos;
21 22
22 @Data 23 @Data
23 public class TbCoapDtlsSessionInfo { 24 public class TbCoapDtlsSessionInfo {
24 25
25 - private TransportProtos.SessionInfoProto sessionInfoProto; 26 + private ValidateDeviceCredentialsResponse msg;
26 private DeviceProfile deviceProfile; 27 private DeviceProfile deviceProfile;
27 private long lastActivityTime; 28 private long lastActivityTime;
28 29
29 30
30 - public TbCoapDtlsSessionInfo(TransportProtos.SessionInfoProto sessionInfoProto, DeviceProfile deviceProfile) {  
31 - this.sessionInfoProto = sessionInfoProto; 31 + public TbCoapDtlsSessionInfo(ValidateDeviceCredentialsResponse msg, DeviceProfile deviceProfile) {
  32 + this.msg = msg;
32 this.deviceProfile = deviceProfile; 33 this.deviceProfile = deviceProfile;
33 this.lastActivityTime = System.currentTimeMillis(); 34 this.lastActivityTime = System.currentTimeMillis();
34 } 35 }
@@ -72,18 +72,4 @@ public abstract class AbstractCoapTransportResource extends CoapResource { @@ -72,18 +72,4 @@ public abstract class AbstractCoapTransportResource extends CoapResource {
72 .build(), TransportServiceCallback.EMPTY); 72 .build(), TransportServiceCallback.EMPTY);
73 } 73 }
74 74
75 - protected void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {  
76 - transportService.reportActivity(sessionInfo);  
77 - }  
78 -  
79 - protected static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) {  
80 - return TransportProtos.SessionEventMsg.newBuilder()  
81 - .setSessionType(TransportProtos.SessionType.ASYNC)  
82 - .setEvent(event).build();  
83 - }  
84 -  
85 - protected int getNextMsgId() {  
86 - return ThreadLocalRandom.current().nextInt(NONE, MAX_MID + 1);  
87 - }  
88 -  
89 } 75 }
@@ -25,6 +25,7 @@ import org.thingsboard.server.common.transport.TransportContext; @@ -25,6 +25,7 @@ import org.thingsboard.server.common.transport.TransportContext;
25 import org.thingsboard.server.gen.transport.TransportProtos; 25 import org.thingsboard.server.gen.transport.TransportProtos;
26 import org.thingsboard.server.transport.coap.adaptors.JsonCoapAdaptor; 26 import org.thingsboard.server.transport.coap.adaptors.JsonCoapAdaptor;
27 import org.thingsboard.server.transport.coap.adaptors.ProtoCoapAdaptor; 27 import org.thingsboard.server.transport.coap.adaptors.ProtoCoapAdaptor;
  28 +import org.thingsboard.server.transport.coap.client.CoapClientContext;
28 import org.thingsboard.server.transport.coap.efento.adaptor.EfentoCoapAdaptor; 29 import org.thingsboard.server.transport.coap.efento.adaptor.EfentoCoapAdaptor;
29 30
30 import java.util.concurrent.ConcurrentHashMap; 31 import java.util.concurrent.ConcurrentHashMap;
@@ -52,6 +53,9 @@ public class CoapTransportContext extends TransportContext { @@ -52,6 +53,9 @@ public class CoapTransportContext extends TransportContext {
52 @Autowired 53 @Autowired
53 private EfentoCoapAdaptor efentoCoapAdaptor; 54 private EfentoCoapAdaptor efentoCoapAdaptor;
54 55
  56 + @Autowired
  57 + private CoapClientContext clientContext;
  58 +
55 private final ConcurrentMap<Integer, TransportProtos.ToDeviceRpcRequestMsg> rpcAwaitingAck = new ConcurrentHashMap<>(); 59 private final ConcurrentMap<Integer, TransportProtos.ToDeviceRpcRequestMsg> rpcAwaitingAck = new ConcurrentHashMap<>();
56 60
57 } 61 }
@@ -16,10 +16,6 @@ @@ -16,10 +16,6 @@
16 package org.thingsboard.server.transport.coap; 16 package org.thingsboard.server.transport.coap;
17 17
18 import com.google.gson.JsonParseException; 18 import com.google.gson.JsonParseException;
19 -import com.google.protobuf.Descriptors;  
20 -import com.google.protobuf.DynamicMessage;  
21 -import lombok.Data;  
22 -import lombok.RequiredArgsConstructor;  
23 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
24 import org.eclipse.californium.core.coap.CoAP; 20 import org.eclipse.californium.core.coap.CoAP;
25 import org.eclipse.californium.core.coap.Request; 21 import org.eclipse.californium.core.coap.Request;
@@ -29,7 +25,6 @@ import org.eclipse.californium.core.observe.ObserveRelation; @@ -29,7 +25,6 @@ import org.eclipse.californium.core.observe.ObserveRelation;
29 import org.eclipse.californium.core.server.resources.CoapExchange; 25 import org.eclipse.californium.core.server.resources.CoapExchange;
30 import org.eclipse.californium.core.server.resources.Resource; 26 import org.eclipse.californium.core.server.resources.Resource;
31 import org.eclipse.californium.core.server.resources.ResourceObserver; 27 import org.eclipse.californium.core.server.resources.ResourceObserver;
32 -import org.springframework.util.CollectionUtils;  
33 import org.thingsboard.server.coapserver.CoapServerService; 28 import org.thingsboard.server.coapserver.CoapServerService;
34 import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo; 29 import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo;
35 import org.thingsboard.server.common.data.DataConstants; 30 import org.thingsboard.server.common.data.DataConstants;
@@ -37,38 +32,30 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -37,38 +32,30 @@ import org.thingsboard.server.common.data.DeviceProfile;
37 import org.thingsboard.server.common.data.DeviceTransportType; 32 import org.thingsboard.server.common.data.DeviceTransportType;
38 import org.thingsboard.server.common.data.StringUtils; 33 import org.thingsboard.server.common.data.StringUtils;
39 import org.thingsboard.server.common.data.TransportPayloadType; 34 import org.thingsboard.server.common.data.TransportPayloadType;
40 -import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration;  
41 -import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration;  
42 -import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration;  
43 -import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;  
44 -import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;  
45 -import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;  
46 -import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;  
47 -import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;  
48 import org.thingsboard.server.common.data.security.DeviceTokenCredentials; 35 import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
49 import org.thingsboard.server.common.msg.session.FeatureType; 36 import org.thingsboard.server.common.msg.session.FeatureType;
50 import org.thingsboard.server.common.msg.session.SessionMsgType; 37 import org.thingsboard.server.common.msg.session.SessionMsgType;
51 -import org.thingsboard.server.common.transport.SessionMsgListener;  
52 import org.thingsboard.server.common.transport.TransportServiceCallback; 38 import org.thingsboard.server.common.transport.TransportServiceCallback;
53 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 39 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
54 import org.thingsboard.server.common.transport.adaptor.JsonConverter; 40 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
  41 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
55 import org.thingsboard.server.gen.transport.TransportProtos; 42 import org.thingsboard.server.gen.transport.TransportProtos;
56 -import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;  
57 import org.thingsboard.server.transport.coap.callback.CoapDeviceAuthCallback; 43 import org.thingsboard.server.transport.coap.callback.CoapDeviceAuthCallback;
58 import org.thingsboard.server.transport.coap.callback.CoapNoOpCallback; 44 import org.thingsboard.server.transport.coap.callback.CoapNoOpCallback;
59 import org.thingsboard.server.transport.coap.callback.CoapOkCallback; 45 import org.thingsboard.server.transport.coap.callback.CoapOkCallback;
  46 +import org.thingsboard.server.transport.coap.callback.GetAttributesSyncSessionCallback;
  47 +import org.thingsboard.server.transport.coap.callback.ToServerRpcSyncSessionCallback;
  48 +import org.thingsboard.server.transport.coap.client.CoapClientContext;
  49 +import org.thingsboard.server.transport.coap.client.TbCoapClientState;
60 50
61 import java.util.List; 51 import java.util.List;
62 -import java.util.Map;  
63 import java.util.Optional; 52 import java.util.Optional;
64 import java.util.Random; 53 import java.util.Random;
65 -import java.util.Set;  
66 import java.util.UUID; 54 import java.util.UUID;
67 import java.util.concurrent.ConcurrentHashMap; 55 import java.util.concurrent.ConcurrentHashMap;
68 import java.util.concurrent.ConcurrentMap; 56 import java.util.concurrent.ConcurrentMap;
69 import java.util.concurrent.TimeUnit; 57 import java.util.concurrent.TimeUnit;
70 import java.util.concurrent.atomic.AtomicInteger; 58 import java.util.concurrent.atomic.AtomicInteger;
71 -import java.util.stream.Collectors;  
72 59
73 @Slf4j 60 @Slf4j
74 public class CoapTransportResource extends AbstractCoapTransportResource { 61 public class CoapTransportResource extends AbstractCoapTransportResource {
@@ -80,13 +67,11 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -80,13 +67,11 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
80 private static final int REQUEST_ID_POSITION_CERTIFICATE_REQUEST = 4; 67 private static final int REQUEST_ID_POSITION_CERTIFICATE_REQUEST = 4;
81 private static final String DTLS_SESSION_ID_KEY = "DTLS_SESSION_ID"; 68 private static final String DTLS_SESSION_ID_KEY = "DTLS_SESSION_ID";
82 69
83 - private final ConcurrentMap<String, CoapObserveSessionInfo> tokenToCoapSessionInfoMap = new ConcurrentHashMap<>();  
84 - private final ConcurrentMap<CoapObserveSessionInfo, ObserveRelation> sessionInfoToObserveRelationMap = new ConcurrentHashMap<>();  
85 - private final Set<UUID> rpcSubscriptions = ConcurrentHashMap.newKeySet();  
86 - private final Set<UUID> attributeSubscriptions = ConcurrentHashMap.newKeySet(); 70 + private final ConcurrentMap<TbCoapClientState, ObserveRelation> sessionInfoToObserveRelationMap = new ConcurrentHashMap<>();
87 71
88 private final ConcurrentMap<String, TbCoapDtlsSessionInfo> dtlsSessionIdMap; 72 private final ConcurrentMap<String, TbCoapDtlsSessionInfo> dtlsSessionIdMap;
89 private final long timeout; 73 private final long timeout;
  74 + private final CoapClientContext clients;
90 75
91 public CoapTransportResource(CoapTransportContext ctx, CoapServerService coapServerService, String name) { 76 public CoapTransportResource(CoapTransportContext ctx, CoapServerService coapServerService, String name) {
92 super(ctx, name); 77 super(ctx, name);
@@ -94,17 +79,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -94,17 +79,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
94 this.addObserver(new CoapResourceObserver()); 79 this.addObserver(new CoapResourceObserver());
95 this.dtlsSessionIdMap = coapServerService.getDtlsSessionsMap(); 80 this.dtlsSessionIdMap = coapServerService.getDtlsSessionsMap();
96 this.timeout = coapServerService.getTimeout(); 81 this.timeout = coapServerService.getTimeout();
  82 + this.clients = ctx.getClientContext();
97 long sessionReportTimeout = ctx.getSessionReportTimeout(); 83 long sessionReportTimeout = ctx.getSessionReportTimeout();
98 - ctx.getScheduler().scheduleAtFixedRate(() -> {  
99 - Set<CoapObserveSessionInfo> coapObserveSessionInfos = sessionInfoToObserveRelationMap.keySet();  
100 - Set<TransportProtos.SessionInfoProto> observeSessions = coapObserveSessionInfos  
101 - .stream()  
102 - .map(CoapObserveSessionInfo::getSessionInfoProto)  
103 - .collect(Collectors.toSet());  
104 - observeSessions.forEach(this::reportActivity);  
105 - }, new Random().nextInt((int) sessionReportTimeout), sessionReportTimeout, TimeUnit.MILLISECONDS); 84 + ctx.getScheduler().scheduleAtFixedRate(clients::reportActivity, new Random().nextInt((int) sessionReportTimeout), sessionReportTimeout, TimeUnit.MILLISECONDS);
106 } 85 }
107 86
  87 + /*
  88 + * Overwritten method from CoapResource to be able to manage our own observe notification counters.
  89 + */
108 @Override 90 @Override
109 public void checkObserveRelation(Exchange exchange, Response response) { 91 public void checkObserveRelation(Exchange exchange, Response response) {
110 String token = getTokenFromRequest(exchange.getRequest()); 92 String token = getTokenFromRequest(exchange.getRequest());
@@ -117,20 +99,15 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -117,20 +99,15 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
117 relation.setEstablished(); 99 relation.setEstablished();
118 addObserveRelation(relation); 100 addObserveRelation(relation);
119 } 101 }
120 - AtomicInteger observeNotificationCounter = tokenToCoapSessionInfoMap.get(token).getObserveNotificationCounter();  
121 - response.getOptions().setObserve(observeNotificationCounter.getAndIncrement()); 102 + AtomicInteger state = clients.getNotificationCounterByToken(token);
  103 + if (state != null) {
  104 + response.getOptions().setObserve(state.getAndIncrement());
  105 + } else {
  106 + response.getOptions().removeObserve();
  107 + }
122 } // ObserveLayer takes care of the else case 108 } // ObserveLayer takes care of the else case
123 } 109 }
124 110
125 - private void clearAndNotifyObserveRelation(ObserveRelation relation, CoAP.ResponseCode code) {  
126 - relation.cancel();  
127 - relation.getExchange().sendResponse(new Response(code));  
128 - }  
129 -  
130 - private Map<CoapObserveSessionInfo, ObserveRelation> getCoapSessionInfoToObserveRelationMap() {  
131 - return sessionInfoToObserveRelationMap;  
132 - }  
133 -  
134 @Override 111 @Override
135 protected void processHandleGet(CoapExchange exchange) { 112 protected void processHandleGet(CoapExchange exchange) {
136 Optional<FeatureType> featureType = getFeatureType(exchange.advanced().getRequest()); 113 Optional<FeatureType> featureType = getFeatureType(exchange.advanced().getRequest());
@@ -232,7 +209,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -232,7 +209,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
232 return dtlsSessionInfo; 209 return dtlsSessionInfo;
233 }); 210 });
234 if (tbCoapDtlsSessionInfo != null) { 211 if (tbCoapDtlsSessionInfo != null) {
235 - processRequest(exchange, type, request, tbCoapDtlsSessionInfo.getSessionInfoProto(), tbCoapDtlsSessionInfo.getDeviceProfile()); 212 + processRequest(exchange, type, request, tbCoapDtlsSessionInfo.getMsg(), tbCoapDtlsSessionInfo.getDeviceProfile());
236 } else { 213 } else {
237 processAccessTokenRequest(exchange, type, request); 214 processAccessTokenRequest(exchange, type, request);
238 } 215 }
@@ -248,129 +225,133 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -248,129 +225,133 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
248 return; 225 return;
249 } 226 }
250 transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(), 227 transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(),
251 - new CoapDeviceAuthCallback(transportContext, exchange, (sessionInfo, deviceProfile) -> {  
252 - processRequest(exchange, type, request, sessionInfo, deviceProfile); 228 + new CoapDeviceAuthCallback(exchange, (deviceCredentials, deviceProfile) -> {
  229 + processRequest(exchange, type, request, deviceCredentials, deviceProfile);
253 })); 230 }));
254 } 231 }
255 232
256 - private void processRequest(CoapExchange exchange, SessionMsgType type, Request request, TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {  
257 - UUID sessionId = toSessionId(sessionInfo); 233 + private void processRequest(CoapExchange exchange, SessionMsgType type, Request request, ValidateDeviceCredentialsResponse deviceCredentials, DeviceProfile deviceProfile) {
  234 + TbCoapClientState clientState = null;
258 try { 235 try {
259 - TransportConfigurationContainer transportConfigurationContainer = getTransportConfigurationContainer(deviceProfile);  
260 - CoapTransportAdaptor coapTransportAdaptor = getCoapTransportAdaptor(transportConfigurationContainer.isJsonPayload()); 236 + clientState = clients.getOrCreateClient(type, deviceCredentials, deviceProfile);
  237 + clientState.updateLastUplinkTime();
261 switch (type) { 238 switch (type) {
262 case POST_ATTRIBUTES_REQUEST: 239 case POST_ATTRIBUTES_REQUEST:
263 - transportService.process(sessionInfo,  
264 - coapTransportAdaptor.convertToPostAttributes(sessionId, request,  
265 - transportConfigurationContainer.getAttributesMsgDescriptor()),  
266 - new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));  
267 - reportSubscriptionInfo(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId)); 240 + handlePostAttributesRequest(clientState, exchange, request);
268 break; 241 break;
269 case POST_TELEMETRY_REQUEST: 242 case POST_TELEMETRY_REQUEST:
270 - transportService.process(sessionInfo,  
271 - coapTransportAdaptor.convertToPostTelemetry(sessionId, request,  
272 - transportConfigurationContainer.getTelemetryMsgDescriptor()),  
273 - new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));  
274 - reportSubscriptionInfo(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId)); 243 + handlePostTelemetryRequest(clientState, exchange, request);
275 break; 244 break;
276 case CLAIM_REQUEST: 245 case CLAIM_REQUEST:
277 - transportService.process(sessionInfo,  
278 - coapTransportAdaptor.convertToClaimDevice(sessionId, request, sessionInfo),  
279 - new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); 246 + handleClaimRequest(clientState, exchange, request);
280 break; 247 break;
281 case SUBSCRIBE_ATTRIBUTES_REQUEST: 248 case SUBSCRIBE_ATTRIBUTES_REQUEST:
282 - CoapObserveSessionInfo currentCoapObserveAttrSessionInfo = tokenToCoapSessionInfoMap.get(getTokenFromRequest(request));  
283 - if (currentCoapObserveAttrSessionInfo == null) {  
284 - attributeSubscriptions.add(sessionId);  
285 - registerAsyncCoapSession(exchange, coapTransportAdaptor, transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(),  
286 - sessionInfo, getTokenFromRequest(request));  
287 - transportService.process(sessionInfo,  
288 - TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), new CoapNoOpCallback(exchange));  
289 - transportService.process(sessionInfo,  
290 - TransportProtos.GetAttributeRequestMsg.newBuilder().setOnlyShared(true).build(),  
291 - new CoapNoOpCallback(exchange));  
292 - } 249 + handleAttributeSubscribeRequest(clientState, exchange, request);
293 break; 250 break;
294 case UNSUBSCRIBE_ATTRIBUTES_REQUEST: 251 case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
295 - CoapObserveSessionInfo coapObserveAttrSessionInfo = lookupAsyncSessionInfo(getTokenFromRequest(request));  
296 - if (coapObserveAttrSessionInfo != null) {  
297 - TransportProtos.SessionInfoProto attrSession = coapObserveAttrSessionInfo.getSessionInfoProto();  
298 - UUID attrSessionId = toSessionId(attrSession);  
299 - attributeSubscriptions.remove(attrSessionId);  
300 - transportService.process(attrSession,  
301 - TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(),  
302 - new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));  
303 - }  
304 - closeAndDeregister(sessionInfo); 252 + handleAttributeUnsubscribeRequest(clientState, exchange, request);
305 break; 253 break;
306 case SUBSCRIBE_RPC_COMMANDS_REQUEST: 254 case SUBSCRIBE_RPC_COMMANDS_REQUEST:
307 - CoapObserveSessionInfo currentCoapObserveRpcSessionInfo = tokenToCoapSessionInfoMap.get(getTokenFromRequest(request));  
308 - if (currentCoapObserveRpcSessionInfo == null) {  
309 - rpcSubscriptions.add(sessionId);  
310 - registerAsyncCoapSession(exchange, coapTransportAdaptor, transportConfigurationContainer.getRpcRequestDynamicMessageBuilder()  
311 - , sessionInfo, getTokenFromRequest(request));  
312 - transportService.process(sessionInfo,  
313 - TransportProtos.SubscribeToRPCMsg.getDefaultInstance(),  
314 - new CoapOkCallback(exchange, CoAP.ResponseCode.VALID, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)  
315 - );  
316 - } 255 + handleRpcSubscribeRequest(clientState, exchange, request);
317 break; 256 break;
318 case UNSUBSCRIBE_RPC_COMMANDS_REQUEST: 257 case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
319 - CoapObserveSessionInfo coapObserveRpcSessionInfo = lookupAsyncSessionInfo(getTokenFromRequest(request));  
320 - if (coapObserveRpcSessionInfo != null) {  
321 - TransportProtos.SessionInfoProto rpcSession = coapObserveRpcSessionInfo.getSessionInfoProto();  
322 - UUID rpcSessionId = toSessionId(rpcSession);  
323 - rpcSubscriptions.remove(rpcSessionId);  
324 - transportService.process(rpcSession,  
325 - TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(),  
326 - new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));  
327 - }  
328 - closeAndDeregister(sessionInfo); 258 + handleRpcUnsubscribeRequest(clientState, exchange, request);
329 break; 259 break;
330 case TO_DEVICE_RPC_RESPONSE: 260 case TO_DEVICE_RPC_RESPONSE:
331 - transportService.process(sessionInfo,  
332 - coapTransportAdaptor.convertToDeviceRpcResponse(sessionId, request, transportConfigurationContainer.getRpcResponseMsgDescriptor()),  
333 - new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); 261 + handleToDeviceRpcResponse(clientState, exchange, request);
334 break; 262 break;
335 case TO_SERVER_RPC_REQUEST: 263 case TO_SERVER_RPC_REQUEST:
336 - transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor,  
337 - transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), sessionInfo), timeout);  
338 - transportService.process(sessionInfo,  
339 - coapTransportAdaptor.convertToServerRpcRequest(sessionId, request),  
340 - new CoapNoOpCallback(exchange)); 264 + handleToServerRpcRequest(clientState, exchange, request);
341 break; 265 break;
342 case GET_ATTRIBUTES_REQUEST: 266 case GET_ATTRIBUTES_REQUEST:
343 - transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor,  
344 - transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), sessionInfo), timeout);  
345 - transportService.process(sessionInfo,  
346 - coapTransportAdaptor.convertToGetAttributes(sessionId, request),  
347 - new CoapNoOpCallback(exchange)); 267 + handleGetAttributesRequest(clientState, exchange, request);
348 break; 268 break;
349 } 269 }
350 } catch (AdaptorException e) { 270 } catch (AdaptorException e) {
351 - log.trace("[{}] Failed to decode message: ", sessionId, e); 271 + if (clientState != null) {
  272 + log.trace("[{}] Failed to decode message: ", clientState.getDeviceId(), e);
  273 + }
352 exchange.respond(CoAP.ResponseCode.BAD_REQUEST); 274 exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
353 } 275 }
354 } 276 }
355 277
356 - private UUID toSessionId(TransportProtos.SessionInfoProto sessionInfoProto) {  
357 - return new UUID(sessionInfoProto.getSessionIdMSB(), sessionInfoProto.getSessionIdLSB()); 278 + private void handlePostAttributesRequest(TbCoapClientState clientState, CoapExchange exchange, Request request) throws AdaptorException {
  279 + TransportProtos.SessionInfoProto sessionInfo = clients.getNewSyncSession(clientState);
  280 + UUID sessionId = toSessionId(sessionInfo);
  281 + transportService.process(sessionInfo, clientState.getAdaptor().convertToPostAttributes(sessionId, request,
  282 + clientState.getConfiguration().getAttributesMsgDescriptor()),
  283 + new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
  284 + }
  285 +
  286 + private void handlePostTelemetryRequest(TbCoapClientState clientState, CoapExchange exchange, Request request) throws AdaptorException {
  287 + TransportProtos.SessionInfoProto sessionInfo = clients.getNewSyncSession(clientState);
  288 + UUID sessionId = toSessionId(sessionInfo);
  289 + transportService.process(sessionInfo, clientState.getAdaptor().convertToPostTelemetry(sessionId, request,
  290 + clientState.getConfiguration().getTelemetryMsgDescriptor()),
  291 + new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
  292 + }
  293 +
  294 + private void handleClaimRequest(TbCoapClientState clientState, CoapExchange exchange, Request request) throws AdaptorException {
  295 + TransportProtos.SessionInfoProto sessionInfo = clients.getNewSyncSession(clientState);
  296 + UUID sessionId = toSessionId(sessionInfo);
  297 + transportService.process(sessionInfo,
  298 + clientState.getAdaptor().convertToClaimDevice(sessionId, request, sessionInfo),
  299 + new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
  300 + }
  301 +
  302 + private void handleAttributeSubscribeRequest(TbCoapClientState clientState, CoapExchange exchange, Request request) {
  303 + String attrSubToken = getTokenFromRequest(request);
  304 + if (!clients.registerAttributeObservation(clientState, attrSubToken, exchange)) {
  305 + log.warn("[{}] Received duplicate attribute subscribe request for token: {}", clientState.getDeviceId(), attrSubToken);
  306 + }
  307 + }
  308 +
  309 + private void handleAttributeUnsubscribeRequest(TbCoapClientState clientState, CoapExchange exchange, Request request) {
  310 + clients.deregisterAttributeObservation(clientState, getTokenFromRequest(request), exchange);
358 } 311 }
359 312
360 - private CoapObserveSessionInfo lookupAsyncSessionInfo(String token) {  
361 - return tokenToCoapSessionInfoMap.remove(token); 313 + private void handleRpcUnsubscribeRequest(TbCoapClientState clientState, CoapExchange exchange, Request request) {
  314 + clients.deregisterRpcObservation(clientState, getTokenFromRequest(request), exchange);
362 } 315 }
363 316
364 - private void registerAsyncCoapSession(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor,  
365 - DynamicMessage.Builder rpcRequestDynamicMessageBuilder, TransportProtos.SessionInfoProto sessionInfo, String token) {  
366 - tokenToCoapSessionInfoMap.putIfAbsent(token, new CoapObserveSessionInfo(sessionInfo));  
367 - transportService.registerAsyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder, sessionInfo));  
368 - transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); 317 + private void handleToDeviceRpcResponse(TbCoapClientState clientState, CoapExchange exchange, Request request) throws AdaptorException {
  318 + TransportProtos.SessionInfoProto session = clientState.getSession();
  319 + if (session == null) {
  320 + session = clients.getNewSyncSession(clientState);
  321 + }
  322 + UUID sessionId = toSessionId(session);
  323 + transportService.process(session,
  324 + clientState.getAdaptor().convertToDeviceRpcResponse(sessionId, request, clientState.getConfiguration().getRpcResponseMsgDescriptor()),
  325 + new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
369 } 326 }
370 327
371 - private CoapSessionListener getCoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor,  
372 - DynamicMessage.Builder rpcRequestDynamicMessageBuilder, TransportProtos.SessionInfoProto sessionInfo) {  
373 - return new CoapSessionListener(exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder, sessionInfo); 328 + private void handleRpcSubscribeRequest(TbCoapClientState clientState, CoapExchange exchange, Request request) {
  329 + String rpcSubToken = getTokenFromRequest(request);
  330 + if (!clients.registerRpcObservation(clientState, rpcSubToken, exchange)) {
  331 + log.warn("[{}] Received duplicate rpc subscribe request.", rpcSubToken);
  332 + }
  333 + }
  334 +
  335 + private void handleGetAttributesRequest(TbCoapClientState clientState, CoapExchange exchange, Request request) throws AdaptorException {
  336 + TransportProtos.SessionInfoProto sessionInfo = clients.getNewSyncSession(clientState);
  337 + UUID sessionId = toSessionId(sessionInfo);
  338 + transportService.registerSyncSession(sessionInfo, new GetAttributesSyncSessionCallback(clientState, exchange, request), timeout);
  339 + transportService.process(sessionInfo,
  340 + clientState.getAdaptor().convertToGetAttributes(sessionId, request),
  341 + new CoapNoOpCallback(exchange));
  342 + }
  343 +
  344 + private void handleToServerRpcRequest(TbCoapClientState clientState, CoapExchange exchange, Request request) throws AdaptorException {
  345 + TransportProtos.SessionInfoProto sessionInfo = clients.getNewSyncSession(clientState);
  346 + UUID sessionId = toSessionId(sessionInfo);
  347 + transportService.registerSyncSession(sessionInfo, new ToServerRpcSyncSessionCallback(clientState, exchange, request), timeout);
  348 + transportService.process(sessionInfo,
  349 + clientState.getAdaptor().convertToServerRpcRequest(sessionId, request),
  350 + new CoapNoOpCallback(exchange));
  351 + }
  352 +
  353 + private UUID toSessionId(TransportProtos.SessionInfoProto sessionInfoProto) {
  354 + return new UUID(sessionInfoProto.getSessionIdMSB(), sessionInfoProto.getSessionIdLSB());
374 } 355 }
375 356
376 private String getTokenFromRequest(Request request) { 357 private String getTokenFromRequest(Request request) {
@@ -452,119 +433,6 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -452,119 +433,6 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
452 } 433 }
453 } 434 }
454 435
455 - @RequiredArgsConstructor  
456 - private class CoapSessionListener implements SessionMsgListener {  
457 -  
458 - private final CoapExchange exchange;  
459 - private final CoapTransportAdaptor coapTransportAdaptor;  
460 - private final DynamicMessage.Builder rpcRequestDynamicMessageBuilder;  
461 - private final TransportProtos.SessionInfoProto sessionInfo;  
462 -  
463 - @Override  
464 - public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg msg) {  
465 - try {  
466 - exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg));  
467 - } catch (AdaptorException e) {  
468 - log.trace("Failed to reply due to error", e);  
469 - exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);  
470 - }  
471 - }  
472 -  
473 - @Override  
474 - public void onAttributeUpdate(UUID sessionId, TransportProtos.AttributeUpdateNotificationMsg msg) {  
475 - log.trace("[{}] Received attributes update notification to device", sessionId);  
476 - try {  
477 - exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg));  
478 - } catch (AdaptorException e) {  
479 - log.trace("Failed to reply due to error", e);  
480 - closeObserveRelationAndNotify(sessionId, CoAP.ResponseCode.INTERNAL_SERVER_ERROR);  
481 - closeAndDeregister();  
482 - }  
483 - }  
484 -  
485 - @Override  
486 - public void onRemoteSessionCloseCommand(UUID sessionId, TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {  
487 - log.trace("[{}] Received the remote command to close the session: {}", sessionId, sessionCloseNotification.getMessage());  
488 - closeObserveRelationAndNotify(sessionId, CoAP.ResponseCode.SERVICE_UNAVAILABLE);  
489 - closeAndDeregister();  
490 - }  
491 -  
492 - @Override  
493 - public void onToDeviceRpcRequest(UUID sessionId, TransportProtos.ToDeviceRpcRequestMsg msg) {  
494 - log.trace("[{}] Received RPC command to device", sessionId);  
495 - boolean sent = false;  
496 - try {  
497 - Response response = coapTransportAdaptor.convertToPublish(isConRequest(), msg, rpcRequestDynamicMessageBuilder);  
498 - int requestId = getNextMsgId();  
499 - response.setMID(requestId);  
500 -  
501 - if (msg.getPersisted() && isConRequest()) {  
502 - transportContext.getRpcAwaitingAck().put(requestId, msg);  
503 - transportContext.getScheduler().schedule(() -> {  
504 - TransportProtos.ToDeviceRpcRequestMsg awaitingAckMsg = transportContext.getRpcAwaitingAck().remove(requestId);  
505 - if (awaitingAckMsg != null) {  
506 - transportService.process(sessionInfo, msg, true, TransportServiceCallback.EMPTY);  
507 - }  
508 - }, Math.max(0, msg.getExpirationTime() - System.currentTimeMillis()), TimeUnit.MILLISECONDS);  
509 - response.addMessageObserver(new TbCoapMessageObserver(requestId, id -> {  
510 - TransportProtos.ToDeviceRpcRequestMsg rpcRequestMsg = transportContext.getRpcAwaitingAck().remove(id);  
511 - if (rpcRequestMsg != null) {  
512 - transportService.process(sessionInfo, rpcRequestMsg, false, TransportServiceCallback.EMPTY);  
513 - }  
514 - }));  
515 - }  
516 - exchange.respond(response);  
517 - sent = true;  
518 - } catch (AdaptorException e) {  
519 - log.trace("Failed to reply due to error", e);  
520 - closeObserveRelationAndNotify(sessionId, CoAP.ResponseCode.INTERNAL_SERVER_ERROR);  
521 - closeAndDeregister();  
522 - } finally {  
523 - if (msg.getPersisted() && !isConRequest()) {  
524 - transportService.process(sessionInfo, msg, sent, TransportServiceCallback.EMPTY);  
525 - }  
526 - }  
527 - }  
528 -  
529 - @Override  
530 - public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg msg) {  
531 - try {  
532 - exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg));  
533 - } catch (AdaptorException e) {  
534 - log.trace("Failed to reply due to error", e);  
535 - exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);  
536 - }  
537 - }  
538 -  
539 - private boolean isConRequest() {  
540 - return exchange.advanced().getRequest().isConfirmable();  
541 - }  
542 -  
543 - private void closeObserveRelationAndNotify(UUID sessionId, CoAP.ResponseCode responseCode) {  
544 - Map<CoapObserveSessionInfo, ObserveRelation> sessionToObserveRelationMap = CoapTransportResource.this.getCoapSessionInfoToObserveRelationMap();  
545 - if (CoapTransportResource.this.getObserverCount() > 0 && !CollectionUtils.isEmpty(sessionToObserveRelationMap)) {  
546 - Optional<CoapObserveSessionInfo> observeSessionToClose = sessionToObserveRelationMap.keySet().stream().filter(coapObserveSessionInfo -> {  
547 - TransportProtos.SessionInfoProto sessionToDelete = coapObserveSessionInfo.getSessionInfoProto();  
548 - UUID observeSessionId = new UUID(sessionToDelete.getSessionIdMSB(), sessionToDelete.getSessionIdLSB());  
549 - return observeSessionId.equals(sessionId);  
550 - }).findFirst();  
551 - if (observeSessionToClose.isPresent()) {  
552 - CoapObserveSessionInfo coapObserveSessionInfo = observeSessionToClose.get();  
553 - ObserveRelation observeRelation = sessionToObserveRelationMap.get(coapObserveSessionInfo);  
554 - CoapTransportResource.this.clearAndNotifyObserveRelation(observeRelation, responseCode);  
555 - }  
556 - }  
557 - }  
558 -  
559 - private void closeAndDeregister() {  
560 - Request request = exchange.advanced().getRequest();  
561 - String token = CoapTransportResource.this.getTokenFromRequest(request);  
562 - CoapObserveSessionInfo deleted = CoapTransportResource.this.lookupAsyncSessionInfo(token);  
563 - CoapTransportResource.this.closeAndDeregister(deleted.getSessionInfoProto());  
564 - }  
565 -  
566 - }  
567 -  
568 public class CoapResourceObserver implements ResourceObserver { 436 public class CoapResourceObserver implements ResourceObserver {
569 437
570 @Override 438 @Override
@@ -587,7 +455,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -587,7 +455,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
587 public void addedObserveRelation(ObserveRelation relation) { 455 public void addedObserveRelation(ObserveRelation relation) {
588 Request request = relation.getExchange().getRequest(); 456 Request request = relation.getExchange().getRequest();
589 String token = getTokenFromRequest(request); 457 String token = getTokenFromRequest(request);
590 - sessionInfoToObserveRelationMap.putIfAbsent(tokenToCoapSessionInfoMap.get(token), relation); 458 + clients.registerObserveRelation(token, relation);
591 log.trace("Added Observe relation for token: {}", token); 459 log.trace("Added Observe relation for token: {}", token);
592 } 460 }
593 461
@@ -595,93 +463,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -595,93 +463,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
595 public void removedObserveRelation(ObserveRelation relation) { 463 public void removedObserveRelation(ObserveRelation relation) {
596 Request request = relation.getExchange().getRequest(); 464 Request request = relation.getExchange().getRequest();
597 String token = getTokenFromRequest(request); 465 String token = getTokenFromRequest(request);
598 - sessionInfoToObserveRelationMap.remove(tokenToCoapSessionInfoMap.get(token)); 466 + clients.deregisterObserveRelation(token);
599 log.trace("Relation removed for token: {}", token); 467 log.trace("Relation removed for token: {}", token);
600 } 468 }
601 } 469 }
602 470
603 - private void closeAndDeregister(TransportProtos.SessionInfoProto session) {  
604 - UUID sessionId = toSessionId(session);  
605 - transportService.process(session, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);  
606 - transportService.deregisterSession(session);  
607 - rpcSubscriptions.remove(sessionId);  
608 - attributeSubscriptions.remove(sessionId);  
609 - }  
610 -  
611 - private TransportConfigurationContainer getTransportConfigurationContainer(DeviceProfile deviceProfile) throws AdaptorException {  
612 - DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();  
613 - if (transportConfiguration instanceof DefaultDeviceProfileTransportConfiguration) {  
614 - return new TransportConfigurationContainer(true);  
615 - } else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) {  
616 - CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration =  
617 - (CoapDeviceProfileTransportConfiguration) transportConfiguration;  
618 - CoapDeviceTypeConfiguration coapDeviceTypeConfiguration =  
619 - coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration();  
620 - if (coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration) {  
621 - DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration =  
622 - (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration;  
623 - TransportPayloadTypeConfiguration transportPayloadTypeConfiguration =  
624 - defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration();  
625 - if (transportPayloadTypeConfiguration instanceof JsonTransportPayloadConfiguration) {  
626 - return new TransportConfigurationContainer(true);  
627 - } else {  
628 - ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration =  
629 - (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;  
630 - String deviceTelemetryProtoSchema = protoTransportPayloadConfiguration.getDeviceTelemetryProtoSchema();  
631 - String deviceAttributesProtoSchema = protoTransportPayloadConfiguration.getDeviceAttributesProtoSchema();  
632 - String deviceRpcRequestProtoSchema = protoTransportPayloadConfiguration.getDeviceRpcRequestProtoSchema();  
633 - String deviceRpcResponseProtoSchema = protoTransportPayloadConfiguration.getDeviceRpcResponseProtoSchema();  
634 - return new TransportConfigurationContainer(false,  
635 - protoTransportPayloadConfiguration.getTelemetryDynamicMessageDescriptor(deviceTelemetryProtoSchema),  
636 - protoTransportPayloadConfiguration.getAttributesDynamicMessageDescriptor(deviceAttributesProtoSchema),  
637 - protoTransportPayloadConfiguration.getRpcResponseDynamicMessageDescriptor(deviceRpcResponseProtoSchema),  
638 - protoTransportPayloadConfiguration.getRpcRequestDynamicMessageBuilder(deviceRpcRequestProtoSchema)  
639 - );  
640 - }  
641 - } else {  
642 - throw new AdaptorException("Invalid CoapDeviceTypeConfiguration type: " + coapDeviceTypeConfiguration.getClass().getSimpleName() + "!");  
643 - }  
644 - } else {  
645 - throw new AdaptorException("Invalid DeviceProfileTransportConfiguration type" + transportConfiguration.getClass().getSimpleName() + "!");  
646 - }  
647 - }  
648 -  
649 - private CoapTransportAdaptor getCoapTransportAdaptor(boolean jsonPayloadType) {  
650 - return jsonPayloadType ? transportContext.getJsonCoapAdaptor() : transportContext.getProtoCoapAdaptor();  
651 - }  
652 -  
653 - @Data  
654 - private static class TransportConfigurationContainer {  
655 -  
656 - private boolean jsonPayload;  
657 - private Descriptors.Descriptor telemetryMsgDescriptor;  
658 - private Descriptors.Descriptor attributesMsgDescriptor;  
659 - private Descriptors.Descriptor rpcResponseMsgDescriptor;  
660 - private DynamicMessage.Builder rpcRequestDynamicMessageBuilder;  
661 -  
662 - public TransportConfigurationContainer(boolean jsonPayload, Descriptors.Descriptor telemetryMsgDescriptor, Descriptors.Descriptor attributesMsgDescriptor, Descriptors.Descriptor rpcResponseMsgDescriptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) {  
663 - this.jsonPayload = jsonPayload;  
664 - this.telemetryMsgDescriptor = telemetryMsgDescriptor;  
665 - this.attributesMsgDescriptor = attributesMsgDescriptor;  
666 - this.rpcResponseMsgDescriptor = rpcResponseMsgDescriptor;  
667 - this.rpcRequestDynamicMessageBuilder = rpcRequestDynamicMessageBuilder;  
668 - }  
669 -  
670 - public TransportConfigurationContainer(boolean jsonPayload) {  
671 - this.jsonPayload = jsonPayload;  
672 - }  
673 - }  
674 -  
675 - @Data  
676 - private static class CoapObserveSessionInfo {  
677 -  
678 - private final TransportProtos.SessionInfoProto sessionInfoProto;  
679 - private final AtomicInteger observeNotificationCounter;  
680 -  
681 - private CoapObserveSessionInfo(TransportProtos.SessionInfoProto sessionInfoProto) {  
682 - this.sessionInfoProto = sessionInfoProto;  
683 - this.observeNotificationCounter = new AtomicInteger(0);  
684 - }  
685 - }  
686 471
687 } 472 }
@@ -24,9 +24,13 @@ import org.eclipse.californium.core.server.resources.CoapExchange; @@ -24,9 +24,13 @@ import org.eclipse.californium.core.server.resources.CoapExchange;
24 import org.eclipse.californium.core.server.resources.Resource; 24 import org.eclipse.californium.core.server.resources.Resource;
25 import org.thingsboard.server.common.data.DeviceTransportType; 25 import org.thingsboard.server.common.data.DeviceTransportType;
26 import org.thingsboard.server.common.data.StringUtils; 26 import org.thingsboard.server.common.data.StringUtils;
  27 +import org.thingsboard.server.common.data.id.DeviceId;
  28 +import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.data.ota.OtaPackageType; 29 import org.thingsboard.server.common.data.ota.OtaPackageType;
28 import org.thingsboard.server.common.data.security.DeviceTokenCredentials; 30 import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
29 import org.thingsboard.server.common.transport.TransportServiceCallback; 31 import org.thingsboard.server.common.transport.TransportServiceCallback;
  32 +import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
  33 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
30 import org.thingsboard.server.gen.transport.TransportProtos; 34 import org.thingsboard.server.gen.transport.TransportProtos;
31 import org.thingsboard.server.transport.coap.callback.CoapDeviceAuthCallback; 35 import org.thingsboard.server.transport.coap.callback.CoapDeviceAuthCallback;
32 36
@@ -68,19 +72,21 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource { @@ -68,19 +72,21 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource {
68 return; 72 return;
69 } 73 }
70 transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(), 74 transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(),
71 - new CoapDeviceAuthCallback(transportContext, exchange, (sessionInfo, deviceProfile) -> {  
72 - getOtaPackageCallback(sessionInfo, exchange, otaPackageType); 75 + new CoapDeviceAuthCallback(exchange, (msg, deviceProfile) -> {
  76 + getOtaPackageCallback(msg, exchange, otaPackageType);
73 })); 77 }));
74 } 78 }
75 79
76 - private void getOtaPackageCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, OtaPackageType firmwareType) { 80 + private void getOtaPackageCallback(ValidateDeviceCredentialsResponse msg, CoapExchange exchange, OtaPackageType firmwareType) {
  81 + TenantId tenantId = msg.getDeviceInfo().getTenantId();
  82 + DeviceId deviceId = msg.getDeviceInfo().getDeviceId();
77 TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder() 83 TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder()
78 - .setTenantIdMSB(sessionInfo.getTenantIdMSB())  
79 - .setTenantIdLSB(sessionInfo.getTenantIdLSB())  
80 - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())  
81 - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) 84 + .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
  85 + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
  86 + .setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
  87 + .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
82 .setType(firmwareType.name()).build(); 88 .setType(firmwareType.name()).build();
83 - transportContext.getTransportService().process(sessionInfo, requestMsg, new OtaPackageCallback(exchange)); 89 + transportContext.getTransportService().process(SessionInfoCreator.create(msg, transportContext, UUID.randomUUID()), requestMsg, new OtaPackageCallback(exchange));
84 } 90 }
85 91
86 private Optional<DeviceTokenCredentials> decodeCredentials(Request request) { 92 private Optional<DeviceTokenCredentials> decodeCredentials(Request request) {
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap;
  17 +
  18 +import com.google.protobuf.Descriptors;
  19 +import com.google.protobuf.DynamicMessage;
  20 +import lombok.Data;
  21 +
  22 +@Data
  23 +public class TransportConfigurationContainer {
  24 +
  25 + private boolean jsonPayload;
  26 + private Descriptors.Descriptor telemetryMsgDescriptor;
  27 + private Descriptors.Descriptor attributesMsgDescriptor;
  28 + private Descriptors.Descriptor rpcResponseMsgDescriptor;
  29 + private DynamicMessage.Builder rpcRequestDynamicMessageBuilder;
  30 +
  31 + public TransportConfigurationContainer(boolean jsonPayload, Descriptors.Descriptor telemetryMsgDescriptor, Descriptors.Descriptor attributesMsgDescriptor, Descriptors.Descriptor rpcResponseMsgDescriptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) {
  32 + this.jsonPayload = jsonPayload;
  33 + this.telemetryMsgDescriptor = telemetryMsgDescriptor;
  34 + this.attributesMsgDescriptor = attributesMsgDescriptor;
  35 + this.rpcResponseMsgDescriptor = rpcResponseMsgDescriptor;
  36 + this.rpcRequestDynamicMessageBuilder = rpcRequestDynamicMessageBuilder;
  37 + }
  38 +
  39 + public TransportConfigurationContainer(boolean jsonPayload) {
  40 + this.jsonPayload = jsonPayload;
  41 + }
  42 +}
@@ -107,7 +107,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { @@ -107,7 +107,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor {
107 Response response = new Response(CoAP.ResponseCode.CONTENT); 107 Response response = new Response(CoAP.ResponseCode.CONTENT);
108 JsonElement result = JsonConverter.toJson(msg); 108 JsonElement result = JsonConverter.toJson(msg);
109 response.setPayload(result.toString()); 109 response.setPayload(result.toString());
110 - response.setAcknowledged(isConfirmable); 110 + response.setConfirmable(isConfirmable);
111 return response; 111 return response;
112 } 112 }
113 113
@@ -125,8 +125,8 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { @@ -125,8 +125,8 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor {
125 public Response convertToPublish(boolean isConfirmable, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException { 125 public Response convertToPublish(boolean isConfirmable, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException {
126 if (msg.getSharedStateMsg()) { 126 if (msg.getSharedStateMsg()) {
127 if (StringUtils.isEmpty(msg.getError())) { 127 if (StringUtils.isEmpty(msg.getError())) {
128 - Response response = new Response(CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);  
129 - response.setAcknowledged(isConfirmable); 128 + Response response = new Response(CoAP.ResponseCode.CONTENT);
  129 + response.setConfirmable(isConfirmable);
130 TransportProtos.AttributeUpdateNotificationMsg notificationMsg = TransportProtos.AttributeUpdateNotificationMsg.newBuilder().addAllSharedUpdated(msg.getSharedAttributeListList()).build(); 130 TransportProtos.AttributeUpdateNotificationMsg notificationMsg = TransportProtos.AttributeUpdateNotificationMsg.newBuilder().addAllSharedUpdated(msg.getSharedAttributeListList()).build();
131 JsonObject result = JsonConverter.toJson(notificationMsg); 131 JsonObject result = JsonConverter.toJson(notificationMsg);
132 response.setPayload(result.toString()); 132 response.setPayload(result.toString());
@@ -139,7 +139,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { @@ -139,7 +139,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor {
139 return new Response(CoAP.ResponseCode.NOT_FOUND); 139 return new Response(CoAP.ResponseCode.NOT_FOUND);
140 } else { 140 } else {
141 Response response = new Response(CoAP.ResponseCode.CONTENT); 141 Response response = new Response(CoAP.ResponseCode.CONTENT);
142 - response.setAcknowledged(isConfirmable); 142 + response.setConfirmable(isConfirmable);
143 JsonObject result = JsonConverter.toJson(msg); 143 JsonObject result = JsonConverter.toJson(msg);
144 response.setPayload(result.toString()); 144 response.setPayload(result.toString());
145 return response; 145 return response;
@@ -148,7 +148,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { @@ -148,7 +148,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor {
148 } 148 }
149 149
150 private Response getObserveNotification(boolean confirmable, JsonElement json) { 150 private Response getObserveNotification(boolean confirmable, JsonElement json) {
151 - Response response = new Response(CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 151 + Response response = new Response(CoAP.ResponseCode.CONTENT);
152 response.setPayload(json.toString()); 152 response.setPayload(json.toString());
153 response.setConfirmable(confirmable); 153 response.setConfirmable(confirmable);
154 return response; 154 return response;
@@ -130,8 +130,8 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor { @@ -130,8 +130,8 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor {
130 public Response convertToPublish(boolean isConfirmable, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException { 130 public Response convertToPublish(boolean isConfirmable, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException {
131 if (msg.getSharedStateMsg()) { 131 if (msg.getSharedStateMsg()) {
132 if (StringUtils.isEmpty(msg.getError())) { 132 if (StringUtils.isEmpty(msg.getError())) {
133 - Response response = new Response(CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);  
134 - response.setAcknowledged(isConfirmable); 133 + Response response = new Response(CoAP.ResponseCode.CONTENT);
  134 + response.setConfirmable(isConfirmable);
135 TransportProtos.AttributeUpdateNotificationMsg notificationMsg = TransportProtos.AttributeUpdateNotificationMsg.newBuilder().addAllSharedUpdated(msg.getSharedAttributeListList()).build(); 135 TransportProtos.AttributeUpdateNotificationMsg notificationMsg = TransportProtos.AttributeUpdateNotificationMsg.newBuilder().addAllSharedUpdated(msg.getSharedAttributeListList()).build();
136 response.setPayload(notificationMsg.toByteArray()); 136 response.setPayload(notificationMsg.toByteArray());
137 return response; 137 return response;
@@ -143,7 +143,7 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor { @@ -143,7 +143,7 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor {
143 return new Response(CoAP.ResponseCode.NOT_FOUND); 143 return new Response(CoAP.ResponseCode.NOT_FOUND);
144 } else { 144 } else {
145 Response response = new Response(CoAP.ResponseCode.CONTENT); 145 Response response = new Response(CoAP.ResponseCode.CONTENT);
146 - response.setAcknowledged(isConfirmable); 146 + response.setConfirmable(isConfirmable);
147 response.setPayload(msg.toByteArray()); 147 response.setPayload(msg.toByteArray());
148 return response; 148 return response;
149 } 149 }
@@ -151,9 +151,9 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor { @@ -151,9 +151,9 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor {
151 } 151 }
152 152
153 private Response getObserveNotification(boolean confirmable, byte[] notification) { 153 private Response getObserveNotification(boolean confirmable, byte[] notification) {
154 - Response response = new Response(CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE); 154 + Response response = new Response(CoAP.ResponseCode.CONTENT);
155 response.setPayload(notification); 155 response.setPayload(notification);
156 - response.setAcknowledged(confirmable); 156 + response.setConfirmable(confirmable);
157 return response; 157 return response;
158 } 158 }
159 159
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap.callback;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.californium.core.coap.Request;
  21 +import org.eclipse.californium.core.server.resources.CoapExchange;
  22 +import org.thingsboard.server.common.transport.SessionMsgListener;
  23 +import org.thingsboard.server.gen.transport.TransportProtos;
  24 +import org.thingsboard.server.transport.coap.client.TbCoapClientState;
  25 +import org.thingsboard.server.transport.coap.client.TbCoapObservationState;
  26 +
  27 +import java.util.UUID;
  28 +
  29 +@RequiredArgsConstructor
  30 +@Slf4j
  31 +public abstract class AbstractSyncSessionCallback implements SessionMsgListener {
  32 +
  33 + protected final TbCoapClientState state;
  34 + protected final CoapExchange exchange;
  35 + protected final Request request;
  36 +
  37 + @Override
  38 + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg getAttributesResponse) {
  39 + logUnsupportedCommandMessage(getAttributesResponse);
  40 + }
  41 +
  42 + @Override
  43 + public void onAttributeUpdate(UUID sessionId, TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification) {
  44 + logUnsupportedCommandMessage(attributeUpdateNotification);
  45 + }
  46 +
  47 + @Override
  48 + public void onRemoteSessionCloseCommand(UUID sessionId, TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {
  49 +
  50 + }
  51 +
  52 + @Override
  53 + public void onToDeviceRpcRequest(UUID sessionId, TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest) {
  54 + logUnsupportedCommandMessage(toDeviceRequest);
  55 + }
  56 +
  57 + @Override
  58 + public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) {
  59 + logUnsupportedCommandMessage(toServerResponse);
  60 + }
  61 +
  62 + private void logUnsupportedCommandMessage(Object update) {
  63 + log.trace("[{}] Ignore unsupported update: {}", state.getDeviceId(), update);
  64 + }
  65 +
  66 + public static boolean isConRequest(TbCoapObservationState state) {
  67 + if (state != null) {
  68 + return state.getExchange().advanced().getRequest().isConfirmable();
  69 + } else {
  70 + return false;
  71 + }
  72 + }
  73 +
  74 +}
@@ -31,12 +31,10 @@ import java.util.function.BiConsumer; @@ -31,12 +31,10 @@ import java.util.function.BiConsumer;
31 31
32 @Slf4j 32 @Slf4j
33 public class CoapDeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> { 33 public class CoapDeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> {
34 - private final TransportContext transportContext;  
35 private final CoapExchange exchange; 34 private final CoapExchange exchange;
36 - private final BiConsumer<TransportProtos.SessionInfoProto, DeviceProfile> onSuccess; 35 + private final BiConsumer<ValidateDeviceCredentialsResponse, DeviceProfile> onSuccess;
37 36
38 - public CoapDeviceAuthCallback(TransportContext transportContext, CoapExchange exchange, BiConsumer<TransportProtos.SessionInfoProto, DeviceProfile> onSuccess) {  
39 - this.transportContext = transportContext; 37 + public CoapDeviceAuthCallback(CoapExchange exchange, BiConsumer<ValidateDeviceCredentialsResponse, DeviceProfile> onSuccess) {
40 this.exchange = exchange; 38 this.exchange = exchange;
41 this.onSuccess = onSuccess; 39 this.onSuccess = onSuccess;
42 } 40 }
@@ -45,8 +43,7 @@ public class CoapDeviceAuthCallback implements TransportServiceCallback<Validate @@ -45,8 +43,7 @@ public class CoapDeviceAuthCallback implements TransportServiceCallback<Validate
45 public void onSuccess(ValidateDeviceCredentialsResponse msg) { 43 public void onSuccess(ValidateDeviceCredentialsResponse msg) {
46 DeviceProfile deviceProfile = msg.getDeviceProfile(); 44 DeviceProfile deviceProfile = msg.getDeviceProfile();
47 if (msg.hasDeviceInfo() && deviceProfile != null) { 45 if (msg.hasDeviceInfo() && deviceProfile != null) {
48 - TransportProtos.SessionInfoProto sessionInfoProto = SessionInfoCreator.create(msg, transportContext, UUID.randomUUID());  
49 - onSuccess.accept(sessionInfoProto, deviceProfile); 46 + onSuccess.accept(msg, deviceProfile);
50 } else { 47 } else {
51 exchange.respond(CoAP.ResponseCode.UNAUTHORIZED); 48 exchange.respond(CoAP.ResponseCode.UNAUTHORIZED);
52 } 49 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap.callback;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.californium.core.coap.CoAP;
  20 +import org.eclipse.californium.core.coap.Request;
  21 +import org.eclipse.californium.core.coap.Response;
  22 +import org.eclipse.californium.core.server.resources.CoapExchange;
  23 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  24 +import org.thingsboard.server.gen.transport.TransportProtos;
  25 +import org.thingsboard.server.transport.coap.client.TbCoapClientState;
  26 +import org.thingsboard.server.transport.coap.client.TbCoapObservationState;
  27 +
  28 +@Slf4j
  29 +public class GetAttributesSyncSessionCallback extends AbstractSyncSessionCallback {
  30 +
  31 + public GetAttributesSyncSessionCallback(TbCoapClientState state, CoapExchange exchange, Request request) {
  32 + super(state, exchange, request);
  33 + }
  34 +
  35 + @Override
  36 + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg msg) {
  37 + try {
  38 + exchange.respond(state.getAdaptor().convertToPublish(AbstractSyncSessionCallback.isConRequest(state.getAttrs()), msg));
  39 + } catch (AdaptorException e) {
  40 + log.trace("[{}] Failed to reply due to error", state.getDeviceId(), e);
  41 + exchange.respond(new Response(CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
  42 + }
  43 + }
  44 +
  45 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap.callback;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.californium.core.coap.CoAP;
  20 +import org.eclipse.californium.core.coap.Request;
  21 +import org.eclipse.californium.core.server.resources.CoapExchange;
  22 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  23 +import org.thingsboard.server.gen.transport.TransportProtos;
  24 +import org.thingsboard.server.transport.coap.client.TbCoapClientState;
  25 +
  26 +import java.util.UUID;
  27 +
  28 +@Slf4j
  29 +public class ToServerRpcSyncSessionCallback extends AbstractSyncSessionCallback {
  30 +
  31 + public ToServerRpcSyncSessionCallback(TbCoapClientState state, CoapExchange exchange, Request request) {
  32 + super(state, exchange, request);
  33 + }
  34 +
  35 + @Override
  36 + public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) {
  37 + try {
  38 + exchange.respond(state.getAdaptor().convertToPublish(isConRequest(state.getRpc()), toServerResponse));
  39 + } catch (AdaptorException e) {
  40 + log.trace("Failed to reply due to error", e);
  41 + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
  42 + }
  43 + }
  44 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap.client;
  17 +
  18 +import org.eclipse.californium.core.observe.ObserveRelation;
  19 +import org.eclipse.californium.core.server.resources.CoapExchange;
  20 +import org.thingsboard.server.common.data.DeviceProfile;
  21 +import org.thingsboard.server.common.msg.session.SessionMsgType;
  22 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  23 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
  24 +import org.thingsboard.server.gen.transport.TransportProtos;
  25 +
  26 +import java.util.concurrent.atomic.AtomicInteger;
  27 +
  28 +public interface CoapClientContext {
  29 +
  30 + boolean registerAttributeObservation(TbCoapClientState clientState, String token, CoapExchange exchange);
  31 +
  32 + boolean registerRpcObservation(TbCoapClientState clientState, String token, CoapExchange exchange);
  33 +
  34 + void onUplink(TransportProtos.SessionInfoProto sessionInfo);
  35 +
  36 + AtomicInteger getNotificationCounterByToken(String token);
  37 +
  38 + TbCoapClientState getOrCreateClient(SessionMsgType type, ValidateDeviceCredentialsResponse deviceCredentials, DeviceProfile deviceProfile) throws AdaptorException;
  39 +
  40 + TransportProtos.SessionInfoProto getNewSyncSession(TbCoapClientState clientState);
  41 +
  42 + void deregisterAttributeObservation(TbCoapClientState clientState, String token, CoapExchange exchange);
  43 +
  44 + void deregisterRpcObservation(TbCoapClientState clientState, String token, CoapExchange exchange);
  45 +
  46 + void reportActivity();
  47 +
  48 + void registerObserveRelation(String token, ObserveRelation relation);
  49 +
  50 + void deregisterObserveRelation(String token);
  51 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap.client;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.californium.core.coap.CoAP;
  21 +import org.eclipse.californium.core.coap.Response;
  22 +import org.eclipse.californium.core.observe.ObserveRelation;
  23 +import org.eclipse.californium.core.server.resources.CoapExchange;
  24 +import org.springframework.stereotype.Service;
  25 +import org.thingsboard.server.coapserver.TbCoapServerComponent;
  26 +import org.thingsboard.server.common.data.DeviceProfile;
  27 +import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration;
  28 +import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration;
  29 +import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration;
  30 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
  31 +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
  32 +import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
  33 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
  34 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
  35 +import org.thingsboard.server.common.data.id.DeviceId;
  36 +import org.thingsboard.server.common.msg.session.FeatureType;
  37 +import org.thingsboard.server.common.msg.session.SessionMsgType;
  38 +import org.thingsboard.server.common.transport.SessionMsgListener;
  39 +import org.thingsboard.server.common.transport.TransportService;
  40 +import org.thingsboard.server.common.transport.TransportServiceCallback;
  41 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  42 +import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
  43 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
  44 +import org.thingsboard.server.gen.transport.TransportProtos;
  45 +import org.thingsboard.server.transport.coap.CoapTransportContext;
  46 +import org.thingsboard.server.transport.coap.TbCoapMessageObserver;
  47 +import org.thingsboard.server.transport.coap.TransportConfigurationContainer;
  48 +import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;
  49 +import org.thingsboard.server.transport.coap.callback.AbstractSyncSessionCallback;
  50 +import org.thingsboard.server.transport.coap.callback.CoapNoOpCallback;
  51 +import org.thingsboard.server.transport.coap.callback.CoapOkCallback;
  52 +
  53 +import java.util.UUID;
  54 +import java.util.concurrent.ConcurrentHashMap;
  55 +import java.util.concurrent.ConcurrentMap;
  56 +import java.util.concurrent.ThreadLocalRandom;
  57 +import java.util.concurrent.TimeUnit;
  58 +import java.util.concurrent.atomic.AtomicInteger;
  59 +
  60 +import static org.eclipse.californium.core.coap.Message.MAX_MID;
  61 +import static org.eclipse.californium.core.coap.Message.NONE;
  62 +
  63 +@Slf4j
  64 +@Service
  65 +@RequiredArgsConstructor
  66 +@TbCoapServerComponent
  67 +public class DefaultCoapClientContext implements CoapClientContext {
  68 +
  69 + private final CoapTransportContext transportContext;
  70 + private final TransportService transportService;
  71 + private final ConcurrentMap<DeviceId, TbCoapClientState> clients = new ConcurrentHashMap<>();
  72 + private final ConcurrentMap<String, TbCoapClientState> clientsByToken = new ConcurrentHashMap<>();
  73 +
  74 + @Override
  75 + public boolean registerAttributeObservation(TbCoapClientState clientState, String token, CoapExchange exchange) {
  76 + return registerFeatureObservation(clientState, token, exchange, FeatureType.ATTRIBUTES);
  77 + }
  78 +
  79 + @Override
  80 + public boolean registerRpcObservation(TbCoapClientState clientState, String token, CoapExchange exchange) {
  81 + return registerFeatureObservation(clientState, token, exchange, FeatureType.RPC);
  82 + }
  83 +
  84 + @Override
  85 + public void onUplink(TransportProtos.SessionInfoProto sessionInfo) {
  86 + getClientState(toDeviceId(sessionInfo)).updateLastUplinkTime();
  87 + }
  88 +
  89 + @Override
  90 + public AtomicInteger getNotificationCounterByToken(String token) {
  91 + TbCoapClientState state = clientsByToken.get(token);
  92 + if (state == null) {
  93 + log.trace("Failed to find state using token: {}", token);
  94 + return null;
  95 + }
  96 + if (state.getAttrs() != null && state.getAttrs().getToken().equals(token)) {
  97 + return state.getAttrs().getObserveCounter();
  98 + } else {
  99 + log.trace("Failed to find attr subscription using token: {}", token);
  100 + }
  101 + if (state.getRpc() != null && state.getRpc().getToken().equals(token)) {
  102 + return state.getRpc().getObserveCounter();
  103 + } else {
  104 + log.trace("Failed to find rpc subscription using token: {}", token);
  105 + }
  106 + return null;
  107 + }
  108 +
  109 + @Override
  110 + public void registerObserveRelation(String token, ObserveRelation relation) {
  111 + TbCoapClientState state = clientsByToken.get(token);
  112 + if (state == null) {
  113 + log.trace("Failed to find state using token: {}", token);
  114 + return;
  115 + }
  116 + if (state.getAttrs() != null && state.getAttrs().getToken().equals(token)) {
  117 + state.getAttrs().setObserveRelation(relation);
  118 + } else {
  119 + log.trace("Failed to find attr subscription using token: {}", token);
  120 + }
  121 + if (state.getRpc() != null && state.getRpc().getToken().equals(token)) {
  122 + state.getRpc().setObserveRelation(relation);
  123 + } else {
  124 + log.trace("Failed to find rpc subscription using token: {}", token);
  125 + }
  126 + }
  127 +
  128 + @Override
  129 + public void deregisterObserveRelation(String token) {
  130 + TbCoapClientState state = clientsByToken.remove(token);
  131 + if (state == null) {
  132 + log.trace("Failed to find state using token: {}", token);
  133 + return;
  134 + }
  135 + if (state.getAttrs() != null && state.getAttrs().getToken().equals(token)) {
  136 + cancelAttributeSubscription(state);
  137 + } else {
  138 + log.trace("Failed to find attr subscription using token: {}", token);
  139 + }
  140 + if (state.getRpc() != null && state.getRpc().getToken().equals(token)) {
  141 + cancelRpcSubscription(state);
  142 + } else {
  143 + log.trace("Failed to find rpc subscription using token: {}", token);
  144 + }
  145 + }
  146 +
  147 + @Override
  148 + public void reportActivity() {
  149 + for (TbCoapClientState state : clients.values()) {
  150 + if (state.getSession() != null) {
  151 + transportService.reportActivity(state.getSession());
  152 + }
  153 + }
  154 + }
  155 +
  156 + private boolean registerFeatureObservation(TbCoapClientState state, String token, CoapExchange exchange, FeatureType featureType) {
  157 + state.lock();
  158 + try {
  159 + boolean newObservation;
  160 + if (FeatureType.ATTRIBUTES.equals(featureType)) {
  161 + if (state.getAttrs() == null) {
  162 + newObservation = true;
  163 + state.setAttrs(new TbCoapObservationState(exchange, token));
  164 + } else {
  165 + newObservation = !state.getAttrs().getToken().equals(token);
  166 + if (newObservation) {
  167 + TbCoapObservationState old = state.getAttrs();
  168 + state.setAttrs(new TbCoapObservationState(exchange, token));
  169 + old.getExchange().respond(CoAP.ResponseCode.DELETED);
  170 + }
  171 + }
  172 + } else {
  173 + if (state.getRpc() == null) {
  174 + newObservation = true;
  175 + state.setRpc(new TbCoapObservationState(exchange, token));
  176 + } else {
  177 + newObservation = !state.getRpc().getToken().equals(token);
  178 + if (newObservation) {
  179 + TbCoapObservationState old = state.getRpc();
  180 + state.setRpc(new TbCoapObservationState(exchange, token));
  181 + old.getExchange().respond(CoAP.ResponseCode.DELETED);
  182 + }
  183 + }
  184 + }
  185 + if (newObservation) {
  186 + clientsByToken.put(token, state);
  187 + if (state.getSession() == null) {
  188 + TransportProtos.SessionInfoProto session = SessionInfoCreator.create(state.getCredentials(), transportContext, UUID.randomUUID());
  189 + state.setSession(session);
  190 + transportService.registerAsyncSession(session, new CoapSessionListener(state));
  191 + transportService.process(session, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null);
  192 + }
  193 + if (FeatureType.ATTRIBUTES.equals(featureType)) {
  194 + transportService.process(state.getSession(),
  195 + TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), new CoapNoOpCallback(exchange));
  196 + transportService.process(state.getSession(),
  197 + TransportProtos.GetAttributeRequestMsg.newBuilder().setOnlyShared(true).build(),
  198 + new CoapNoOpCallback(exchange));
  199 + } else {
  200 + transportService.process(state.getSession(),
  201 + TransportProtos.SubscribeToRPCMsg.getDefaultInstance(),
  202 + new CoapOkCallback(exchange, CoAP.ResponseCode.VALID, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)
  203 + );
  204 + }
  205 + }
  206 + return newObservation;
  207 + } finally {
  208 + state.unlock();
  209 + }
  210 + }
  211 +
  212 + @Override
  213 + public void deregisterAttributeObservation(TbCoapClientState state, String token, CoapExchange exchange) {
  214 + state.lock();
  215 + try {
  216 + clientsByToken.remove(token);
  217 + if (state.getSession() == null) {
  218 + log.trace("[{}] Failed to delete attribute observation: {}. Session is not present.", state.getDeviceId(), token);
  219 + return;
  220 + }
  221 + if (state.getAttrs() == null) {
  222 + log.trace("[{}] Failed to delete attribute observation: {}. It is not registered.", state.getDeviceId(), token);
  223 + return;
  224 + }
  225 + if (!state.getAttrs().getToken().equals(token)) {
  226 + log.trace("[{}] Failed to delete attribute observation: {}. Token mismatch.", state.getDeviceId(), token);
  227 + return;
  228 + }
  229 + cancelAttributeSubscription(state);
  230 + } finally {
  231 + state.unlock();
  232 + }
  233 + }
  234 +
  235 + @Override
  236 + public void deregisterRpcObservation(TbCoapClientState state, String token, CoapExchange exchange) {
  237 + state.lock();
  238 + try {
  239 + clientsByToken.remove(token);
  240 + if (state.getSession() == null) {
  241 + log.trace("[{}] Failed to delete rpc observation: {}. Session is not present.", state.getDeviceId(), token);
  242 + return;
  243 + }
  244 + if (state.getRpc() == null) {
  245 + log.trace("[{}] Failed to delete rpc observation: {}. It is not registered.", state.getDeviceId(), token);
  246 + return;
  247 + }
  248 + if (!state.getRpc().getToken().equals(token)) {
  249 + log.trace("[{}] Failed to delete rpc observation: {}. Token mismatch.", state.getDeviceId(), token);
  250 + return;
  251 + }
  252 + cancelRpcSubscription(state);
  253 + } finally {
  254 + state.unlock();
  255 + }
  256 + }
  257 +
  258 + @Override
  259 + public TbCoapClientState getOrCreateClient(SessionMsgType type, ValidateDeviceCredentialsResponse deviceCredentials, DeviceProfile deviceProfile) throws AdaptorException {
  260 + DeviceId deviceId = deviceCredentials.getDeviceInfo().getDeviceId();
  261 + TbCoapClientState state = getClientState(deviceId);
  262 + state.lock();
  263 + try {
  264 + if (state.getConfiguration() == null || state.getAdaptor() == null) {
  265 + state.setConfiguration(getTransportConfigurationContainer(deviceProfile));
  266 + state.setAdaptor(getCoapTransportAdaptor(state.getConfiguration().isJsonPayload()));
  267 + }
  268 + if (state.getCredentials() == null) {
  269 + state.setCredentials(deviceCredentials);
  270 + }
  271 + } finally {
  272 + state.unlock();
  273 + }
  274 + return state;
  275 + }
  276 +
  277 + @Override
  278 + public TransportProtos.SessionInfoProto getNewSyncSession(TbCoapClientState state) {
  279 + return SessionInfoCreator.create(state.getCredentials(), transportContext, UUID.randomUUID());
  280 + }
  281 +
  282 + private TbCoapClientState getClientState(DeviceId deviceId) {
  283 + return clients.computeIfAbsent(deviceId, TbCoapClientState::new);
  284 + }
  285 +
  286 + private static DeviceId toDeviceId(TransportProtos.SessionInfoProto s) {
  287 + return new DeviceId(new UUID(s.getDeviceIdMSB(), s.getDeviceIdLSB()));
  288 + }
  289 +
  290 + private static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) {
  291 + return TransportProtos.SessionEventMsg.newBuilder()
  292 + .setSessionType(TransportProtos.SessionType.ASYNC)
  293 + .setEvent(event).build();
  294 + }
  295 +
  296 + private TransportConfigurationContainer getTransportConfigurationContainer(DeviceProfile deviceProfile) throws AdaptorException {
  297 + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
  298 + if (transportConfiguration instanceof DefaultDeviceProfileTransportConfiguration) {
  299 + return new TransportConfigurationContainer(true);
  300 + } else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) {
  301 + CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration =
  302 + (CoapDeviceProfileTransportConfiguration) transportConfiguration;
  303 + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration =
  304 + coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration();
  305 + if (coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration) {
  306 + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration =
  307 + (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration;
  308 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration =
  309 + defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration();
  310 + if (transportPayloadTypeConfiguration instanceof JsonTransportPayloadConfiguration) {
  311 + return new TransportConfigurationContainer(true);
  312 + } else {
  313 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration =
  314 + (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
  315 + String deviceTelemetryProtoSchema = protoTransportPayloadConfiguration.getDeviceTelemetryProtoSchema();
  316 + String deviceAttributesProtoSchema = protoTransportPayloadConfiguration.getDeviceAttributesProtoSchema();
  317 + String deviceRpcRequestProtoSchema = protoTransportPayloadConfiguration.getDeviceRpcRequestProtoSchema();
  318 + String deviceRpcResponseProtoSchema = protoTransportPayloadConfiguration.getDeviceRpcResponseProtoSchema();
  319 + return new TransportConfigurationContainer(false,
  320 + protoTransportPayloadConfiguration.getTelemetryDynamicMessageDescriptor(deviceTelemetryProtoSchema),
  321 + protoTransportPayloadConfiguration.getAttributesDynamicMessageDescriptor(deviceAttributesProtoSchema),
  322 + protoTransportPayloadConfiguration.getRpcResponseDynamicMessageDescriptor(deviceRpcResponseProtoSchema),
  323 + protoTransportPayloadConfiguration.getRpcRequestDynamicMessageBuilder(deviceRpcRequestProtoSchema)
  324 + );
  325 + }
  326 + } else {
  327 + throw new AdaptorException("Invalid CoapDeviceTypeConfiguration type: " + coapDeviceTypeConfiguration.getClass().getSimpleName() + "!");
  328 + }
  329 + } else {
  330 + throw new AdaptorException("Invalid DeviceProfileTransportConfiguration type" + transportConfiguration.getClass().getSimpleName() + "!");
  331 + }
  332 + }
  333 +
  334 + private CoapTransportAdaptor getCoapTransportAdaptor(boolean jsonPayloadType) {
  335 + return jsonPayloadType ? transportContext.getJsonCoapAdaptor() : transportContext.getProtoCoapAdaptor();
  336 + }
  337 +
  338 + @RequiredArgsConstructor
  339 + private class CoapSessionListener implements SessionMsgListener {
  340 +
  341 + private final TbCoapClientState state;
  342 +
  343 + @Override
  344 + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg msg) {
  345 + TbCoapObservationState attrs = state.getAttrs();
  346 + if (attrs != null) {
  347 + try {
  348 + boolean conRequest = AbstractSyncSessionCallback.isConRequest(state.getAttrs());
  349 + Response response = state.getAdaptor().convertToPublish(conRequest, msg);
  350 + attrs.getExchange().respond(response);
  351 + } catch (AdaptorException e) {
  352 + log.trace("Failed to reply due to error", e);
  353 + cancelObserveRelation(attrs);
  354 + cancelAttributeSubscription(state);
  355 + }
  356 + } else {
  357 + log.debug("[{}] Get Attrs exchange is empty", state.getDeviceId());
  358 + }
  359 + }
  360 +
  361 + @Override
  362 + public void onAttributeUpdate(UUID sessionId, TransportProtos.AttributeUpdateNotificationMsg msg) {
  363 + log.trace("[{}] Received attributes update notification to device", sessionId);
  364 + TbCoapObservationState attrs = state.getAttrs();
  365 + if (attrs != null) {
  366 + try {
  367 + boolean conRequest = AbstractSyncSessionCallback.isConRequest(state.getAttrs());
  368 + Response response = state.getAdaptor().convertToPublish(conRequest, msg);
  369 + attrs.getExchange().respond(response);
  370 + } catch (AdaptorException e) {
  371 + log.trace("[{}] Failed to reply due to error", state.getDeviceId(), e);
  372 + cancelObserveRelation(attrs);
  373 + cancelAttributeSubscription(state);
  374 + }
  375 + } else {
  376 + log.debug("[{}] Get Attrs exchange is empty", state.getDeviceId());
  377 + }
  378 + }
  379 +
  380 + @Override
  381 + public void onRemoteSessionCloseCommand(UUID sessionId, TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {
  382 + log.trace("[{}] Received the remote command to close the session: {}", sessionId, sessionCloseNotification.getMessage());
  383 + cancelRpcSubscription(state);
  384 + cancelAttributeSubscription(state);
  385 + }
  386 +
  387 + @Override
  388 + public void onToDeviceRpcRequest(UUID sessionId, TransportProtos.ToDeviceRpcRequestMsg msg) {
  389 + log.trace("[{}] Received RPC command to device", sessionId);
  390 + boolean sent = false;
  391 + boolean conRequest = AbstractSyncSessionCallback.isConRequest(state.getRpc());
  392 + try {
  393 + Response response = state.getAdaptor().convertToPublish(conRequest, msg, state.getConfiguration().getRpcRequestDynamicMessageBuilder());
  394 + int requestId = getNextMsgId();
  395 + response.setMID(requestId);
  396 + if (msg.getPersisted() && conRequest) {
  397 + transportContext.getRpcAwaitingAck().put(requestId, msg);
  398 + transportContext.getScheduler().schedule(() -> {
  399 + TransportProtos.ToDeviceRpcRequestMsg awaitingAckMsg = transportContext.getRpcAwaitingAck().remove(requestId);
  400 + if (awaitingAckMsg != null) {
  401 + transportService.process(state.getSession(), msg, true, TransportServiceCallback.EMPTY);
  402 + }
  403 + }, Math.max(0, msg.getExpirationTime() - System.currentTimeMillis()), TimeUnit.MILLISECONDS);
  404 + response.addMessageObserver(new TbCoapMessageObserver(requestId, id -> {
  405 + TransportProtos.ToDeviceRpcRequestMsg rpcRequestMsg = transportContext.getRpcAwaitingAck().remove(id);
  406 + if (rpcRequestMsg != null) {
  407 + transportService.process(state.getSession(), rpcRequestMsg, false, TransportServiceCallback.EMPTY);
  408 + }
  409 + }));
  410 + }
  411 + state.getRpc().getExchange().respond(response);
  412 + sent = true;
  413 + } catch (AdaptorException e) {
  414 + log.trace("Failed to reply due to error", e);
  415 + cancelObserveRelation(state.getRpc());
  416 + cancelRpcSubscription(state);
  417 + } finally {
  418 + if (msg.getPersisted() && !conRequest) {
  419 + transportService.process(state.getSession(), msg, sent, TransportServiceCallback.EMPTY);
  420 + }
  421 + }
  422 + }
  423 +
  424 + @Override
  425 + public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg msg) {
  426 +
  427 + }
  428 +
  429 + private void cancelObserveRelation(TbCoapObservationState attrs) {
  430 + if (attrs.getObserveRelation() != null) {
  431 + attrs.getObserveRelation().cancel();
  432 + }
  433 + }
  434 + }
  435 +
  436 + protected int getNextMsgId() {
  437 + return ThreadLocalRandom.current().nextInt(NONE, MAX_MID + 1);
  438 + }
  439 +
  440 + private void cancelRpcSubscription(TbCoapClientState state) {
  441 + if (state.getRpc() != null) {
  442 + clientsByToken.remove(state.getRpc().getToken());
  443 + CoapExchange exchange = state.getAttrs().getExchange();
  444 + state.setRpc(null);
  445 + transportService.process(state.getSession(),
  446 + TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(),
  447 + new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
  448 + if (state.getAttrs() == null) {
  449 + transportService.process(state.getSession(), getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);
  450 + transportService.deregisterSession(state.getSession());
  451 + state.setSession(null);
  452 + }
  453 + }
  454 + }
  455 +
  456 + private void cancelAttributeSubscription(TbCoapClientState state) {
  457 + if (state.getAttrs() != null) {
  458 + clientsByToken.remove(state.getAttrs().getToken());
  459 + CoapExchange exchange = state.getAttrs().getExchange();
  460 + state.setAttrs(null);
  461 + transportService.process(state.getSession(),
  462 + TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(),
  463 + new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
  464 + if (state.getRpc() == null) {
  465 + transportService.process(state.getSession(), getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);
  466 + transportService.deregisterSession(state.getSession());
  467 + state.setSession(null);
  468 + }
  469 + }
  470 + }
  471 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap.client;
  17 +
  18 +import lombok.Data;
  19 +import lombok.Getter;
  20 +import lombok.Setter;
  21 +import org.eclipse.californium.core.network.Exchange;
  22 +import org.thingsboard.server.common.data.id.DeviceId;
  23 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
  24 +import org.thingsboard.server.gen.transport.TransportProtos;
  25 +import org.thingsboard.server.transport.coap.TransportConfigurationContainer;
  26 +import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;
  27 +
  28 +import java.util.HashMap;
  29 +import java.util.List;
  30 +import java.util.Map;
  31 +import java.util.concurrent.Future;
  32 +import java.util.concurrent.atomic.AtomicInteger;
  33 +import java.util.concurrent.locks.Lock;
  34 +import java.util.concurrent.locks.ReentrantLock;
  35 +import java.util.stream.Collectors;
  36 +import java.util.stream.Stream;
  37 +
  38 +@Data
  39 +public class TbCoapClientState {
  40 +
  41 + private final DeviceId deviceId;
  42 + private final Lock lock;
  43 +
  44 + private volatile TransportConfigurationContainer configuration;
  45 + private volatile CoapTransportAdaptor adaptor;
  46 + private volatile ValidateDeviceCredentialsResponse credentials;
  47 +
  48 + private volatile TransportProtos.SessionInfoProto session;
  49 +
  50 + private volatile TbCoapObservationState attrs;
  51 + private volatile TbCoapObservationState rpc;
  52 +
  53 + @Getter
  54 + @Setter
  55 + private boolean asleep;
  56 + @Getter
  57 + private long lastUplinkTime;
  58 + @Getter
  59 + @Setter
  60 + private Future<Void> sleepTask;
  61 +
  62 + private boolean firstEdrxDownlink = true;
  63 +
  64 + public TbCoapClientState(DeviceId deviceId) {
  65 + this.deviceId = deviceId;
  66 + this.lock = new ReentrantLock();
  67 + }
  68 +
  69 + public void lock() {
  70 + lock.lock();
  71 + }
  72 +
  73 + public void unlock() {
  74 + lock.unlock();
  75 + }
  76 +
  77 + public long updateLastUplinkTime(){
  78 + this.lastUplinkTime = System.currentTimeMillis();
  79 + this.firstEdrxDownlink = true;
  80 + return lastUplinkTime;
  81 + }
  82 +
  83 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap.client;
  17 +
  18 +import lombok.Data;
  19 +import lombok.RequiredArgsConstructor;
  20 +import org.eclipse.californium.core.observe.ObserveRelation;
  21 +import org.eclipse.californium.core.server.resources.CoapExchange;
  22 +
  23 +import java.util.concurrent.atomic.AtomicInteger;
  24 +
  25 +@Data
  26 +@RequiredArgsConstructor
  27 +public class TbCoapObservationState {
  28 +
  29 + private final CoapExchange exchange;
  30 + private final String token;
  31 + private final AtomicInteger observeCounter = new AtomicInteger(0);
  32 + private volatile ObserveRelation observeRelation;
  33 +
  34 +}
@@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransp @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransp
31 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; 31 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
32 import org.thingsboard.server.common.data.device.profile.EfentoCoapDeviceTypeConfiguration; 32 import org.thingsboard.server.common.data.device.profile.EfentoCoapDeviceTypeConfiguration;
33 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 33 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  34 +import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
34 import org.thingsboard.server.gen.transport.TransportProtos; 35 import org.thingsboard.server.gen.transport.TransportProtos;
35 import org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos; 36 import org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos;
36 import org.thingsboard.server.gen.transport.coap.MeasurementsProtos; 37 import org.thingsboard.server.gen.transport.coap.MeasurementsProtos;
@@ -81,7 +82,8 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource { @@ -81,7 +82,8 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
81 log.trace("Successfully parsed Efento ProtoMeasurements: [{}]", protoMeasurements.getCloudToken()); 82 log.trace("Successfully parsed Efento ProtoMeasurements: [{}]", protoMeasurements.getCloudToken());
82 String token = protoMeasurements.getCloudToken(); 83 String token = protoMeasurements.getCloudToken();
83 transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(), 84 transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(),
84 - new CoapDeviceAuthCallback(transportContext, exchange, (sessionInfo, deviceProfile) -> { 85 + new CoapDeviceAuthCallback(exchange, (msg, deviceProfile) -> {
  86 + TransportProtos.SessionInfoProto sessionInfo = SessionInfoCreator.create(msg, transportContext, UUID.randomUUID());
85 UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); 87 UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
86 try { 88 try {
87 validateEfentoTransportConfiguration(deviceProfile); 89 validateEfentoTransportConfiguration(deviceProfile);
@@ -43,50 +43,4 @@ public abstract class AbstractLwM2mTransportResource extends LwM2mCoapResource { @@ -43,50 +43,4 @@ public abstract class AbstractLwM2mTransportResource extends LwM2mCoapResource {
43 43
44 protected abstract void processHandlePost(CoapExchange exchange); 44 protected abstract void processHandlePost(CoapExchange exchange);
45 45
46 - public static class CoapOkCallback implements TransportServiceCallback<Void> {  
47 - private final CoapExchange exchange;  
48 - private final CoAP.ResponseCode onSuccessResponse;  
49 - private final CoAP.ResponseCode onFailureResponse;  
50 -  
51 - public CoapOkCallback(CoapExchange exchange, CoAP.ResponseCode onSuccessResponse, CoAP.ResponseCode onFailureResponse) {  
52 - this.exchange = exchange;  
53 - this.onSuccessResponse = onSuccessResponse;  
54 - this.onFailureResponse = onFailureResponse;  
55 - }  
56 -  
57 - @Override  
58 - public void onSuccess(Void msg) {  
59 - Response response = new Response(onSuccessResponse);  
60 - response.setAcknowledged(isConRequest());  
61 - exchange.respond(response);  
62 - }  
63 -  
64 - @Override  
65 - public void onError(Throwable e) {  
66 - exchange.respond(onFailureResponse);  
67 - }  
68 -  
69 - private boolean isConRequest() {  
70 - return exchange.advanced().getRequest().isConfirmable();  
71 - }  
72 - }  
73 -  
74 - public static class CoapNoOpCallback implements TransportServiceCallback<Void> {  
75 - private final CoapExchange exchange;  
76 -  
77 - CoapNoOpCallback(CoapExchange exchange) {  
78 - this.exchange = exchange;  
79 - }  
80 -  
81 - @Override  
82 - public void onSuccess(Void msg) {  
83 - }  
84 -  
85 - @Override  
86 - public void onError(Throwable e) {  
87 - exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);  
88 - }  
89 - }  
90 -  
91 -  
92 } 46 }
@@ -161,6 +161,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -161,6 +161,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
161 //TODO: check that the client supports FW and SW by checking the supported objects in the model. 161 //TODO: check that the client supports FW and SW by checking the supported objects in the model.
162 List<String> attributesToFetch = new ArrayList<>(); 162 List<String> attributesToFetch = new ArrayList<>();
163 LwM2MClientFwOtaInfo fwInfo = getOrInitFwInfo(client); 163 LwM2MClientFwOtaInfo fwInfo = getOrInitFwInfo(client);
  164 +
164 if (fwInfo.isSupported()) { 165 if (fwInfo.isSupported()) {
165 attributesToFetch.add(FIRMWARE_TITLE); 166 attributesToFetch.add(FIRMWARE_TITLE);
166 attributesToFetch.add(FIRMWARE_VERSION); 167 attributesToFetch.add(FIRMWARE_VERSION);