Browse Source

Lwm2m: back fix bug: shared attr & change observe in profile (#4175)

* Lwm2m:  back fix bug: shared attr & change observe in profile

* Lwm2m:  replace:  lwServer by leshanServer. Remove lwServer  from interface LwM2mTransportService

* Lwm2m:  californium ver 2.6.1

* Lwm2m:  californium ver 2.6.1

* Lwm2m:  Json clone
pull/4200/head
nickAS21 6 years ago
committed by GitHub
parent
commit
675e552672
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
  2. 78
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
  3. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java
  4. 13
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java
  5. 248
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java
  6. 28
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java
  7. 2
      pom.xml

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

@ -18,7 +18,6 @@ package org.thingsboard.server.transport.lwm2m.server;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.response.ObserveResponse; import org.eclipse.leshan.core.response.ObserveResponse;
import org.eclipse.leshan.server.californium.LeshanServer;
import org.eclipse.leshan.server.observation.ObservationListener; import org.eclipse.leshan.server.observation.ObservationListener;
import org.eclipse.leshan.server.queue.PresenceListener; import org.eclipse.leshan.server.queue.PresenceListener;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
@ -30,11 +29,9 @@ import java.util.Collection;
@Slf4j @Slf4j
public class LwM2mServerListener { public class LwM2mServerListener {
private final LeshanServer lhServer;
private final LwM2mTransportServiceImpl service; private final LwM2mTransportServiceImpl service;
public LwM2mServerListener(LeshanServer lhServer, LwM2mTransportServiceImpl service) { public LwM2mServerListener(LwM2mTransportServiceImpl service) {
this.lhServer = lhServer;
this.service = service; this.service = service;
} }
@ -45,7 +42,7 @@ public class LwM2mServerListener {
@Override @Override
public void registered(Registration registration, Registration previousReg, public void registered(Registration registration, Registration previousReg,
Collection<Observation> previousObservations) { Collection<Observation> previousObservations) {
service.onRegistered(lhServer, registration, previousObservations); service.onRegistered(registration, previousObservations);
} }
/** /**
@ -54,7 +51,7 @@ public class LwM2mServerListener {
@Override @Override
public void updated(RegistrationUpdate update, Registration updatedRegistration, public void updated(RegistrationUpdate update, Registration updatedRegistration,
Registration previousRegistration) { Registration previousRegistration) {
service.updatedReg(lhServer, updatedRegistration); service.updatedReg(updatedRegistration);
} }
/** /**
@ -63,7 +60,7 @@ public class LwM2mServerListener {
@Override @Override
public void unregistered(Registration registration, Collection<Observation> observations, boolean expired, public void unregistered(Registration registration, Collection<Observation> observations, boolean expired,
Registration newReg) { Registration newReg) {
service.unReg(lhServer, registration, observations); service.unReg(registration, observations);
} }
}; };

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

@ -55,10 +55,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -78,21 +75,27 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandle
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.RESPONSE_CHANNEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.RESPONSE_CHANNEL;
@Slf4j @Slf4j
@Service("LwM2mTransportRequest") @Service
@TbLwM2mTransportComponent @TbLwM2mTransportComponent
public class LwM2mTransportRequest { public class LwM2mTransportRequest {
private ExecutorService executorResponse; private ExecutorService executorResponse;
private LwM2mValueConverterImpl converter; private LwM2mValueConverterImpl converter;
@Autowired private final LwM2mTransportContextServer context;
private LwM2mTransportServiceImpl service;
@Autowired private final LwM2mClientContext lwM2mClientContext;
private LwM2mTransportContextServer context;
private final LeshanServer leshanServer;
@Autowired @Autowired
private LwM2mClientContext lwM2mClientContext; private LwM2mTransportServiceImpl serviceImpl;
public LwM2mTransportRequest(LwM2mTransportContextServer context, LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer) {
this.context = context;
this.lwM2mClientContext = lwM2mClientContext;
this.leshanServer = leshanServer;
}
@PostConstruct @PostConstruct
public void init() { public void init() {
@ -101,32 +104,22 @@ public class LwM2mTransportRequest {
new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL))); new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL)));
} }
public Collection<Registration> doGetRegistrations(LeshanServer lwServer) {
Collection<Registration> registrations = new ArrayList<>();
for (Iterator<Registration> iterator = lwServer.getRegistrationService().getAllRegistrations(); iterator
.hasNext(); ) {
registrations.add(iterator.next());
}
return registrations;
}
/** /**
* Device management and service enablement, including Read, Write, Execute, Discover, Create, Delete and Write-Attributes * Device management and service enablement, including Read, Write, Execute, Discover, Create, Delete and Write-Attributes
* *
* @param lwServer -
* @param registration - * @param registration -
* @param target - * @param target -
* @param typeOper - * @param typeOper -
* @param contentFormatParam - * @param contentFormatParam -
* @param observation - * @param observation -
*/ */
public void sendAllRequest(LeshanServer lwServer, Registration registration, String target, String typeOper, public void sendAllRequest(Registration registration, String target, String typeOper,
String contentFormatParam, Observation observation, Object params, long timeoutInMs) { String contentFormatParam, Observation observation, Object params, long timeoutInMs) {
LwM2mPath resultIds = new LwM2mPath(target); LwM2mPath resultIds = new LwM2mPath(target);
if (registration != null && resultIds.getObjectId() >= 0) { if (registration != null && resultIds.getObjectId() >= 0) {
DownlinkRequest request = null; DownlinkRequest request = null;
ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null; ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null;
ResourceModel resource = service.context.getLwM2MTransportConfigServer().getResourceModel(registration, resultIds); ResourceModel resource = serviceImpl.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getResourceModel(registration, resultIds);
timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
switch (typeOper) { switch (typeOper) {
case GET_TYPE_OPER_READ: case GET_TYPE_OPER_READ:
@ -156,7 +149,7 @@ public class LwM2mTransportRequest {
break; break;
case POST_TYPE_OPER_WRITE_REPLACE: case POST_TYPE_OPER_WRITE_REPLACE:
// Request to write a <b>String Single-Instance Resource</b> using the TLV content format. // Request to write a <b>String Single-Instance Resource</b> using the TLV content format.
if (resource != null) { if (resource != null && contentFormat != null) {
if (contentFormat.equals(ContentFormat.TLV) && !resource.multiple) { if (contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {
request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resource.type, registration); request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resource.type, registration);
} }
@ -168,8 +161,8 @@ public class LwM2mTransportRequest {
break; break;
case PUT_TYPE_OPER_WRITE_UPDATE: case PUT_TYPE_OPER_WRITE_UPDATE:
if (resultIds.getResourceId() >= 0) { if (resultIds.getResourceId() >= 0) {
ResourceModel resourceModel = lwServer.getModelProvider().getObjectModel(registration).getObjectModel(resultIds.getObjectId()).resources.get(resultIds.getResourceId()); // ResourceModel resourceModel = leshanServer.getModelProvider().getObjectModel(registration).getObjectModel(resultIds.getObjectId()).resources.get(resultIds.getResourceId());
ResourceModel.Type typeRes = resourceModel.type; // ResourceModel.Type typeRes = resourceModel.type;
LwM2mNode node = LwM2mSingleResource.newStringResource(resultIds.getResourceId(), (String) this.converter.convertValue(params, resource.type, ResourceModel.Type.STRING, resultIds)); LwM2mNode node = LwM2mSingleResource.newStringResource(resultIds.getResourceId(), (String) this.converter.convertValue(params, resource.type, ResourceModel.Type.STRING, resultIds));
request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, target, node); request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, target, node);
} }
@ -206,7 +199,7 @@ public class LwM2mTransportRequest {
* Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60"); * Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60");
* Attribute [] attrs = {gt, st}; * Attribute [] attrs = {gt, st};
*/ */
Attribute pmin = new Attribute(MINIMUM_PERIOD, Integer.toUnsignedLong(Integer.valueOf("1"))); Attribute pmin = new Attribute(MINIMUM_PERIOD, Integer.toUnsignedLong(Integer.parseInt("1")));
Attribute[] attrs = {pmin}; Attribute[] attrs = {pmin};
AttributeSet attrSet = new AttributeSet(attrs); AttributeSet attrSet = new AttributeSet(attrs);
if (resultIds.isResource()) { if (resultIds.isResource()) {
@ -221,25 +214,24 @@ public class LwM2mTransportRequest {
} }
if (request != null) { if (request != null) {
this.sendRequest(lwServer, registration, request, timeoutInMs); this.sendRequest(registration, request, timeoutInMs);
} }
} }
} }
/** /**
* *
* @param lwServer -
* @param registration - * @param registration -
* @param request - * @param request -
* @param timeoutInMs - * @param timeoutInMs -
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void sendRequest(LeshanServer lwServer, Registration registration, DownlinkRequest request, long timeoutInMs) { private void sendRequest(Registration registration, DownlinkRequest request, long timeoutInMs) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
lwServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> { leshanServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
if (!lwM2MClient.isInit()) { if (!lwM2MClient.isInit()) {
lwM2MClient.initValue(this.service, request.getPath().toString()); lwM2MClient.initValue(this.serviceImpl, request.getPath().toString());
} }
if (isSuccess(((Response) response.getCoapResponse()).getCode())) { if (isSuccess(((Response) response.getCoapResponse()).getCode())) {
this.handleResponse(registration, request.getPath().toString(), response, request); this.handleResponse(registration, request.getPath().toString(), response, request);
@ -247,23 +239,23 @@ public class LwM2mTransportRequest {
String msg = String.format("%s: sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client", String msg = String.format("%s: sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client",
LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(), LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString(),
((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue().toString()); ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue().toString());
service.sentLogsToThingsboard(msg, registration); serviceImpl.sentLogsToThingsboard(msg, registration);
log.info("[{}] [{}] - [{}] [{}] Update SendRequest[{}]", registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(), log.info("[{}] [{}] - [{}] [{}] Update SendRequest[{}]", registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(),
((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue()); ((LwM2mSingleResource) ((WriteRequest) request).getNode()).getValue());
} }
} else { } else {
String msg = String.format("%s: sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", LOG_LW2M_ERROR, String msg = String.format("%s: sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", LOG_LW2M_ERROR,
((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
service.sentLogsToThingsboard(msg, registration); serviceImpl.sentLogsToThingsboard(msg, registration);
log.error("[{}] - [{}] [{}] error SendRequest", ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); log.error("[{}], [{}] - [{}] [{}] error SendRequest", registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
} }
}, e -> { }, e -> {
if (!lwM2MClient.isInit()) { if (!lwM2MClient.isInit()) {
lwM2MClient.initValue(this.service, request.getPath().toString()); lwM2MClient.initValue(this.serviceImpl, request.getPath().toString());
} }
String msg = String.format("%s: sendRequest: Resource path - %s msg error - %s SendRequest to Client", String msg = String.format("%s: sendRequest: Resource path - %s msg error - %s SendRequest to Client",
LOG_LW2M_ERROR, request.getPath().toString(), e.toString()); LOG_LW2M_ERROR, request.getPath().toString(), e.toString());
service.sentLogsToThingsboard(msg, registration); serviceImpl.sentLogsToThingsboard(msg, registration);
log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString()); log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString());
}); });
@ -275,15 +267,17 @@ public class LwM2mTransportRequest {
case STRING: // String case STRING: // String
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, value.toString()) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, value.toString()); return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, value.toString()) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, value.toString());
case INTEGER: // Long case INTEGER: // Long
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Integer.toUnsignedLong(Integer.valueOf(value.toString()))) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Integer.toUnsignedLong(Integer.valueOf(value.toString()))); final long valueInt = Integer.toUnsignedLong(Integer.parseInt(value.toString()));
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueInt) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueInt);
case OBJLNK: // ObjectLink case OBJLNK: // ObjectLink
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString())); return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString()));
case BOOLEAN: // Boolean case BOOLEAN: // Boolean
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Boolean.valueOf(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Boolean.valueOf(value.toString())); return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Boolean.parseBoolean(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Boolean.parseBoolean(value.toString()));
case FLOAT: // Double case FLOAT: // Double
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Double.valueOf(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Double.valueOf(value.toString())); return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Double.parseDouble(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Double.parseDouble(value.toString()));
case TIME: // Date case TIME: // Date
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, new Date(Long.decode(value.toString()))) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, new Date((Long) Integer.toUnsignedLong(Integer.valueOf(value.toString())))); Date date = new Date(Long.decode(value.toString()));
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date);
case OPAQUE: // byte[] value, base64 case OPAQUE: // byte[] value, base64
return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())); return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray()));
default: default:
@ -293,7 +287,7 @@ public class LwM2mTransportRequest {
String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
patn, type, value, e.toString()); patn, type, value, e.toString());
service.sentLogsToThingsboard(msg, registration); serviceImpl.sentLogsToThingsboard(msg, registration);
log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
return null; return null;
} }
@ -317,7 +311,7 @@ public class LwM2mTransportRequest {
*/ */
private void sendResponse(Registration registration, String path, LwM2mResponse response, DownlinkRequest request) { private void sendResponse(Registration registration, String path, LwM2mResponse response, DownlinkRequest request) {
if (response instanceof ReadResponse) { if (response instanceof ReadResponse) {
service.onObservationResponse(registration, path, (ReadResponse) response); serviceImpl.onObservationResponse(registration, path, (ReadResponse) response);
} else if (response instanceof CancelObservationResponse) { } else if (response instanceof CancelObservationResponse) {
log.info("[{}] Path [{}] CancelObservationResponse 3_Send", path, response); log.info("[{}] Path [{}] CancelObservationResponse 3_Send", path, response);
} else if (response instanceof DeleteResponse) { } else if (response instanceof DeleteResponse) {
@ -330,7 +324,7 @@ public class LwM2mTransportRequest {
log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", path, response); log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", path, response);
} else if (response instanceof WriteResponse) { } else if (response instanceof WriteResponse) {
log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", path, response); log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", path, response);
service.onWriteResponseOk(registration, path, (WriteRequest) request); serviceImpl.onWriteResponseOk(registration, path, (WriteRequest) request);
} }
} }
} }

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

@ -33,7 +33,7 @@ public class LwM2mTransportServerInitializer {
@Autowired @Autowired
private LwM2mTransportServiceImpl service; private LwM2mTransportServiceImpl service;
@Autowired(required = false) @Autowired
private LeshanServer leshanServer; private LeshanServer leshanServer;
@Autowired @Autowired
@ -49,7 +49,7 @@ public class LwM2mTransportServerInitializer {
private void startLhServer() { private void startLhServer() {
this.leshanServer.start(); this.leshanServer.start();
LwM2mServerListener lhServerCertListener = new LwM2mServerListener(this.leshanServer, service); LwM2mServerListener lhServerCertListener = new LwM2mServerListener(service);
this.leshanServer.getRegistrationService().addListener(lhServerCertListener.registrationListener); this.leshanServer.getRegistrationService().addListener(lhServerCertListener.registrationListener);
this.leshanServer.getPresenceService().addListener(lhServerCertListener.presenceListener); this.leshanServer.getPresenceService().addListener(lhServerCertListener.presenceListener);
this.leshanServer.getObservationService().addListener(lhServerCertListener.observationListener); this.leshanServer.getObservationService().addListener(lhServerCertListener.observationListener);

13
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java

@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.server;
import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.observation.Observation;
import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.server.californium.LeshanServer;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
@ -28,17 +27,17 @@ import java.util.Optional;
public interface LwM2mTransportService { public interface LwM2mTransportService {
void onRegistered(LeshanServer lwServer, Registration registration, Collection<Observation> previousObsersations); void onRegistered(Registration registration, Collection<Observation> previousObsersations);
void updatedReg(LeshanServer lwServer, Registration registration); void updatedReg(Registration registration);
void unReg(LeshanServer lwServer, Registration registration, Collection<Observation> observations); void unReg(Registration registration, Collection<Observation> observations);
void onSleepingDev(Registration registration); void onSleepingDev(Registration registration);
void setCancelObservations(LeshanServer lwServer, Registration registration); void setCancelObservations(Registration registration);
void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path); void setCancelObservationRecourse(Registration registration, String path);
void onObservationResponse(Registration registration, String path, ReadResponse response); void onObservationResponse(Registration registration, String path, ReadResponse response);
@ -48,7 +47,7 @@ public interface LwM2mTransportService {
void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt); void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt);
void doTrigger(LeshanServer lwServer, Registration registration, String path); void doTrigger(Registration registration, String path);
void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);

248
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java

@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Sets;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
@ -35,7 +36,7 @@ import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.core.util.NamedThreadFactory;
import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.californium.LeshanServer;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
@ -105,29 +106,32 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
protected final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); protected final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
protected final Lock writeLock = readWriteLock.writeLock(); protected final Lock writeLock = readWriteLock.writeLock();
@Autowired private final TransportService transportService;
private TransportService transportService;
@Autowired public final LwM2mTransportContextServer lwM2mTransportContextServer;
public LwM2mTransportContextServer context;
@Autowired private final LwM2mClientContext lwM2mClientContext;
private LwM2mTransportRequest lwM2mTransportRequest;
@Autowired private final LeshanServer leshanServer;
private LwM2mClientContext lwM2mClientContext;
@Autowired(required = false) private final LwM2mTransportRequest lwM2mTransportRequest;
private LeshanServer leshanServer;
public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer, LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, @Lazy LwM2mTransportRequest lwM2mTransportRequest) {
this.transportService = transportService;
this.lwM2mTransportContextServer = lwM2mTransportContextServer;
this.lwM2mClientContext = lwM2mClientContext;
this.leshanServer = leshanServer;
this.lwM2mTransportRequest = lwM2mTransportRequest;
}
@PostConstruct @PostConstruct
public void init() { public void init() {
this.context.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) context.getLwM2MTransportConfigServer().getSessionReportTimeout()), context.getLwM2MTransportConfigServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS); this.lwM2mTransportContextServer.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) lwM2mTransportContextServer.getLwM2MTransportConfigServer().getSessionReportTimeout()), lwM2mTransportContextServer.getLwM2MTransportConfigServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS);
this.executorRegistered = Executors.newFixedThreadPool(this.context.getLwM2MTransportConfigServer().getRegisteredPoolSize(), this.executorRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getRegisteredPoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL))); new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL)));
this.executorUpdateRegistered = Executors.newFixedThreadPool(this.context.getLwM2MTransportConfigServer().getUpdateRegisteredPoolSize(), this.executorUpdateRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getUpdateRegisteredPoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL))); new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL)));
this.executorUnRegistered = Executors.newFixedThreadPool(this.context.getLwM2MTransportConfigServer().getUnRegisteredPoolSize(), this.executorUnRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getUnRegisteredPoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL))); new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL)));
this.converter = LwM2mValueConverterImpl.getInstance(); this.converter = LwM2mValueConverterImpl.getInstance();
} }
@ -143,17 +147,15 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* 1.2 Remove from sessions Model by enpPoint * 1.2 Remove from sessions Model by enpPoint
* Next -> Create new LwM2MClient for current session -> setModelClient... * Next -> Create new LwM2MClient for current session -> setModelClient...
* *
* @param lwServer - LeshanServer
* @param registration - Registration LwM2M Client * @param registration - Registration LwM2M Client
* @param previousObsersations - may be null * @param previousObsersations - may be null
*/ */
public void onRegistered(LeshanServer lwServer, Registration registration, Collection<Observation> previousObsersations) { public void onRegistered(Registration registration, Collection<Observation> previousObsersations) {
executorRegistered.submit(() -> { executorRegistered.submit(() -> {
try { try {
log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
LwM2mClient lwM2MClient = this.lwM2mClientContext.updateInSessionsLwM2MClient(registration); LwM2mClient lwM2MClient = this.lwM2mClientContext.updateInSessionsLwM2MClient(registration);
if (lwM2MClient != null) { if (lwM2MClient != null) {
this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration);
SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
if (sessionInfo != null) { if (sessionInfo != null) {
lwM2MClient.setDeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); lwM2MClient.setDeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
@ -163,9 +165,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo));
transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration); this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration);
this.initLwM2mFromClientValue(lwServer, registration, lwM2MClient); this.initLwM2mFromClientValue(registration, lwM2MClient);
} else { } else {
log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
} }
@ -181,10 +182,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
/** /**
* if sessionInfo removed from sessions, then new registerAsyncSession * if sessionInfo removed from sessions, then new registerAsyncSession
* *
* @param lwServer - LeshanServer
* @param registration - Registration LwM2M Client * @param registration - Registration LwM2M Client
*/ */
public void updatedReg(LeshanServer lwServer, Registration registration) { public void updatedReg(Registration registration) {
executorUpdateRegistered.submit(() -> { executorUpdateRegistered.submit(() -> {
try { try {
SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
@ -205,10 +205,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* @param observations - All paths observations before unReg * @param observations - All paths observations before unReg
* !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
*/ */
public void unReg(LeshanServer lwServer, Registration registration, Collection<Observation> observations) { public void unReg(Registration registration, Collection<Observation> observations) {
executorUnRegistered.submit(() -> { executorUnRegistered.submit(() -> {
try { try {
this.setCancelObservations(lwServer, registration); this.setCancelObservations(registration);
this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration);
this.closeClientSession(registration); this.closeClientSession(registration);
} catch (Throwable t) { } catch (Throwable t) {
@ -238,10 +238,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
} }
@Override @Override
public void setCancelObservations(LeshanServer lwServer, Registration registration) { public void setCancelObservations(Registration registration) {
if (registration != null) { if (registration != null) {
Set<Observation> observations = lwServer.getObservationService().getObservations(registration); Set<Observation> observations = leshanServer.getObservationService().getObservations(registration);
observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString())); observations.forEach(observation -> this.setCancelObservationRecourse(registration, observation.getPath().toString()));
} }
} }
@ -251,8 +251,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* {@code ObservationService#cancelObservation()} * {@code ObservationService#cancelObservation()}
*/ */
@Override @Override
public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) { public void setCancelObservationRecourse(Registration registration, String path) {
lwServer.getObservationService().cancelObservations(registration, path); leshanServer.getObservationService().cancelObservations(registration, path);
} }
/** /**
@ -294,12 +294,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
String path = this.getPathAttributeUpdate(sessionInfo, de.getKey()); String path = this.getPathAttributeUpdate(sessionInfo, de.getKey());
String value = de.getValue().getAsString(); String value = de.getValue().getAsString();
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
LwM2mClientProfile profile = lwM2mClientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); LwM2mClientProfile clientProfile = lwM2mClientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
ResourceModel resourceModel = context.getLwM2MTransportConfigServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path)); if (path != null && !path.isEmpty() && (this.validatePathInAttrProfile(clientProfile, path) || this.validatePathInTelemetryProfile(clientProfile, path))) {
if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) { ResourceModel resourceModel = lwM2mTransportContextServer.getLwM2MTransportConfigServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path));
if (resourceModel != null && resourceModel.operations.isWritable()) { if (resourceModel != null && resourceModel.operations.isWritable()) {
lwM2mTransportRequest.sendAllRequest(leshanServer, lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
ContentFormat.TLV.getName(), null, value, this.context.getLwM2MTransportConfigServer().getTimeout()); ContentFormat.TLV.getName(), null, value, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout());
} else { } else {
log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value); log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);
String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
@ -353,9 +353,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* Trigger bootStrap path = "/1/0/9" - have to implemented on client * Trigger bootStrap path = "/1/0/9" - have to implemented on client
*/ */
@Override @Override
public void doTrigger(LeshanServer lwServer, Registration registration, String path) { public void doTrigger(Registration registration, String path) {
lwM2mTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE, lwM2mTransportRequest.sendAllRequest(registration, path, POST_TYPE_OPER_EXECUTE,
ContentFormat.TLV.getName(), null, null, this.context.getLwM2MTransportConfigServer().getTimeout()); ContentFormat.TLV.getName(), null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout());
} }
/** /**
@ -438,7 +438,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) { public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) {
SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
if (sessionInfo != null) { if (sessionInfo != null) {
context.sentParametersOnThingsboard(msg, topicName, sessionInfo); lwM2mTransportContextServer.sentParametersOnThingsboard(msg, topicName, sessionInfo);
} else { } else {
log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null); log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null);
} }
@ -455,30 +455,29 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* - Request to the client after registration to read all resource values for all objects * - Request to the client after registration to read all resource values for all objects
* - then Observe Request to the client marked as observe from the profile configuration. * - then Observe Request to the client marked as observe from the profile configuration.
* *
* @param lwServer - LeshanServer
* @param registration - Registration LwM2M Client * @param registration - Registration LwM2M Client
* @param lwM2MClient - object with All parameters off client * @param lwM2MClient - object with All parameters off client
*/ */
private void initLwM2mFromClientValue(LeshanServer lwServer, Registration registration, LwM2mClient lwM2MClient) { private void initLwM2mFromClientValue(Registration registration, LwM2mClient lwM2MClient) {
LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration); LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration);
Set<String> clientObjects = this.getAllOjectsInClient(registration); Set<String> clientObjects = this.getAllOjectsInClient(registration);
if (clientObjects != null && !LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { if (clientObjects != null && !LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
// #2 // #2
if (!LwM2mTransportHandler.getClientUpdateValueAfterConnect(lwM2MClientProfile)) { if (!LwM2mTransportHandler.getClientUpdateValueAfterConnect(lwM2MClientProfile)) {
this.initReadAttrTelemetryObserveToClient(lwServer, registration, lwM2MClient, GET_TYPE_OPER_READ); this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, GET_TYPE_OPER_READ);
} }
// #3 // #3
else { else {
lwM2MClient.getPendingRequests().addAll(clientObjects); lwM2MClient.getPendingRequests().addAll(clientObjects);
clientObjects.forEach(path -> { clientObjects.forEach(path -> {
lwM2mTransportRequest.sendAllRequest(lwServer, registration, path, GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), lwM2mTransportRequest.sendAllRequest(registration, path, GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
null, null, this.context.getLwM2MTransportConfigServer().getTimeout()); null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout());
}); });
} }
} }
// #1 // #1
this.initReadAttrTelemetryObserveToClient(lwServer, registration, lwM2MClient, GET_TYPE_OPER_OBSERVE); this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, GET_TYPE_OPER_OBSERVE);
} }
/** /**
@ -513,8 +512,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* #2 Update new Resources (replace old Resource Value on new Resource Value) * #2 Update new Resources (replace old Resource Value on new Resource Value)
* *
* @param registration - Registration LwM2M Client * @param registration - Registration LwM2M Client
* @param - LwM2mSingleResource response.getContent() * @param lwM2mResource - LwM2mSingleResource response.getContent()
* @param - LwM2mSingleResource response.getContent()
* @param path - resource * @param path - resource
*/ */
private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) { private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
@ -522,31 +520,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
lwM2MClient.updateResourceValue(path, lwM2mResource); lwM2MClient.updateResourceValue(path, lwM2mResource);
Set<String> paths = new HashSet<>(); Set<String> paths = new HashSet<>();
paths.add(path); paths.add(path);
this.updateAttrTelemetry(registration, false, paths); this.updateAttrTelemetry(registration, paths);
} }
/** /**
* Sent Attribute and Telemetry to Thingsboard * Sent Attribute and Telemetry to Thingsboard
* #1 - get AttrName/TelemetryName with value: * #1 - get AttrName/TelemetryName with value from LwM2MClient:
* #1.1 from Client
* #1.2 from LwM2MClient:
* -- resourceId == path from LwM2MClientProfile.postAttributeProfile/postTelemetryProfile/postObserveProfile * -- resourceId == path from LwM2MClientProfile.postAttributeProfile/postTelemetryProfile/postObserveProfile
* -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId) * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)
* #2 - set Attribute/Telemetry * #2 - set Attribute/Telemetry
* *
* @param registration - Registration LwM2M Client * @param registration - Registration LwM2M Client
*/ */
private void updateAttrTelemetry(Registration registration, boolean start, Set<String> paths) { private void updateAttrTelemetry(Registration registration, Set<String> paths) {
JsonObject attributes = new JsonObject(); JsonObject attributes = new JsonObject();
JsonObject telemetries = new JsonObject(); JsonObject telemetries = new JsonObject();
if (start) {
// #1.1
JsonObject attributeClient = this.getAttributeClient(registration);
if (attributeClient != null) {
attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue()));
}
}
// #1.2
try { try {
writeLock.lock(); writeLock.lock();
this.getParametersFromProfile(attributes, telemetries, registration, paths); this.getParametersFromProfile(attributes, telemetries, registration, paths);
@ -562,25 +550,35 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
} }
/** /**
* @param profile - * @param clientProfile -
* @param path - * @param path -
* @return true if path isPresent in postAttributeProfile * @return true if path isPresent in postAttributeProfile
*/ */
private boolean validatePathInAttrProfile(LwM2mClientProfile profile, String path) { private boolean validatePathInAttrProfile(LwM2mClientProfile clientProfile, String path) {
Set<String> attributesSet = new Gson().fromJson(profile.getPostAttributeProfile(), new TypeToken<>() { try {
}.getType()); List<String> attributesSet = new Gson().fromJson(clientProfile.getPostAttributeProfile(), new TypeToken<>() {
return attributesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent(); }.getType());
return attributesSet.stream().anyMatch(p -> p.equals(path));
} catch (Exception e) {
log.error("Fail Validate Path [{}] ClientProfile.Attribute", path, e);
return false;
}
} }
/** /**
* @param profile - * @param clientProfile -
* @param path - * @param path -
* @return true if path isPresent in postAttributeProfile * @return true if path isPresent in postAttributeProfile
*/ */
private boolean validatePathInTelemetryProfile(LwM2mClientProfile profile, String path) { private boolean validatePathInTelemetryProfile(LwM2mClientProfile clientProfile, String path) {
Set<String> telemetriesSet = new Gson().fromJson(profile.getPostTelemetryProfile(), new TypeToken<>() { try {
}.getType()); List<String> telemetriesSet = new Gson().fromJson(clientProfile.getPostTelemetryProfile(), new TypeToken<>() {
return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent(); }.getType());
return telemetriesSet.stream().anyMatch(p -> p.equals(path));
} catch (Exception e) {
log.error("Fail Validate Path [{}] ClientProfile.Telemetry", path, e);
return false;
}
} }
/** /**
@ -588,10 +586,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* #1 - Analyze: * #1 - Analyze:
* #1.1 path in resource profile == client resource * #1.1 path in resource profile == client resource
* *
* @param lwServer -
* @param registration - * @param registration -
*/ */
private void initReadAttrTelemetryObserveToClient(LeshanServer lwServer, Registration registration, LwM2mClient lwM2MClient, String typeOper) { private void initReadAttrTelemetryObserveToClient(Registration registration, LwM2mClient lwM2MClient, String typeOper) {
try { try {
LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration); LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration);
Set<String> clientInstances = this.getAllInstancesInClient(registration); Set<String> clientInstances = this.getAllInstancesInClient(registration);
@ -606,19 +603,18 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
}); });
} }
Set<String> pathSent = ConcurrentHashMap.newKeySet(); Set<String> pathSent = ConcurrentHashMap.newKeySet();
result.forEach(p -> { result.forEach(target -> {
// #1.1 // #1.1
String target = p;
String[] resPath = target.split("/"); String[] resPath = target.split("/");
String instance = "/" + resPath[1] + "/" + resPath[2]; String instance = "/" + resPath[1] + "/" + resPath[2];
if (clientInstances.contains(instance)) { if (clientInstances != null && clientInstances.size() > 0 && clientInstances.contains(instance)) {
pathSent.add(target); pathSent.add(target);
} }
}); });
lwM2MClient.getPendingRequests().addAll(pathSent); lwM2MClient.getPendingRequests().addAll(pathSent);
pathSent.forEach(target -> { pathSent.forEach(target -> {
lwM2mTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, ContentFormat.TLV.getName(), lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),
null, null, this.context.getLwM2MTransportConfigServer().getTimeout()); null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout());
}); });
if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) { if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
lwM2MClient.initValue(this, null); lwM2MClient.initValue(this, null);
@ -677,26 +673,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
return (clientInstances.size() > 0) ? clientInstances : null; return (clientInstances.size() > 0) ? clientInstances : null;
} }
/** // /**
* get AttrName/TelemetryName with value from Client // * get AttrName/TelemetryName with value from Client
* // *
* @param registration - // * @param registration -
* @return - JsonObject, format: {name: value}} // * @return - JsonObject, format: {name: value}}
*/ // */
private JsonObject getAttributeClient(Registration registration) { // private JsonObject getAttributeClient(Registration registration) {
if (registration.getAdditionalRegistrationAttributes().size() > 0) { // if (registration.getAdditionalRegistrationAttributes().size() > 0) {
JsonObject resNameValues = new JsonObject(); // JsonObject resNameValues = new JsonObject();
registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty); // registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty);
return resNameValues; // return resNameValues;
} // }
return null; // return null;
} // }
/** /**
* @param attributes - new JsonObject * @param attributes - new JsonObject
* @param telemetry - new JsonObject * @param telemetry - new JsonObject
* @param registration - Registration LwM2M Client * @param registration - Registration LwM2M Client
* @param path * @param path -
*/ */
private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) { private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) {
LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration); LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration);
@ -746,7 +742,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
LwM2mPath pathIds = new LwM2mPath(path); LwM2mPath pathIds = new LwM2mPath(path);
ResourceValue resourceValue = this.returnResourceValueFromLwM2MClient(lwM2MClient, pathIds); ResourceValue resourceValue = this.returnResourceValueFromLwM2MClient(lwM2MClient, pathIds);
return resourceValue == null ? null : return resourceValue == null ? null :
this.converter.convertValue(resourceValue.getResourceValue(), this.context.getLwM2MTransportConfigServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds).toString(); this.converter.convertValue(resourceValue.getResourceValue(), this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds).toString();
} }
/** /**
@ -796,25 +792,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* @param deviceProfile - * @param deviceProfile -
*/ */
private void onDeviceUpdateChangeProfile(Set<String> registrationIds, DeviceProfile deviceProfile) { private void onDeviceUpdateChangeProfile(Set<String> registrationIds, DeviceProfile deviceProfile) {
LwM2mClientProfile lwM2MClientProfileOld = lwM2mClientContext.getProfiles().get(deviceProfile.getUuidId()); LwM2mClientProfile lwM2MClientProfileOld = lwM2mClientContext.getProfiles().get(deviceProfile.getUuidId()).clone();
if (lwM2mClientContext.addUpdateProfileParameters(deviceProfile)) { if (lwM2mClientContext.addUpdateProfileParameters(deviceProfile)) {
// #1 // #1
JsonArray attributeOld = lwM2MClientProfileOld.getPostAttributeProfile(); JsonArray attributeOld = lwM2MClientProfileOld.getPostAttributeProfile();
Set<String> attributeSetOld = new Gson().fromJson(attributeOld, new TypeToken<>() { Set attributeSetOld = this.convertJsonArrayToSet (attributeOld);
}.getType());
JsonArray telemetryOld = lwM2MClientProfileOld.getPostTelemetryProfile(); JsonArray telemetryOld = lwM2MClientProfileOld.getPostTelemetryProfile();
Set<String> telemetrySetOld = new Gson().fromJson(telemetryOld, new TypeToken<>() { Set telemetrySetOld = this.convertJsonArrayToSet (telemetryOld);
}.getType());
JsonArray observeOld = lwM2MClientProfileOld.getPostObserveProfile(); JsonArray observeOld = lwM2MClientProfileOld.getPostObserveProfile();
JsonObject keyNameOld = lwM2MClientProfileOld.getPostKeyNameProfile(); JsonObject keyNameOld = lwM2MClientProfileOld.getPostKeyNameProfile();
LwM2mClientProfile lwM2MClientProfileNew = lwM2mClientContext.getProfiles().get(deviceProfile.getUuidId()); LwM2mClientProfile lwM2MClientProfileNew = lwM2mClientContext.getProfiles().get(deviceProfile.getUuidId());
JsonArray attributeNew = lwM2MClientProfileNew.getPostAttributeProfile(); JsonArray attributeNew = lwM2MClientProfileNew.getPostAttributeProfile();
Set<String> attributeSetNew = new Gson().fromJson(attributeNew, new TypeToken<>() { Set<String> attributeSetNew = this.convertJsonArrayToSet (attributeNew);
}.getType());
JsonArray telemetryNew = lwM2MClientProfileNew.getPostTelemetryProfile(); JsonArray telemetryNew = lwM2MClientProfileNew.getPostTelemetryProfile();
Set<String> telemetrySetNew = new Gson().fromJson(telemetryNew, new TypeToken<>() { Set telemetrySetNew = this.convertJsonArrayToSet (telemetryNew);
}.getType());
JsonArray observeNew = lwM2MClientProfileNew.getPostObserveProfile(); JsonArray observeNew = lwM2MClientProfileNew.getPostObserveProfile();
JsonObject keyNameNew = lwM2MClientProfileNew.getPostKeyNameProfile(); JsonObject keyNameNew = lwM2MClientProfileNew.getPostKeyNameProfile();
@ -847,11 +839,11 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) { if (sentAttrToThingsboard.getPathPostParametersAdd().size() > 0) {
// update value in Resources // update value in Resources
registrationIds.forEach(registrationId -> { registrationIds.forEach(registrationId -> {
LeshanServer lwServer = leshanServer; // LeshanServer lwServer = leshanServer;
Registration registration = lwM2mClientContext.getRegistration(registrationId); Registration registration = lwM2mClientContext.getRegistration(registrationId);
this.readResourceValueObserve(lwServer, registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ); this.readResourceValueObserve(registration, sentAttrToThingsboard.getPathPostParametersAdd(), GET_TYPE_OPER_READ);
// sent attr/telemetry to tingsboard for new path // sent attr/telemetry to tingsboard for new path
this.updateAttrTelemetry(registration, false, sentAttrToThingsboard.getPathPostParametersAdd()); this.updateAttrTelemetry(registration, sentAttrToThingsboard.getPathPostParametersAdd());
}); });
} }
// #4.2 del // #4.2 del
@ -862,10 +854,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
// #5.1 // #5.1
if (!observeOld.equals(observeNew)) { if (!observeOld.equals(observeNew)) {
Set<String> observeSetOld = new Gson().fromJson(observeOld, new TypeToken<>() { Set<String> observeSetOld = new Gson().fromJson(observeOld, new TypeToken<>() {}.getType());
}.getType()); Set<String> observeSetNew = new Gson().fromJson(observeNew, new TypeToken<>() {}.getType());
Set<String> observeSetNew = new Gson().fromJson(observeNew, new TypeToken<>() {
}.getType());
//#5.2 add //#5.2 add
// path Attr/Telemetry includes newObserve // path Attr/Telemetry includes newObserve
attributeSetOld.addAll(telemetrySetOld); attributeSetOld.addAll(telemetrySetOld);
@ -876,17 +866,22 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd()); ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sentObserveToClientOld.getPathPostParametersAdd(), sentObserveToClientNew.getPathPostParametersAdd());
// sent Request observe to Client // sent Request observe to Client
registrationIds.forEach(registrationId -> { registrationIds.forEach(registrationId -> {
LeshanServer lwServer = leshanServer;
Registration registration = lwM2mClientContext.getRegistration(registrationId); Registration registration = lwM2mClientContext.getRegistration(registrationId);
this.readResourceValueObserve(lwServer, registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE); this.readResourceValueObserve(registration, postObserveAnalyzer.getPathPostParametersAdd(), GET_TYPE_OPER_OBSERVE);
// 5.3 del // 5.3 del
// sent Request cancel observe to Client // sent Request cancel observe to Client
this.cancelObserveIsValue(lwServer, registration, postObserveAnalyzer.getPathPostParametersDel()); this.cancelObserveIsValue(registration, postObserveAnalyzer.getPathPostParametersDel());
}); });
} }
} }
} }
private Set <String> convertJsonArrayToSet (JsonArray jsonArray) {
List<String> attributeListOld = new Gson().fromJson(jsonArray, new TypeToken<>() {
}.getType());
return Sets.newConcurrentHashSet(attributeListOld);
}
/** /**
* Compare old list with new list after change AttrTelemetryObserve in config Profile * Compare old list with new list after change AttrTelemetryObserve in config Profile
* *
@ -917,20 +912,19 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* Update Resource value after change RezAttrTelemetry in config Profile * Update Resource value after change RezAttrTelemetry in config Profile
* sent response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue() * sent response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue()
* *
* @param lwServer - LeshanServer
* @param registration - Registration LwM2M Client * @param registration - Registration LwM2M Client
* @param targets - path Resources == [ "/2/0/0", "/2/0/1"] * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
*/ */
private void readResourceValueObserve(LeshanServer lwServer, Registration registration, Set<String> targets, String typeOper) { private void readResourceValueObserve(Registration registration, Set<String> targets, String typeOper) {
targets.forEach(target -> { targets.forEach(target -> {
LwM2mPath pathIds = new LwM2mPath(target); LwM2mPath pathIds = new LwM2mPath(target);
if (pathIds.isResource()) { if (pathIds.isResource()) {
if (GET_TYPE_OPER_READ.equals(typeOper)) { if (GET_TYPE_OPER_READ.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, lwM2mTransportRequest.sendAllRequest(registration, target, typeOper,
ContentFormat.TLV.getName(), null, null, this.context.getLwM2MTransportConfigServer().getTimeout()); ContentFormat.TLV.getName(), null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout());
} else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) { } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
lwM2mTransportRequest.sendAllRequest(lwServer, registration, target, typeOper, lwM2mTransportRequest.sendAllRequest(registration, target, typeOper,
null, null, null, this.context.getLwM2MTransportConfigServer().getTimeout()); null, null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout());
} }
} }
}); });
@ -946,11 +940,11 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
return analyzerParameters; return analyzerParameters;
} }
private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set<String> paramAnallyzer) { private void cancelObserveIsValue(Registration registration, Set<String> paramAnallyzer) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
paramAnallyzer.forEach(p -> { paramAnallyzer.forEach(p -> {
if (this.returnResourceValueFromLwM2MClient(lwM2MClient, new LwM2mPath(p)) != null) { if (this.returnResourceValueFromLwM2MClient(lwM2MClient, new LwM2mPath(p)) != null) {
this.setCancelObservationRecourse(lwServer, registration, p); this.setCancelObservationRecourse(registration, p);
} }
} }
); );
@ -958,8 +952,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
private void putDelayedUpdateResourcesClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) { private void putDelayedUpdateResourcesClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) {
if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) { if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) {
lwM2mTransportRequest.sendAllRequest(leshanServer, lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE, lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
ContentFormat.TLV.getName(), null, valueNew, this.context.getLwM2MTransportConfigServer().getTimeout()); ContentFormat.TLV.getName(), null, valueNew, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout());
} else { } else {
log.error("05 delayError"); log.error("05 delayError");
} }
@ -1037,7 +1031,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
/** /**
* @param lwM2MClient - * @param lwM2MClient -
* @return * @return SessionInfoProto -
*/ */
private SessionInfoProto getNewSessionInfoProto(LwM2mClient lwM2MClient) { private SessionInfoProto getNewSessionInfoProto(LwM2mClient lwM2MClient) {
if (lwM2MClient != null) { if (lwM2MClient != null) {
@ -1048,7 +1042,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
return null; return null;
} else { } else {
return SessionInfoProto.newBuilder() return SessionInfoProto.newBuilder()
.setNodeId(this.context.getNodeId()) .setNodeId(this.lwM2mTransportContextServer.getNodeId())
.setSessionIdMSB(lwM2MClient.getSessionId().getMostSignificantBits()) .setSessionIdMSB(lwM2MClient.getSessionId().getMostSignificantBits())
.setSessionIdLSB(lwM2MClient.getSessionId().getLeastSignificantBits()) .setSessionIdLSB(lwM2MClient.getSessionId().getLeastSignificantBits())
.setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB()) .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
@ -1114,7 +1108,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
if (attrSharedNames.size() > 0) { if (attrSharedNames.size() > 0) {
//#2.1 //#2.1
try { try {
TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames); TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, attrSharedNames);
transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
} catch (AdaptorException e) { } catch (AdaptorException e) {
log.warn("Failed to decode get attributes request", e); log.warn("Failed to decode get attributes request", e);
@ -1138,8 +1132,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet() ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()
.stream() .stream()
.filter(e -> (attrSet.contains(e.getKey()) && context.getLwM2MTransportConfigServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null && .filter(e -> (attrSet.contains(e.getKey()) && lwM2mTransportContextServer.getLwM2MTransportConfigServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null &&
context.getLwM2MTransportConfigServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable())) lwM2mTransportContextServer.getLwM2MTransportConfigServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable()))
.collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue)); .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
Set<String> namesIsWritable = ConcurrentHashMap.newKeySet(); Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();

28
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server.client; package org.thingsboard.server.transport.lwm2m.server.client;
import com.google.gson.Gson;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import lombok.Data; import lombok.Data;
@ -25,7 +26,7 @@ public class LwM2mClientProfile {
* {"clientLwM2mSettings": { * {"clientLwM2mSettings": {
* clientUpdateValueAfterConnect: false; * clientUpdateValueAfterConnect: false;
* } * }
**/ **/
JsonObject postClientLwM2mSettings; JsonObject postClientLwM2mSettings;
/** /**
@ -34,7 +35,7 @@ public class LwM2mClientProfile {
* "/3/0/0": "manufacturer", * "/3/0/0": "manufacturer",
* "/3/0/2": "serialNumber" * "/3/0/2": "serialNumber"
* } * }
**/ **/
JsonObject postKeyNameProfile; JsonObject postKeyNameProfile;
/** /**
@ -51,4 +52,27 @@ public class LwM2mClientProfile {
* [ "/2/0/0", "/2/0/1"] * [ "/2/0/0", "/2/0/1"]
*/ */
JsonArray postObserveProfile; JsonArray postObserveProfile;
public LwM2mClientProfile clone() {
LwM2mClientProfile lwM2mClientProfile = new LwM2mClientProfile();
lwM2mClientProfile.postClientLwM2mSettings = this.deepCopy(this.postClientLwM2mSettings, JsonObject.class);
lwM2mClientProfile.postKeyNameProfile = this.deepCopy(this.postKeyNameProfile, JsonObject.class);
lwM2mClientProfile.postAttributeProfile = this.deepCopy(this.postAttributeProfile, JsonArray.class);
lwM2mClientProfile.postTelemetryProfile = this.deepCopy(this.postTelemetryProfile, JsonArray.class);
lwM2mClientProfile.postObserveProfile = this.deepCopy(this.postObserveProfile, JsonArray.class);
return lwM2mClientProfile;
}
private <T> T deepCopy(T elements, Class<T> type) {
try {
Gson gson = new Gson();
return gson.fromJson(gson.toJson(elements), type);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
} }

2
pom.xml

@ -65,7 +65,7 @@
<jackson-annotations.version>2.12.1</jackson-annotations.version> <jackson-annotations.version>2.12.1</jackson-annotations.version>
<jackson-core.version>2.12.1</jackson-core.version> <jackson-core.version>2.12.1</jackson-core.version>
<json-schema-validator.version>2.2.6</json-schema-validator.version> <json-schema-validator.version>2.2.6</json-schema-validator.version>
<californium.version>2.6.0</californium.version> <californium.version>2.6.1</californium.version>
<leshan-server.version>1.3.0</leshan-server.version> <leshan-server.version>1.3.0</leshan-server.version>
<leshan-core.version>1.3.0</leshan-core.version> <leshan-core.version>1.3.0</leshan-core.version>
<leshan-client.version>1.3.0</leshan-client.version> <leshan-client.version>1.3.0</leshan-client.version>

Loading…
Cancel
Save