Commit 6cace79b3c4558d9ef3603c72fc5d108eedc611c

Authored by Andrew Shvayka
Committed by GitHub
2 parents a764838e 4f2e7207

Merge pull request #4607 from thingsboard/lwm2m_rpc_fw_sw

Lwm2m: [WIP] Rps Fw Sw
... ... @@ -89,7 +89,7 @@ public class DefaultTbResourceService implements TbResourceService {
89 89 } catch (InvalidDDFFileException | IOException e) {
90 90 throw new ThingsboardException(e, ThingsboardErrorCode.GENERAL);
91 91 }
92   - if (resource.getResourceType().equals(ResourceType.LWM2M_MODEL) && toLwM2mObject(resource) == null) {
  92 + if (resource.getResourceType().equals(ResourceType.LWM2M_MODEL) && toLwM2mObject(resource, true) == null) {
93 93 throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getSearchText()));
94 94 }
95 95 } else {
... ... @@ -131,7 +131,7 @@ public class DefaultTbResourceService implements TbResourceService {
131 131 List<TbResource> resources = resourceService.findTenantResourcesByResourceTypeAndObjectIds(tenantId, ResourceType.LWM2M_MODEL,
132 132 objectIds);
133 133 return resources.stream()
134   - .flatMap(s -> Stream.ofNullable(toLwM2mObject(s)))
  134 + .flatMap(s -> Stream.ofNullable(toLwM2mObject(s, false)))
135 135 .sorted(getComparator(sortProperty, sortOrder))
136 136 .collect(Collectors.toList());
137 137 }
... ... @@ -142,7 +142,7 @@ public class DefaultTbResourceService implements TbResourceService {
142 142 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
143 143 PageData<TbResource> resourcePageData = resourceService.findTenantResourcesByResourceTypeAndPageLink(tenantId, ResourceType.LWM2M_MODEL, pageLink);
144 144 return resourcePageData.getData().stream()
145   - .flatMap(s -> Stream.ofNullable(toLwM2mObject(s)))
  145 + .flatMap(s -> Stream.ofNullable(toLwM2mObject(s, false)))
146 146 .sorted(getComparator(sortProperty, sortOrder))
147 147 .collect(Collectors.toList());
148 148 }
... ... @@ -167,7 +167,7 @@ public class DefaultTbResourceService implements TbResourceService {
167 167 return "DESC".equals(sortOrder) ? comparator.reversed() : comparator;
168 168 }
169 169
170   - private LwM2mObject toLwM2mObject(TbResource resource) {
  170 + private LwM2mObject toLwM2mObject(TbResource resource, boolean isSave) {
171 171 try {
172 172 DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator());
173 173 List<ObjectModel> objectModels =
... ... @@ -186,12 +186,16 @@ public class DefaultTbResourceService implements TbResourceService {
186 186 instance.setId(0);
187 187 List<LwM2mResourceObserve> resources = new ArrayList<>();
188 188 obj.resources.forEach((k, v) -> {
189   - if (v.operations.isReadable()) {
  189 + if (isSave) {
  190 + LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
  191 + resources.add(lwM2MResourceObserve);
  192 + }
  193 + else if (v.operations.isReadable()) {
190 194 LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
191 195 resources.add(lwM2MResourceObserve);
192 196 }
193 197 });
194   - if (resources.size() > 0) {
  198 + if (isSave || resources.size() > 0) {
195 199 instance.setResources(resources.toArray(LwM2mResourceObserve[]::new));
196 200 lwM2mObject.setInstances(new LwM2mInstance[]{instance});
197 201 return lwM2mObject;
... ...
... ... @@ -23,6 +23,7 @@ import com.google.gson.JsonElement;
23 23 import com.google.gson.JsonObject;
24 24 import com.google.gson.reflect.TypeToken;
25 25 import lombok.extern.slf4j.Slf4j;
  26 +import org.apache.commons.lang3.StringUtils;
26 27 import org.eclipse.leshan.core.model.ObjectModel;
27 28 import org.eclipse.leshan.core.model.ResourceModel;
28 29 import org.eclipse.leshan.core.node.LwM2mObject;
... ... @@ -61,6 +62,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
61 62 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
62 63 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
63 64 import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
  65 +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
64 66 import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto;
65 67 import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
66 68 import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
... ... @@ -99,21 +101,20 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L
99 101 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE;
100 102 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_STRATEGY_2;
101 103 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER;
102   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_All;
103 104 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
104 105 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE;
105 106 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL;
106   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
  107 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL;
107 108 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ;
108 109 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES;
109 110 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
110   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE;
111 111 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID;
112 112 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_RESULT_ID;
113 113 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertJsonArrayToSet;
114 114 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId;
115 115 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
116 116 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getAckCallback;
  117 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.setValidTypeOper;
117 118 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey;
118 119
119 120
... ... @@ -125,7 +126,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
125 126 private ExecutorService registrationExecutor;
126 127 private ExecutorService updateRegistrationExecutor;
127 128 private ExecutorService unRegistrationExecutor;
128   - private LwM2mValueConverterImpl converter;
  129 + public LwM2mValueConverterImpl converter;
129 130
130 131 private final TransportService transportService;
131 132 private final LwM2mTransportContext context;
... ... @@ -133,10 +134,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
133 134 public final FirmwareDataCache firmwareDataCache;
134 135 public final LwM2mTransportServerHelper helper;
135 136 private final LwM2MJsonAdaptor adaptor;
136   - private final LwM2mClientContext clientContext;
137   - private final LwM2mTransportRequest lwM2mTransportRequest;
138 137 private final TbLwM2MDtlsSessionStore sessionStore;
139   -
  138 + public final LwM2mClientContext clientContext;
  139 + public final LwM2mTransportRequest lwM2mTransportRequest;
  140 + private final Map<UUID, Long> rpcSubscriptions;
140 141
141 142 public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper,
142 143 LwM2mClientContext clientContext,
... ... @@ -151,6 +152,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
151 152 this.firmwareDataCache = firmwareDataCache;
152 153 this.context = context;
153 154 this.adaptor = adaptor;
  155 + this.rpcSubscriptions = new ConcurrentHashMap<>();
154 156 this.sessionStore = sessionStore;
155 157 }
156 158
... ... @@ -241,16 +243,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
241 243
242 244 /**
243 245 * @param registration - Registration LwM2M Client
244   - * @param observations - All paths observations before unReg
245   - * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
  246 + * @param observations - !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
246 247 */
247 248 public void unReg(Registration registration, Collection<Observation> observations) {
  249 + log.error("Client unRegistration -> test", new RuntimeException());
248 250 unRegistrationExecutor.submit(() -> {
249 251 try {
250   - this.setCancelObservationsAll(registration);
251 252 this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
252 253 this.closeClientSession(registration);
253   - ;
254 254 } catch (Throwable t) {
255 255 log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
256 256 this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()), registration.getId());
... ... @@ -285,12 +285,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
285 285 @Override
286 286 public void setCancelObservationsAll(Registration registration) {
287 287 if (registration != null) {
288   - lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL,
  288 + lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL_ALL,
289 289 null, null, this.config.getTimeout(), null);
290   -// Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
291   -// observations.forEach(observation -> lwM2mTransportRequest.sendAllRequest(registration,
292   -// convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), OBSERVE_CANCEL,
293   -// null, null, this.config.getTimeout(), null));
294 290 }
295 291 }
296 292
... ... @@ -354,12 +350,13 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
354 350 */
355 351 @Override
356 352 public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
357   - LwM2mClient lwM2MClient = clientContext.getClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
  353 + LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo);
358 354 if (msg.getSharedUpdatedCount() > 0) {
359 355 msg.getSharedUpdatedList().forEach(tsKvProto -> {
360 356 String pathName = tsKvProto.getKv().getKey();
361 357 String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
362 358 Object valueNew = getValueFromKvProto(tsKvProto.getKv());
  359 + log.warn("12) Shared AttributeUpdate start pathName [{}], pathIdVer [{}], valueNew [{}]", pathName, pathIdVer, valueNew);
363 360 if ((FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName)
364 361 && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())))
365 362 || (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.TITLE).equals(pathName)
... ... @@ -438,121 +435,49 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
438 435 clientContext.getLwM2mClients().forEach(e -> e.deleteResources(pathIdVer, this.config.getModelProvider()));
439 436 }
440 437
  438 + /**
  439 + * #1 del from rpcSubscriptions by timeout
  440 + * #2 if not present in rpcSubscriptions by requestId: create new Lwm2mClientRpcRequest, after success - add requestId, timeout
  441 + */
441 442 @Override
442 443 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) {
443   - log.warn("4) RPC-OK finish to [{}]", toDeviceRpcRequestMsg);
444   - Lwm2mClientRpcRequest lwm2mClientRpcRequest = null;
445   - try {
446   - Registration registration = clientContext.getClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).getRegistration();
447   - lwm2mClientRpcRequest = this.getDeviceRpcRequest(toDeviceRpcRequestMsg, sessionInfo, registration);
448   - if (lwm2mClientRpcRequest.getErrorMsg() != null) {
449   - lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
450   - this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
451   - } else {
452   - lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), lwm2mClientRpcRequest.getContentFormatName(),
453   - lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(),
454   - this.config.getTimeout(), lwm2mClientRpcRequest);
455   - }
456   - } catch (Exception e) {
457   - if (lwm2mClientRpcRequest == null) {
458   - lwm2mClientRpcRequest = new Lwm2mClientRpcRequest();
459   - }
460   - lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
461   - if (lwm2mClientRpcRequest.getErrorMsg() == null) {
462   - lwm2mClientRpcRequest.setErrorMsg(e.getMessage());
463   - }
464   - this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
465   - }
466   - }
467   -
468   - private Lwm2mClientRpcRequest getDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest,
469   - SessionInfoProto sessionInfo, Registration registration) throws IllegalArgumentException {
470   - Lwm2mClientRpcRequest lwm2mClientRpcRequest = new Lwm2mClientRpcRequest();
471   - try {
472   - lwm2mClientRpcRequest.setRequestId(toDeviceRequest.getRequestId());
473   - lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
474   - lwm2mClientRpcRequest.setValidTypeOper(toDeviceRequest.getMethodName());
475   - JsonObject rpcRequest = LwM2mTransportUtil.validateJson(toDeviceRequest.getParams());
476   - if (rpcRequest != null) {
477   - if (rpcRequest.has(lwm2mClientRpcRequest.keyNameKey)) {
478   - String targetIdVer = this.getPresentPathIntoProfile(sessionInfo,
479   - rpcRequest.get(lwm2mClientRpcRequest.keyNameKey).getAsString());
480   - if (targetIdVer != null) {
481   - lwm2mClientRpcRequest.setTargetIdVer(targetIdVer);
482   - lwm2mClientRpcRequest.setInfoMsg(String.format("Changed by: key - %s, pathIdVer - %s",
483   - rpcRequest.get(lwm2mClientRpcRequest.keyNameKey).getAsString(), targetIdVer));
484   - }
485   - }
486   - if (lwm2mClientRpcRequest.getTargetIdVer() == null) {
487   - lwm2mClientRpcRequest.setValidTargetIdVerKey(rpcRequest, registration);
488   - }
489   - if (rpcRequest.has(lwm2mClientRpcRequest.contentFormatNameKey)) {
490   - lwm2mClientRpcRequest.setValidContentFormatName(rpcRequest);
491   - }
492   - if (rpcRequest.has(lwm2mClientRpcRequest.timeoutInMsKey) && rpcRequest.get(lwm2mClientRpcRequest.timeoutInMsKey).getAsLong() > 0) {
493   - lwm2mClientRpcRequest.setTimeoutInMs(rpcRequest.get(lwm2mClientRpcRequest.timeoutInMsKey).getAsLong());
494   - }
495   - if (rpcRequest.has(lwm2mClientRpcRequest.valueKey)) {
496   - lwm2mClientRpcRequest.setValue(rpcRequest.get(lwm2mClientRpcRequest.valueKey).getAsString());
497   - }
498   - if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) {
499   - ConcurrentHashMap<String, Object> params = new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey)
500   - .getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {
501   - }.getType());
502   - if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()) {
503   - ConcurrentHashMap<String, Object> paramsResourceId = convertParamsToResourceId(params, sessionInfo);
504   - if (paramsResourceId.size() > 0) {
505   - lwm2mClientRpcRequest.setParams(paramsResourceId);
506   - }
507   - } else {
508   - lwm2mClientRpcRequest.setParams(params);
509   - }
510   - } else if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonArray()) {
511   - new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey)
512   - .getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {
513   - }.getType());
  444 + // #1
  445 + this.checkRpcRequestTimeout();
  446 + String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null";
  447 + LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName());
  448 + UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB());
  449 + log.warn("4) RPC-OK finish to [{}], keys: [{}]", requestUUID, this.rpcSubscriptions.keySet());
  450 + if (!this.rpcSubscriptions.containsKey(requestUUID)) {
  451 + this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime());
  452 + Lwm2mClientRpcRequest lwm2mClientRpcRequest = null;
  453 + try {
  454 + Registration registration = clientContext.getClient(sessionInfo).getRegistration();
  455 + lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this);
  456 + if (lwm2mClientRpcRequest.getErrorMsg() != null) {
  457 + lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
  458 + this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
  459 + } else {
  460 + lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(),
  461 + null,
  462 + lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(),
  463 + this.config.getTimeout(), lwm2mClientRpcRequest);
514 464 }
515   - lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
516   - if (!(OBSERVE_READ_ALL == lwm2mClientRpcRequest.getTypeOper()
517   - || DISCOVER_All == lwm2mClientRpcRequest.getTypeOper()
518   - || OBSERVE_CANCEL == lwm2mClientRpcRequest.getTypeOper())
519   - && lwm2mClientRpcRequest.getTargetIdVer() == null) {
520   - lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " +
521   - lwm2mClientRpcRequest.keyNameKey + " is null or bad format");
  465 + } catch (Exception e) {
  466 + if (lwm2mClientRpcRequest == null) {
  467 + lwm2mClientRpcRequest = new Lwm2mClientRpcRequest();
522 468 }
523   - /**
524   - * EXECUTE && WRITE_REPLACE - only for Resource or ResourceInstance
525   - */
526   - else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
527   - || WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper())
528   - && lwm2mClientRpcRequest.getTargetIdVer() != null
529   - && !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource()
530   - || new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) {
531   - lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey
532   - + ". Only Resource or ResourceInstance can be this operation");
  469 + lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
  470 + if (lwm2mClientRpcRequest.getErrorMsg() == null) {
  471 + lwm2mClientRpcRequest.setErrorMsg(e.getMessage());
533 472 }
534   - } else {
535   - lwm2mClientRpcRequest.setErrorMsg("Params of request is bad Json format.");
  473 + this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
536 474 }
537   - } catch (Exception e) {
538   - throw new IllegalArgumentException(lwm2mClientRpcRequest.getErrorMsg());
539 475 }
540   - return lwm2mClientRpcRequest;
541 476 }
542 477
543   - private ConcurrentHashMap<String, Object> convertParamsToResourceId(ConcurrentHashMap<String, Object> params,
544   - SessionInfoProto sessionInfo) {
545   - ConcurrentHashMap<String, Object> paramsIdVer = new ConcurrentHashMap<>();
546   - params.forEach((k, v) -> {
547   - String targetIdVer = this.getPresentPathIntoProfile(sessionInfo, k);
548   - if (targetIdVer != null) {
549   - LwM2mPath targetId = new LwM2mPath(convertPathFromIdVerToObjectId(targetIdVer));
550   - if (targetId.isResource()) {
551   - paramsIdVer.put(String.valueOf(targetId.getResourceId()), v);
552   - }
553   - }
554   - });
555   - return paramsIdVer;
  478 + private void checkRpcRequestTimeout() {
  479 + Set<UUID> rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> System.currentTimeMillis() > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet());
  480 + rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove);
556 481 }
557 482
558 483 public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) {
... ... @@ -722,10 +647,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
722 647 * set setClient_fw_info... = value
723 648 **/
724 649 if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
725   - lwM2MClient.getFwUpdate().initReadValue(this, lwM2mTransportRequest, path);
  650 + lwM2MClient.getFwUpdate().initReadValue(this, this.lwM2mTransportRequest, path);
726 651 }
727 652 if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
728   - lwM2MClient.getSwUpdate().initReadValue(this, lwM2mTransportRequest, path);
  653 + lwM2MClient.getSwUpdate().initReadValue(this, this.lwM2mTransportRequest, path);
729 654 }
730 655
731 656 /**
... ... @@ -742,7 +667,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
742 667 && (convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) {
743 668 if (DOWNLOADED.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
744 669 && lwM2MClient.getFwUpdate().conditionalFwExecuteStart()) {
745   - lwM2MClient.getFwUpdate().executeFwSwWare(this, lwM2mTransportRequest);
  670 + lwM2MClient.getFwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest);
746 671 } else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
747 672 && lwM2MClient.getFwUpdate().conditionalFwExecuteAfterSuccess()) {
748 673 lwM2MClient.getFwUpdate().finishFwSwUpdate(this, true);
... ... @@ -767,7 +692,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
767 692 && (convertPathFromObjectIdToIdVer(SW_RESULT_ID, registration).equals(path))) {
768 693 if (DOWNLOADED.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
769 694 && lwM2MClient.getSwUpdate().conditionalSwUpdateExecute()) {
770   - lwM2MClient.getSwUpdate().executeFwSwWare(this, lwM2mTransportRequest);
  695 + lwM2MClient.getSwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest);
771 696 } else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
772 697 && lwM2MClient.getSwUpdate().conditionalSwExecuteAfterSuccess()) {
773 698 lwM2MClient.getSwUpdate().finishFwSwUpdate(this, true);
... ... @@ -970,11 +895,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
970 895 * @return - return value of Resource by idPath
971 896 */
972 897 private LwM2mResource getResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) {
973   - LwM2mResource resourceValue = null;
974   - if (new LwM2mPath(convertPathFromIdVerToObjectId(path)).isResource()) {
975   - resourceValue = lwM2MClient.getResources().get(path).getLwM2mResource();
  898 + LwM2mResource lwm2mResourceValue = null;
  899 + ResourceValue resourceValue = lwM2MClient.getResources().get(path);
  900 + if (resourceValue != null) {
  901 + if (new LwM2mPath(convertPathFromIdVerToObjectId(path)).isResource()) {
  902 + lwm2mResourceValue = lwM2MClient.getResources().get(path).getLwM2mResource();
  903 + }
976 904 }
977   - return resourceValue;
  905 + return lwm2mResourceValue;
978 906 }
979 907
980 908 /**
... ... @@ -1281,7 +1209,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
1281 1209 * @param name -
1282 1210 * @return -
1283 1211 */
1284   - private String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
  1212 + public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
1285 1213 LwM2mClientProfile profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
1286 1214 LwM2mClient lwM2mClient = clientContext.getClient(sessionInfo);
1287 1215 return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
... ... @@ -1304,7 +1232,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
1304 1232
1305 1233 this.updateAttributeFromThingsboard(tsKvProtos, sessionInfo);
1306 1234 } catch (Exception e) {
1307   - log.error(String.valueOf(e));
  1235 + log.error("", e);
1308 1236 }
1309 1237 }
1310 1238
... ... @@ -1378,6 +1306,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
1378 1306 private void reportActivityAndRegister(SessionInfoProto sessionInfo) {
1379 1307 if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) {
1380 1308 transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo));
  1309 + this.reportActivitySubscription(sessionInfo);
1381 1310 }
1382 1311 }
1383 1312
... ... @@ -1510,4 +1439,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
1510 1439 return this.config;
1511 1440 }
1512 1441
  1442 + private void reportActivitySubscription(TransportProtos.SessionInfoProto sessionInfo) {
  1443 + transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
  1444 + .setAttributeSubscription(true)
  1445 + .setRpcSubscription(true)
  1446 + .setLastActivityTime(System.currentTimeMillis())
  1447 + .build(), TransportServiceCallback.EMPTY);
  1448 + }
1513 1449 }
... ...
... ... @@ -94,12 +94,6 @@ public class LwM2mServerListener {
94 94 @Override
95 95 public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
96 96 if (registration != null) {
97   -// if (observation.getPath().isResource() || observation.getPath().isResourceInstance()) {
98   -// String msg = String.format("%s: Successful Observation %s.", LOG_LW2M_INFO,
99   -// observation.getPath());
100   -// log.warn(msg);
101   -// service.sendLogsToThingsboard(msg, registration.getId());
102   -// }
103 97 service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
104 98 registration), response, null);
105 99 }
... ...
... ... @@ -22,6 +22,8 @@ import org.eclipse.californium.core.coap.Response;
22 22 import org.eclipse.leshan.core.Link;
23 23 import org.eclipse.leshan.core.model.ResourceModel;
24 24 import org.eclipse.leshan.core.node.LwM2mNode;
  25 +import org.eclipse.leshan.core.node.LwM2mObject;
  26 +import org.eclipse.leshan.core.node.LwM2mObjectInstance;
25 27 import org.eclipse.leshan.core.node.LwM2mPath;
26 28 import org.eclipse.leshan.core.node.LwM2mResource;
27 29 import org.eclipse.leshan.core.node.LwM2mSingleResource;
... ... @@ -34,6 +36,7 @@ import org.eclipse.leshan.core.request.DownlinkRequest;
34 36 import org.eclipse.leshan.core.request.ExecuteRequest;
35 37 import org.eclipse.leshan.core.request.ObserveRequest;
36 38 import org.eclipse.leshan.core.request.ReadRequest;
  39 +import org.eclipse.leshan.core.request.WriteAttributesRequest;
37 40 import org.eclipse.leshan.core.request.WriteRequest;
38 41 import org.eclipse.leshan.core.request.exception.ClientSleepingException;
39 42 import org.eclipse.leshan.core.response.DeleteResponse;
... ... @@ -79,10 +82,12 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L
79 82 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE;
80 83 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
81 84 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER;
82   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_All;
  85 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_ALL;
83 86 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
84 87 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL;
  88 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL;
85 89 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
  90 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES;
86 91 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
87 92 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE;
88 93 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_REQUEST_CHANNEL;
... ... @@ -123,7 +128,7 @@ public class LwM2mTransportRequest {
123 128 */
124 129
125 130 public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
126   - String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
  131 + String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) {
127 132 try {
128 133 String target = convertPathFromIdVerToObjectId(targetIdVer);
129 134 ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT;
... ... @@ -132,41 +137,42 @@ public class LwM2mTransportRequest {
132 137 if (!OBSERVE_READ_ALL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) {
133 138 if (lwM2MClient.isValidObjectVersion(targetIdVer)) {
134 139 timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
135   - DownlinkRequest request = createRequest (registration, lwM2MClient, typeOper, contentFormat, target,
136   - targetIdVer, resultIds, params, rpcRequest);
  140 + DownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target,
  141 + targetIdVer, resultIds, params, lwm2mClientRpcRequest);
137 142 if (request != null) {
138 143 try {
139   - this.sendRequest(registration, lwM2MClient, request, timeoutInMs, rpcRequest);
  144 + this.sendRequest(registration, lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest);
140 145 } catch (ClientSleepingException e) {
141 146 DownlinkRequest finalRequest = request;
142 147 long finalTimeoutInMs = timeoutInMs;
143   - Lwm2mClientRpcRequest finalRpcRequest = rpcRequest;
  148 + Lwm2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest;
144 149 lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest));
145 150 } catch (Exception e) {
146 151 log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e);
147 152 }
148   - }
149   - else if (WRITE_UPDATE.name().equals(typeOper.name())) {
150   - Lwm2mClientRpcRequest rpcRequestClone = (Lwm2mClientRpcRequest) rpcRequest.clone();
151   - if (rpcRequestClone != null) {
  153 + } else if (WRITE_UPDATE.name().equals(typeOper.name())) {
  154 + if (lwm2mClientRpcRequest != null) {
152 155 String errorMsg = String.format("Path %s params is not valid", targetIdVer);
153   - handler.sentRpcRequest(rpcRequestClone, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
154   - rpcRequest = null;
  156 + handler.sentRpcRequest(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
155 157 }
156   - }
157   - else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) {
  158 + } else if (WRITE_REPLACE.name().equals(typeOper.name()) || EXECUTE.name().equals(typeOper.name())) {
  159 + if (lwm2mClientRpcRequest != null) {
  160 + String errorMsg = String.format("Path %s object model is absent", targetIdVer);
  161 + handler.sentRpcRequest(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
  162 + }
  163 + } else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) {
158 164 log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
159   - if (rpcRequest != null) {
  165 + if (lwm2mClientRpcRequest != null) {
160 166 ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
161 167 String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
162   - handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
  168 + this.handler.sentRpcRequest(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
163 169 }
164 170 }
165   - } else if (rpcRequest != null) {
  171 + } else if (lwm2mClientRpcRequest != null) {
166 172 String errorMsg = String.format("Path %s not found in object version", targetIdVer);
167   - handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
  173 + this.handler.sentRpcRequest(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
168 174 }
169   - } else if (OBSERVE_READ_ALL.name().equals(typeOper.name()) || DISCOVER_All.name().equals(typeOper.name())) {
  175 + } else if (OBSERVE_READ_ALL.name().equals(typeOper.name()) || DISCOVER_ALL.name().equals(typeOper.name())) {
170 176 Set<String> paths;
171 177 if (OBSERVE_READ_ALL.name().equals(typeOper.name())) {
172 178 Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
... ... @@ -175,34 +181,34 @@ public class LwM2mTransportRequest {
175 181 assert registration != null;
176 182 Link[] objectLinks = registration.getSortedObjectLinks();
177 183 paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet());
178   - String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO,
179   - typeOper.name(), paths);
180   - handler.sendLogsToThingsboard(msg, registration.getId());
181 184 }
182   - if (rpcRequest != null) {
  185 + String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO,
  186 + typeOper.name(), paths);
  187 + this.handler.sendLogsToThingsboard(msg, registration.getId());
  188 + if (lwm2mClientRpcRequest != null) {
183 189 String valueMsg = String.format("Paths - %s", paths);
184   - handler.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
  190 + this.handler.sentRpcRequest(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
185 191 }
186   - } else if (OBSERVE_CANCEL.name().equals(typeOper.name())) {
  192 + } else if (OBSERVE_CANCEL_ALL.name().equals(typeOper.name())) {
187 193 int observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration);
188 194 String observeCancelMsgAll = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO,
189 195 OBSERVE_CANCEL.name(), observeCancelCnt);
190   - this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsgAll, rpcRequest);
  196 + this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsgAll, lwm2mClientRpcRequest);
191 197 }
192 198 } catch (Exception e) {
193 199 String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
194 200 typeOper.name(), e.getMessage());
195 201 handler.sendLogsToThingsboard(msg, registration.getId());
196   - if (rpcRequest != null) {
  202 + if (lwm2mClientRpcRequest != null) {
197 203 String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage());
198   - handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
  204 + handler.sentRpcRequest(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
199 205 }
200 206 }
201 207 }
202 208
203   - private DownlinkRequest createRequest (Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper,
204   - ContentFormat contentFormat, String target, String targetIdVer,
205   - LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) {
  209 + private DownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper,
  210 + ContentFormat contentFormat, String target, String targetIdVer,
  211 + LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) {
206 212 DownlinkRequest request = null;
207 213 switch (typeOper) {
208 214 case READ:
... ... @@ -212,7 +218,7 @@ public class LwM2mTransportRequest {
212 218 request = new DiscoverRequest(target);
213 219 break;
214 220 case OBSERVE:
215   - String msg = String.format("%s: Send Observation %s.", LOG_LW2M_INFO, targetIdVer);
  221 + String msg = String.format("%s: Send Observation %s.", LOG_LW2M_INFO, targetIdVer);
216 222 log.warn(msg);
217 223 if (resultIds.isResource()) {
218 224 Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
... ... @@ -240,11 +246,13 @@ public class LwM2mTransportRequest {
240 246 this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsg, rpcRequest);
241 247 break;
242 248 case EXECUTE:
243   - ResourceModel resourceModelExe = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
244   - if (params != null && !resourceModelExe.multiple) {
245   - request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModelExe.type, ResourceModel.Type.STRING, resultIds));
246   - } else {
247   - request = new ExecuteRequest(target);
  249 + ResourceModel resourceModelExecute = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
  250 + if (resourceModelExecute != null) {
  251 + if (params != null && !resourceModelExecute.multiple) {
  252 + request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModelExecute.type, ResourceModel.Type.STRING, resultIds));
  253 + } else {
  254 + request = new ExecuteRequest(target);
  255 + }
248 256 }
249 257 break;
250 258 case WRITE_REPLACE:
... ... @@ -255,10 +263,12 @@ public class LwM2mTransportRequest {
255 263 * JSON, TEXT;
256 264 **/
257 265 ResourceModel resourceModelWrite = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
258   - contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat);
259   - request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
260   - resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type,
261   - registration, rpcRequest);
  266 + if (resourceModelWrite != null) {
  267 + contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat);
  268 + request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
  269 + resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type,
  270 + registration, rpcRequest);
  271 + }
262 272 break;
263 273 case WRITE_UPDATE:
264 274 if (resultIds.isResource()) {
... ... @@ -297,7 +307,7 @@ public class LwM2mTransportRequest {
297 307 }
298 308 break;
299 309 case WRITE_ATTRIBUTES:
300   - request = createWriteAttributeRequest(target, params);
  310 + request = createWriteAttributeRequest(target, params, this.handler);
301 311 break;
302 312 case DELETE:
303 313 request = new DeleteRequest(target);
... ... @@ -465,7 +475,13 @@ public class LwM2mTransportRequest {
465 475 } else if (response instanceof ExecuteResponse) {
466 476 log.warn("[{}] Path [{}] ExecuteResponse 7_Send", pathIdVer, response);
467 477 } else if (response instanceof WriteAttributesResponse) {
  478 + msgLog = String.format("%s: type operation: %s path: %s value: %s",
  479 + LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString());
  480 + handler.sendLogsToThingsboard(msgLog, registration.getId());
468 481 log.warn("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response);
  482 + if (rpcRequest != null) {
  483 + handler.sentRpcRequest(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE);
  484 + }
469 485 } else if (response instanceof WriteResponse) {
470 486 log.warn("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response);
471 487 this.infoWriteResponse(registration, response, request);
... ... @@ -486,29 +502,37 @@ public class LwM2mTransportRequest {
486 502 private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request) {
487 503 try {
488 504 LwM2mNode node = ((WriteRequest) request).getNode();
489   - String msg;
  505 + String msg = null;
490 506 Object value;
491   - LwM2mSingleResource singleResource = (LwM2mSingleResource) node;
492   - if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) {
493   - int valueLength;
494   - if (singleResource.getType() == ResourceModel.Type.STRING) {
495   - valueLength = ((String) singleResource.getValue()).length();
496   - value = ((String) singleResource.getValue())
497   - .substring(Math.min(valueLength, config.getLogMaxLength()));
  507 + if (node instanceof LwM2mObject) {
  508 + msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s",
  509 + LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObject) node).toString());
  510 + } else if (node instanceof LwM2mObjectInstance) {
  511 + msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s",
  512 + LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObjectInstance) node).prettyPrint());
  513 + } else if (node instanceof LwM2mSingleResource) {
  514 + LwM2mSingleResource singleResource = (LwM2mSingleResource) node;
  515 + if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) {
  516 + int valueLength;
  517 + if (singleResource.getType() == ResourceModel.Type.STRING) {
  518 + valueLength = ((String) singleResource.getValue()).length();
  519 + value = ((String) singleResource.getValue())
  520 + .substring(Math.min(valueLength, config.getLogMaxLength()));
498 521
  522 + } else {
  523 + valueLength = ((byte[]) singleResource.getValue()).length;
  524 + value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()),
  525 + Math.min(valueLength, config.getLogMaxLength())));
  526 + }
  527 + value = valueLength > config.getLogMaxLength() ? value + "..." : value;
  528 + msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s",
  529 + LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value);
499 530 } else {
500   - valueLength = ((byte[]) singleResource.getValue()).length;
501   - value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()),
502   - Math.min(valueLength, config.getLogMaxLength())));
  531 + value = this.converter.convertValue(singleResource.getValue(),
  532 + singleResource.getType(), ResourceModel.Type.STRING, request.getPath());
  533 + msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s",
  534 + LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value);
503 535 }
504   - value = valueLength > config.getLogMaxLength() ? value + "..." : value;
505   - msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s",
506   - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value);
507   - } else {
508   - value = this.converter.convertValue(singleResource.getValue(),
509   - singleResource.getType(), ResourceModel.Type.STRING, request.getPath());
510   - msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s",
511   - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value);
512 536 }
513 537 if (msg != null) {
514 538 handler.sendLogsToThingsboard(msg, registration.getId());
... ... @@ -530,11 +554,11 @@ public class LwM2mTransportRequest {
530 554 LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
531 555 if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
532 556 lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name());
533   - lwM2MClient.getFwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
  557 + lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
534 558 }
535 559 if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) {
536 560 lwM2MClient.getSwUpdate().setStateUpdate(DOWNLOADED.name());
537   - lwM2MClient.getSwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
  561 + lwM2MClient.getSwUpdate().sendLogs(this.handler,WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
538 562 }
539 563 }
540 564
... ... @@ -545,21 +569,21 @@ public class LwM2mTransportRequest {
545 569 LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
546 570 if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
547 571 lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name());
548   - lwM2MClient.getFwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
  572 + lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
549 573 }
550 574 if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) {
551 575 lwM2MClient.getSwUpdate().setStateUpdate(FAILED.name());
552   - lwM2MClient.getSwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
  576 + lwM2MClient.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
553 577 }
554 578 }
555 579
556 580 private void afterExecuteFwSwUpdateError(Registration registration, DownlinkRequest request, String msgError) {
557 581 LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
558 582 if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) {
559   - lwM2MClient.getFwUpdate().sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
  583 + lwM2MClient.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
560 584 }
561 585 if (request.getPath().toString().equals(SW_INSTALL_ID) && lwM2MClient.getSwUpdate() != null) {
562   - lwM2MClient.getSwUpdate().sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
  586 + lwM2MClient.getSwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
563 587 }
564 588 }
565 589
... ...
... ... @@ -61,9 +61,12 @@ import java.util.Set;
61 61 import java.util.concurrent.ConcurrentHashMap;
62 62
63 63 import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION;
  64 +import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN;
  65 +import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN;
64 66 import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD;
65 67 import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD;
66 68 import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
  69 +import static org.eclipse.leshan.core.attributes.Attribute.STEP;
67 70 import static org.eclipse.leshan.core.model.ResourceModel.Type.BOOLEAN;
68 71 import static org.eclipse.leshan.core.model.ResourceModel.Type.FLOAT;
69 72 import static org.eclipse.leshan.core.model.ResourceModel.Type.INTEGER;
... ... @@ -104,8 +107,7 @@ public class LwM2mTransportUtil {
104 107
105 108 public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
106 109
107   - public static final String
108   - LOG_LW2M_TELEMETRY = "LwM2MLog";
  110 + public static final String LOG_LW2M_TELEMETRY = "logLwm2m";
109 111 public static final String LOG_LW2M_INFO = "info";
110 112 public static final String LOG_LW2M_ERROR = "error";
111 113 public static final String LOG_LW2M_WARN = "warn";
... ... @@ -117,6 +119,23 @@ public class LwM2mTransportUtil {
117 119 public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized";
118 120 public static final String LWM2M_VERSION_DEFAULT = "1.0";
119 121
  122 + // RPC
  123 + public static final String TYPE_OPER_KEY = "typeOper";
  124 + public static final String TARGET_ID_VER_KEY = "targetIdVer";
  125 + public static final String KEY_NAME_KEY = "key";
  126 + public static final String VALUE_KEY = "value";
  127 + public static final String PARAMS_KEY = "params";
  128 + public static final String SEPARATOR_KEY = ":";
  129 + public static final String FINISH_VALUE_KEY = ",";
  130 + public static final String START_JSON_KEY = "{";
  131 + public static final String FINISH_JSON_KEY = "}";
  132 + // public static final String contentFormatNameKey = "contentFormatName";
  133 + public static final String INFO_KEY = "info";
  134 + // public static final String TIME_OUT_IN_MS = "timeOutInMs";
  135 + public static final String RESULT_KEY = "result";
  136 + public static final String ERROR_KEY = "error";
  137 + public static final String METHOD_KEY = "methodName";
  138 +
120 139 // FirmWare
121 140 public static final String FW_UPDATE = "Firmware update";
122 141 public static final Integer FW_ID = 5;
... ... @@ -182,20 +201,21 @@ public class LwM2mTransportUtil {
182 201 */
183 202 READ(0, "Read"),
184 203 DISCOVER(1, "Discover"),
185   - DISCOVER_All(2, "DiscoverAll"),
  204 + DISCOVER_ALL(2, "DiscoverAll"),
186 205 OBSERVE_READ_ALL(3, "ObserveReadAll"),
187 206 /**
188 207 * POST
189 208 */
190 209 OBSERVE(4, "Observe"),
191 210 OBSERVE_CANCEL(5, "ObserveCancel"),
192   - EXECUTE(6, "Execute"),
  211 + OBSERVE_CANCEL_ALL(6, "ObserveCancelAll"),
  212 + EXECUTE(7, "Execute"),
193 213 /**
194 214 * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see
195 215 * section 5.3.3 of the LW M2M spec).
196 216 * if all resources are to be replaced
197 217 */
198   - WRITE_REPLACE(7, "WriteReplace"),
  218 + WRITE_REPLACE(8, "WriteReplace"),
199 219 /*
200 220 PUT
201 221 */
... ... @@ -204,18 +224,16 @@ public class LwM2mTransportUtil {
204 224 * 5.3.3 of the LW M2M spec).
205 225 * if this is a partial update request
206 226 */
207   - WRITE_UPDATE(8, "WriteUpdate"),
208   - WRITE_ATTRIBUTES(9, "WriteAttributes"),
209   - DELETE(10, "Delete"),
  227 + WRITE_UPDATE(9, "WriteUpdate"),
  228 + WRITE_ATTRIBUTES(10, "WriteAttributes"),
  229 + DELETE(11, "Delete");
210 230
211 231 // only for RPC
212   - FW_READ_INFO(11, "FirmwareReadInfo"),
213   - FW_UPDATE(12, "FirmwareUpdate"),
214   - FW_UPDATE_URL(14, "FirmwareUpdateUrl"),
215   - SW_READ_INFO(15, "SoftwareReadInfo"),
216   - SW_UPDATE(16, "SoftwareUpdate"),
217   - SW_UPDATE_URL(17, "SoftwareUpdateUrl"),
218   - SW_UNINSTALL(18, "SoftwareUninstall");
  232 +// FW_READ_INFO(12, "FirmwareReadInfo"),
  233 +// FW_UPDATE(13, "FirmwareUpdate"),
  234 +// SW_READ_INFO(15, "SoftwareReadInfo"),
  235 +// SW_UPDATE(16, "SoftwareUpdate"),
  236 +// SW_UNINSTALL(18, "SoftwareUninstall");
219 237
220 238 public int code;
221 239 public String type;
... ... @@ -817,26 +835,29 @@ public class LwM2mTransportUtil {
817 835 * Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60");
818 836 * Attribute [] attrs = {gt, st};
819 837 */
820   - public static DownlinkRequest createWriteAttributeRequest(String target, Object params) {
821   - AttributeSet attrSet = new AttributeSet(createWriteAttributes(params));
  838 + public static DownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MTransportMsgHandler serviceImpl) {
  839 + AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target));
822 840 return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null;
823 841 }
824 842
825   - private static Attribute[] createWriteAttributes(Object params) {
  843 + private static Attribute[] createWriteAttributes(Object params, DefaultLwM2MTransportMsgHandler serviceImpl, String target) {
826 844 List<Attribute> attributeLists = new ArrayList<>();
827 845 ObjectMapper oMapper = new ObjectMapper();
828 846 Map<String, Object> map = oMapper.convertValue(params, ConcurrentHashMap.class);
829 847 map.forEach((k, v) -> {
830   - if (!v.toString().isEmpty() || (v.toString().isEmpty() && OBJECT_VERSION.equals(k))) {
831   - attributeLists.add(new Attribute(k,
832   - (DIMENSION.equals(k) || MINIMUM_PERIOD.equals(k) || MAXIMUM_PERIOD.equals(k)) ?
833   - ((Double) v).longValue() : v));
  848 + if (StringUtils.trimToNull(v.toString()) != null) {
  849 + Object attrValue = convertWriteAttributes(k, v, serviceImpl, target);
  850 + if (attrValue != null) {
  851 + Attribute attribute = createAttribute(k, attrValue);
  852 + if (attribute != null) {
  853 + attributeLists.add(new Attribute(k, attrValue));
  854 + }
  855 + }
834 856 }
835 857 });
836 858 return attributeLists.toArray(Attribute[]::new);
837 859 }
838 860
839   -
840 861 public static Set<String> convertJsonArrayToSet(JsonArray jsonArray) {
841 862 List<String> attributeListOld = new Gson().fromJson(jsonArray, new TypeToken<List<String>>() {
842 863 }.getType());
... ... @@ -863,4 +884,47 @@ public class LwM2mTransportUtil {
863 884 return null;
864 885 }
865 886 }
  887 +
  888 + public static LwM2mTypeOper setValidTypeOper(String typeOper) {
  889 + try {
  890 + return LwM2mTransportUtil.LwM2mTypeOper.fromLwLwM2mTypeOper(typeOper);
  891 + } catch (Exception e) {
  892 + return null;
  893 + }
  894 + }
  895 +
  896 + public static Object convertWriteAttributes(String type, Object value, DefaultLwM2MTransportMsgHandler serviceImpl, String target) {
  897 + switch (type) {
  898 + /** Integer [0:255]; */
  899 + case DIMENSION:
  900 + Long dim = (Long) serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), INTEGER, new LwM2mPath(target));
  901 + return dim >= 0 && dim <= 255 ? dim : null;
  902 + /**String;*/
  903 + case OBJECT_VERSION:
  904 + return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), STRING, new LwM2mPath(target));
  905 + /**INTEGER */
  906 + case MINIMUM_PERIOD:
  907 + case MAXIMUM_PERIOD:
  908 + return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), INTEGER, new LwM2mPath(target));
  909 + /**Float; */
  910 + case GREATER_THAN:
  911 + case LESSER_THAN:
  912 + case STEP:
  913 + if (value.getClass().getSimpleName().equals("String") ) {
  914 + value = Double.valueOf((String) value);
  915 + }
  916 + return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), FLOAT, new LwM2mPath(target));
  917 + default:
  918 + return null;
  919 + }
  920 + }
  921 +
  922 + private static Attribute createAttribute(String key, Object attrValue) {
  923 + try {
  924 + return new Attribute(key, attrValue);
  925 + } catch (Exception e) {
  926 + log.error("CreateAttribute, not valid parameter key: [{}], attrValue: [{}], error: [{}]", key, attrValue, e.getMessage());
  927 + return null;
  928 + }
  929 + }
866 930 }
... ...
... ... @@ -31,8 +31,8 @@ import org.eclipse.leshan.server.registration.Registration;
31 31 import org.eclipse.leshan.server.security.SecurityInfo;
32 32 import org.thingsboard.server.common.data.Device;
33 33 import org.thingsboard.server.common.data.DeviceProfile;
34   -import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
35 34 import org.thingsboard.server.common.data.firmware.FirmwareType;
  35 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
36 36 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
37 37 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
38 38 import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler;
... ... @@ -117,7 +117,6 @@ public class LwM2mClient implements Cloneable {
117 117 this.pendingReadRequests = new CopyOnWriteArrayList<>();
118 118 this.resources = new ConcurrentHashMap<>();
119 119 this.profileId = profileId;
120   - this.sessionId = sessionId;
121 120 this.init = false;
122 121 this.queuedRequests = new ConcurrentLinkedQueue<>();
123 122
... ... @@ -194,17 +193,31 @@ public class LwM2mClient implements Cloneable {
194 193 public Object getResourceValue(String pathRezIdVer, String pathRezId) {
195 194 String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
196 195 if (this.resources.get(pathRez) != null) {
197   - return this.resources.get(pathRez).getLwM2mResource().isMultiInstances() ?
  196 + return this.resources.get(pathRez).getLwM2mResource().isMultiInstances() ?
198 197 this.resources.get(pathRez).getLwM2mResource().getValues() :
199 198 this.resources.get(pathRez).getLwM2mResource().getValue();
200 199 }
201 200 return null;
202 201 }
203 202
204   - public Object getResourceName (String pathRezIdVer, String pathRezId) {
  203 + public Object getResourceNameByRezId(String pathRezIdVer, String pathRezId) {
205 204 String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
206 205 if (this.resources.get(pathRez) != null) {
207   - return this.resources.get(pathRez).getResourceModel().name;
  206 + return this.resources.get(pathRez).getResourceModel().name;
  207 + }
  208 + return null;
  209 + }
  210 +
  211 + public String getRezIdByResourceNameAndObjectInstanceId(String resourceName, String pathObjectInstanceIdVer, LwM2mModelProvider modelProvider) {
  212 + LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathObjectInstanceIdVer));
  213 + if (pathIds.isObjectInstance()) {
  214 + Set<Integer> rezIds = modelProvider.getObjectModel(registration)
  215 + .getObjectModel(pathIds.getObjectId()).resources.entrySet()
  216 + .stream()
  217 + .filter(map -> resourceName.equals(map.getValue().name))
  218 + .map(map -> map.getKey())
  219 + .collect(Collectors.toSet());
  220 + return rezIds.size() > 0 ? String.valueOf(rezIds.stream().findFirst().get()) : null;
208 221 }
209 222 return null;
210 223 }
... ... @@ -225,11 +238,11 @@ public class LwM2mClient implements Cloneable {
225 238 .getObjectModel(pathIds.getObjectId()) : null;
226 239 }
227 240
228   - public String objectToString (LwM2mObject lwM2mObject, LwM2mValueConverterImpl converter, String pathIdVer) {
  241 + public String objectToString(LwM2mObject lwM2mObject, LwM2mValueConverterImpl converter, String pathIdVer) {
229 242 StringBuilder builder = new StringBuilder();
230 243 builder.append("LwM2mObject [id=").append(lwM2mObject.getId()).append(", instances={");
231 244 lwM2mObject.getInstances().forEach((instId, inst) -> {
232   - builder.append(instId).append("=").append(this.instanceToString(inst, converter, pathIdVer)).append(", ");
  245 + builder.append(instId).append("=").append(this.instanceToString(inst, converter, pathIdVer)).append(", ");
233 246 });
234 247 int startInd = builder.lastIndexOf(", ");
235 248 if (startInd > 0) {
... ... @@ -238,11 +251,12 @@ public class LwM2mClient implements Cloneable {
238 251 builder.append("}]");
239 252 return builder.toString();
240 253 }
241   - public String instanceToString (LwM2mObjectInstance objectInstance, LwM2mValueConverterImpl converter, String pathIdVer) {
  254 +
  255 + public String instanceToString(LwM2mObjectInstance objectInstance, LwM2mValueConverterImpl converter, String pathIdVer) {
242 256 StringBuilder builder = new StringBuilder();
243 257 builder.append("LwM2mObjectInstance [id=").append(objectInstance.getId()).append(", resources={");
244 258 objectInstance.getResources().forEach((resId, res) -> {
245   - builder.append(resId).append("=").append(this.resourceToString (res, converter, pathIdVer)).append(", ");
  259 + builder.append(resId).append("=").append(this.resourceToString(res, converter, pathIdVer)).append(", ");
246 260 });
247 261 int startInd = builder.lastIndexOf(", ");
248 262 if (startInd > 0) {
... ... @@ -252,12 +266,11 @@ public class LwM2mClient implements Cloneable {
252 266 return builder.toString();
253 267 }
254 268
255   - public String resourceToString (LwM2mResource lwM2mResource, LwM2mValueConverterImpl converter, String pathIdVer) {
  269 + public String resourceToString(LwM2mResource lwM2mResource, LwM2mValueConverterImpl converter, String pathIdVer) {
256 270 if (!OPAQUE.equals(lwM2mResource.getType())) {
257 271 return lwM2mResource.isMultiInstances() ? ((LwM2mMultipleResource) lwM2mResource).toString() :
258 272 ((LwM2mSingleResource) lwM2mResource).toString();
259   - }
260   - else {
  273 + } else {
261 274 return String.format("LwM2mSingleResource [id=%s, value=%s, type=%s]", lwM2mResource.getId(),
262 275 converter.convertValue(lwM2mResource.getValue(),
263 276 OPAQUE, STRING, new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))), lwM2mResource.getType().name());
... ... @@ -275,7 +288,8 @@ public class LwM2mClient implements Cloneable {
275 288 resources.add(LwM2mSingleResource.newResource(resId, converter.convertValue(params,
276 289 equalsResourceTypeGetSimpleName(params), resourceModel.type, pathIds), resourceModel.type));
277 290
278   - }});
  291 + }
  292 + });
279 293 return resources;
280 294 }
281 295
... ... @@ -291,7 +305,8 @@ public class LwM2mClient implements Cloneable {
291 305 resources.add(LwM2mSingleResource.newResource(resId,
292 306 converter.convertValue(value, equalsResourceTypeGetSimpleName(value), resourceModel.type, pathIds), resourceModel.type));
293 307
294   - }});
  308 + }
  309 + });
295 310 return resources;
296 311 }
297 312
... ...
... ... @@ -35,8 +35,6 @@ public interface LwM2mClientContext {
35 35
36 36 LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo);
37 37
38   - LwM2mClient getClient(UUID sessionId);
39   -
40 38 LwM2mClient getOrRegister(Registration registration);
41 39
42 40 LwM2mClient registerOrUpdate(Registration registration);
... ...
... ... @@ -83,13 +83,11 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
83 83
84 84 @Override
85 85 public LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo) {
86   - return getClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
87   - }
  86 + return lwM2mClientsByEndpoint.values().stream().filter(c ->
  87 + (new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()))
  88 + .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB())))
88 89
89   - @Override
90   - public LwM2mClient getClient(UUID sessionId) {
91   - //TODO: refactor this to search by sessionId efficiently.
92   - return lwM2mClientsByEndpoint.values().stream().filter(c -> c.getSessionId().equals(sessionId)).findAny().get();
  90 + ).findAny().get();
93 91 }
94 92
95 93 @Override
... ...
... ... @@ -286,7 +286,7 @@ public class LwM2mFwSwUpdate {
286 286 Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
287 287 String value = FIRMWARE.equals(this.type) ? LwM2mTransportUtil.UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type :
288 288 LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type;
289   - String key = splitCamelCaseString((String) this.lwM2MClient.getResourceName(null, this.pathResultId));
  289 + String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId));
290 290 if (success) {
291 291 this.stateUpdate = FirmwareUpdateStatus.UPDATED.name();
292 292 this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null);
... ...
... ... @@ -15,94 +15,93 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.server.client;
17 17
  18 +import com.google.gson.Gson;
18 19 import com.google.gson.JsonObject;
  20 +import com.google.gson.reflect.TypeToken;
19 21 import lombok.Data;
20 22 import lombok.extern.slf4j.Slf4j;
21   -import org.eclipse.leshan.core.request.ContentFormat;
  23 +import org.apache.commons.lang3.StringUtils;
  24 +import org.eclipse.leshan.core.node.LwM2mPath;
22 25 import org.eclipse.leshan.server.registration.Registration;
23 26 import org.thingsboard.server.gen.transport.TransportProtos;
24   -import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
25   -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
  27 +import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler;
26 28
  29 +import java.util.Map;
  30 +import java.util.Objects;
27 31 import java.util.concurrent.ConcurrentHashMap;
  32 +import java.util.concurrent.TimeoutException;
28 33
  34 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.ERROR_KEY;
  35 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FINISH_JSON_KEY;
  36 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FINISH_VALUE_KEY;
  37 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.INFO_KEY;
  38 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.KEY_NAME_KEY;
  39 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
  40 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_ALL;
  41 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
  42 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL;
  43 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
  44 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES;
  45 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
  46 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE;
  47 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.METHOD_KEY;
  48 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.PARAMS_KEY;
  49 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESULT_KEY;
  50 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SEPARATOR_KEY;
  51 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.START_JSON_KEY;
  52 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TARGET_ID_VER_KEY;
  53 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.VALUE_KEY;
  54 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId;
29 55 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validPathIdVer;
30 56
31 57 @Slf4j
32 58 @Data
33 59 public class Lwm2mClientRpcRequest {
34   - public final String targetIdVerKey = "targetIdVer";
35   - public final String keyNameKey = "key";
36   - public final String typeOperKey = "typeOper";
37   - public final String contentFormatNameKey = "contentFormatName";
38   - public final String valueKey = "value";
39   - public final String infoKey = "info";
40   - public final String paramsKey = "params";
41   - public final String timeoutInMsKey = "timeOutInMs";
42   - public final String resultKey = "result";
43   - public final String errorKey = "error";
44   - public final String methodKey = "methodName";
  60 +
  61 + private Registration registration;
  62 + private TransportProtos.SessionInfoProto sessionInfo;
  63 + private String bodyParams;
  64 + private int requestId;
45 65
46 66 private LwM2mTypeOper typeOper;
  67 + private String key;
47 68 private String targetIdVer;
48   - private String contentFormatName;
49   - private long timeoutInMs;
50 69 private Object value;
51   - private ConcurrentHashMap<String, Object> params;
52   - private SessionInfoProto sessionInfo;
53   - private int requestId;
  70 + private Map<String, Object> params;
  71 +
54 72 private String errorMsg;
55 73 private String valueMsg;
56 74 private String infoMsg;
57 75 private String responseCode;
58 76
59   - public void setValidTypeOper(String typeOper) {
60   - try {
61   - this.typeOper = LwM2mTypeOper.fromLwLwM2mTypeOper(typeOper);
62   - } catch (Exception e) {
63   - this.errorMsg = this.methodKey + " - " + typeOper + " is not valid.";
64   - }
  77 + public Lwm2mClientRpcRequest() {
65 78 }
66 79
67   - public void setValidContentFormatName(JsonObject rpcRequest) {
68   - try {
69   - if (ContentFormat.fromName(rpcRequest.get(this.contentFormatNameKey).getAsString()) != null) {
70   - this.contentFormatName = rpcRequest.get(this.contentFormatNameKey).getAsString();
71   - } else {
72   - this.errorMsg = this.contentFormatNameKey + " - " + rpcRequest.get(this.contentFormatNameKey).getAsString() + " is not valid.";
73   - }
74   - } catch (Exception e) {
75   - this.errorMsg = this.contentFormatNameKey + " - " + rpcRequest.get(this.contentFormatNameKey).getAsString() + " is not valid.";
  80 + public Lwm2mClientRpcRequest(LwM2mTypeOper lwM2mTypeOper, String bodyParams, int requestId,
  81 + TransportProtos.SessionInfoProto sessionInfo, Registration registration, DefaultLwM2MTransportMsgHandler handler) {
  82 + this.registration = registration;
  83 + this.sessionInfo = sessionInfo;
  84 + this.requestId = requestId;
  85 + if (lwM2mTypeOper != null) {
  86 + this.typeOper = lwM2mTypeOper;
  87 + } else {
  88 + this.errorMsg = METHOD_KEY + " - " + typeOper + " is not valid.";
76 89 }
77   - }
78   -
79   - public void setValidTargetIdVerKey(JsonObject rpcRequest, Registration registration) {
80   - if (rpcRequest.has(this.targetIdVerKey)) {
81   - String targetIdVerStr = rpcRequest.get(targetIdVerKey).getAsString();
82   - // targetIdVer without ver - ok
83   - try {
84   - // targetIdVer with/without ver - ok
85   - this.targetIdVer = validPathIdVer(targetIdVerStr, registration);
86   - if (this.targetIdVer != null) {
87   - this.infoMsg = String.format("Changed by: pathIdVer - %s", this.targetIdVer);
88   - }
89   - } catch (Exception e) {
90   - if (this.targetIdVer == null) {
91   - this.errorMsg = this.targetIdVerKey + " - " + targetIdVerStr + " is not valid.";
92   - }
93   - }
  90 + if (this.errorMsg == null && !bodyParams.equals("null")) {
  91 + this.bodyParams = bodyParams;
  92 + this.init(handler);
94 93 }
95 94 }
96 95
97 96 public TransportProtos.ToDeviceRpcResponseMsg getDeviceRpcResponseResultMsg() {
98 97 JsonObject payloadResp = new JsonObject();
99   - payloadResp.addProperty(this.resultKey, this.responseCode);
  98 + payloadResp.addProperty(RESULT_KEY, this.responseCode);
100 99 if (this.errorMsg != null) {
101   - payloadResp.addProperty(this.errorKey, this.errorMsg);
  100 + payloadResp.addProperty(ERROR_KEY, this.errorMsg);
102 101 } else if (this.valueMsg != null) {
103   - payloadResp.addProperty(this.valueKey, this.valueMsg);
  102 + payloadResp.addProperty(VALUE_KEY, this.valueMsg);
104 103 } else if (this.infoMsg != null) {
105   - payloadResp.addProperty(this.infoKey, this.infoMsg);
  104 + payloadResp.addProperty(INFO_KEY, this.infoMsg);
106 105 }
107 106 return TransportProtos.ToDeviceRpcResponseMsg.newBuilder()
108 107 .setPayload(payloadResp.getAsJsonObject().toString())
... ... @@ -110,13 +109,171 @@ public class Lwm2mClientRpcRequest {
110 109 .build();
111 110 }
112 111
113   - @Override
114   - public Object clone() {
  112 + private void init(DefaultLwM2MTransportMsgHandler handler) {
115 113 try {
116   - return super.clone();
117   - } catch (CloneNotSupportedException e) {
118   - log.error("", e);
  114 + // #1
  115 + if (this.bodyParams.contains(KEY_NAME_KEY)) {
  116 + String targetIdVerStr = this.getValueKeyFromBody(KEY_NAME_KEY);
  117 + if (targetIdVerStr != null) {
  118 + String targetIdVer = handler.getPresentPathIntoProfile(sessionInfo, targetIdVerStr);
  119 + if (targetIdVer != null) {
  120 + this.targetIdVer = targetIdVer;
  121 + this.setInfoMsg(String.format("Changed by: key - %s, pathIdVer - %s",
  122 + targetIdVerStr, targetIdVer));
  123 + }
  124 + }
  125 + }
  126 + if (this.getTargetIdVer() == null && this.bodyParams.contains(TARGET_ID_VER_KEY)) {
  127 + this.setValidTargetIdVerKey();
  128 + }
  129 + if (this.bodyParams.contains(VALUE_KEY)) {
  130 + this.value = this.getValueKeyFromBody(VALUE_KEY);
  131 + }
  132 + try {
  133 + if (this.bodyParams.contains(PARAMS_KEY)) {
  134 + this.setValidParamsKey(handler);
  135 + }
  136 + } catch (Exception e) {
  137 + this.setErrorMsg(String.format("Params of request is bad Json format. %s", e.getMessage()));
  138 + }
  139 +
  140 + if (this.getTargetIdVer() == null
  141 + && !(OBSERVE_READ_ALL == this.getTypeOper()
  142 + || DISCOVER_ALL == this.getTypeOper()
  143 + || OBSERVE_CANCEL == this.getTypeOper())) {
  144 + this.setErrorMsg(TARGET_ID_VER_KEY + " and " +
  145 + KEY_NAME_KEY + " is null or bad format");
  146 + }
  147 + /**
  148 + * EXECUTE && WRITE_REPLACE - only for Resource or ResourceInstance
  149 + */
  150 + else if (this.getTargetIdVer() != null
  151 + && (EXECUTE == this.getTypeOper()
  152 + || WRITE_REPLACE == this.getTypeOper())
  153 + && !(new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.getTargetIdVer()))).isResource()
  154 + || new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.getTargetIdVer()))).isResourceInstance())) {
  155 + this.setErrorMsg("Invalid parameter " + TARGET_ID_VER_KEY
  156 + + ". Only Resource or ResourceInstance can be this operation");
  157 + }
  158 + } catch (Exception e) {
  159 + this.setErrorMsg(String.format("Bad format request. %s", e.getMessage()));
  160 + }
  161 +
  162 + }
  163 +
  164 + private void setValidTargetIdVerKey() {
  165 + String targetIdVerStr = this.getValueKeyFromBody(TARGET_ID_VER_KEY);
  166 + // targetIdVer without ver - ok
  167 + try {
  168 + // targetIdVer with/without ver - ok
  169 + this.targetIdVer = validPathIdVer(targetIdVerStr, this.registration);
  170 + if (this.targetIdVer != null) {
  171 + this.infoMsg = String.format("Changed by: pathIdVer - %s", this.targetIdVer);
  172 + }
  173 + } catch (Exception e) {
  174 + if (this.targetIdVer == null) {
  175 + this.errorMsg = TARGET_ID_VER_KEY + " - " + targetIdVerStr + " is not valid.";
  176 + }
  177 + }
  178 + }
  179 +
  180 + private void setValidParamsKey(DefaultLwM2MTransportMsgHandler handler) {
  181 + String paramsStr = this.getValueKeyFromBody(PARAMS_KEY);
  182 + if (paramsStr != null) {
  183 + String params2Json =
  184 + START_JSON_KEY
  185 + + "\""
  186 + + paramsStr
  187 + .replaceAll(SEPARATOR_KEY, "\"" + SEPARATOR_KEY + "\"")
  188 + .replaceAll(FINISH_VALUE_KEY, "\"" + FINISH_VALUE_KEY + "\"")
  189 + + "\""
  190 + + FINISH_JSON_KEY;
  191 + // jsonObject
  192 + Map<String, Object> params = new Gson().fromJson(params2Json, new TypeToken<ConcurrentHashMap<String, Object>>() {
  193 + }.getType());
  194 + if (WRITE_UPDATE == this.getTypeOper()) {
  195 + if (this.targetIdVer != null) {
  196 + Map<String, Object> paramsResourceId = this.convertParamsToResourceId((ConcurrentHashMap<String, Object>) params, handler);
  197 + if (paramsResourceId.size() > 0) {
  198 + this.setParams(paramsResourceId);
  199 + }
  200 + }
  201 + } else if (WRITE_ATTRIBUTES == this.getTypeOper()) {
  202 + this.setParams(params);
  203 + }
  204 + }
  205 + }
  206 +
  207 + private String getValueKeyFromBody(String key) {
  208 + String valueKey = null;
  209 + int startInd = -1;
  210 + int finishInd = -1;
  211 + try {
  212 + switch (key) {
  213 + case KEY_NAME_KEY:
  214 + case TARGET_ID_VER_KEY:
  215 + case VALUE_KEY:
  216 + startInd = this.bodyParams.indexOf(SEPARATOR_KEY, this.bodyParams.indexOf(key));
  217 + finishInd = this.bodyParams.indexOf(FINISH_VALUE_KEY, this.bodyParams.indexOf(key));
  218 + if (startInd >= 0 && finishInd < 0) {
  219 + finishInd = this.bodyParams.indexOf(FINISH_JSON_KEY, this.bodyParams.indexOf(key));
  220 + }
  221 + break;
  222 + case PARAMS_KEY:
  223 + startInd = this.bodyParams.indexOf(START_JSON_KEY, this.bodyParams.indexOf(key));
  224 + finishInd = this.bodyParams.indexOf(FINISH_JSON_KEY, this.bodyParams.indexOf(key));
  225 + }
  226 + if (startInd >= 0 && finishInd > 0) {
  227 + valueKey = this.bodyParams.substring(startInd + 1, finishInd);
  228 + }
  229 + } catch (Exception e) {
  230 + log.error("", new TimeoutException());
  231 + }
  232 + /**
  233 + * ReplaceAll "\""
  234 + */
  235 + if (StringUtils.trimToNull(valueKey) != null) {
  236 + char[] chars = valueKey.toCharArray();
  237 + for (int i = 0; i < chars.length; i++) {
  238 + if (chars[i] == 92 || chars[i] == 34) chars[i] = 32;
  239 + }
  240 + return key.equals(PARAMS_KEY) ? String.valueOf(chars) : String.valueOf(chars).replaceAll(" ", "");
119 241 }
120 242 return null;
121 243 }
  244 +
  245 + private ConcurrentHashMap<String, Object> convertParamsToResourceId(ConcurrentHashMap<String, Object> params,
  246 + DefaultLwM2MTransportMsgHandler serviceImpl) {
  247 + Map<String, Object> paramsIdVer = new ConcurrentHashMap<>();
  248 + LwM2mPath targetId = new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.targetIdVer)));
  249 + if (targetId.isObjectInstance()) {
  250 + params.forEach((k, v) -> {
  251 + try {
  252 + int id = Integer.parseInt(k);
  253 + paramsIdVer.put(String.valueOf(id), v);
  254 + } catch (NumberFormatException e) {
  255 + String targetIdVer = serviceImpl.getPresentPathIntoProfile(sessionInfo, k);
  256 + if (targetIdVer != null) {
  257 + LwM2mPath lwM2mPath = new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(targetIdVer)));
  258 + paramsIdVer.put(String.valueOf(lwM2mPath.getResourceId()), v);
  259 + }
  260 + /** WRITE_UPDATE*/
  261 + else {
  262 + String rezId = this.getRezIdByResourceNameAndObjectInstanceId(k, serviceImpl);
  263 + if (rezId != null) {
  264 + paramsIdVer.put(rezId, v);
  265 + }
  266 + }
  267 + }
  268 + });
  269 + }
  270 + return (ConcurrentHashMap<String, Object>) paramsIdVer;
  271 + }
  272 +
  273 + private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MTransportMsgHandler handler) {
  274 + LwM2mClient lwM2mClient = handler.clientContext.getClient(this.sessionInfo);
  275 + return lwM2mClient != null ?
  276 + lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) :
  277 + null;
  278 + }
122 279 }
... ...