From 4f2e72071d757acda77718ea7ce422a984ecc59b Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 28 May 2021 07:45:11 +0300 Subject: [PATCH] LWM2M: add requestUUID --- .../DefaultLwM2MTransportMsgHandler.java | 202 +++++-------- .../lwm2m/server/LwM2mServerListener.java | 6 - .../lwm2m/server/LwM2mTransportRequest.java | 144 +++++---- .../lwm2m/server/LwM2mTransportUtil.java | 110 +++++-- .../lwm2m/server/client/LwM2mClient.java | 43 ++- .../server/client/LwM2mClientContext.java | 2 - .../server/client/LwM2mClientContextImpl.java | 10 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 2 +- .../server/client/Lwm2mClientRpcRequest.java | 273 ++++++++++++++---- 9 files changed, 490 insertions(+), 302 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index 986085a7c9..bcbf5a6f00 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -23,6 +23,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mObject; @@ -61,6 +62,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; @@ -99,21 +101,20 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_STRATEGY_2; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_All; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertJsonArrayToSet; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getAckCallback; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.setValidTypeOper; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey; @@ -125,7 +126,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler private ExecutorService registrationExecutor; private ExecutorService updateRegistrationExecutor; private ExecutorService unRegistrationExecutor; - private LwM2mValueConverterImpl converter; + public LwM2mValueConverterImpl converter; private final TransportService transportService; private final LwM2mTransportContext context; @@ -133,10 +134,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public final FirmwareDataCache firmwareDataCache; public final LwM2mTransportServerHelper helper; private final LwM2MJsonAdaptor adaptor; - private final LwM2mClientContext clientContext; - private final LwM2mTransportRequest lwM2mTransportRequest; private final TbLwM2MDtlsSessionStore sessionStore; - + public final LwM2mClientContext clientContext; + public final LwM2mTransportRequest lwM2mTransportRequest; + private final Map rpcSubscriptions; public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, @@ -151,6 +152,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler this.firmwareDataCache = firmwareDataCache; this.context = context; this.adaptor = adaptor; + this.rpcSubscriptions = new ConcurrentHashMap<>(); this.sessionStore = sessionStore; } @@ -241,16 +243,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler /** * @param registration - Registration LwM2M Client - * @param observations - All paths observations before unReg - * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect + * @param observations - !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect */ public void unReg(Registration registration, Collection observations) { + log.error("Client unRegistration -> test", new RuntimeException()); unRegistrationExecutor.submit(() -> { try { - this.setCancelObservationsAll(registration); this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); this.closeClientSession(registration); - ; } catch (Throwable t) { log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()), registration.getId()); @@ -285,12 +285,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler @Override public void setCancelObservationsAll(Registration registration) { if (registration != null) { - lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL, + lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL_ALL, null, null, this.config.getTimeout(), null); -// Set observations = context.getServer().getObservationService().getObservations(registration); -// observations.forEach(observation -> lwM2mTransportRequest.sendAllRequest(registration, -// convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), OBSERVE_CANCEL, -// null, null, this.config.getTimeout(), null)); } } @@ -354,12 +350,13 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler */ @Override public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { - LwM2mClient lwM2MClient = clientContext.getClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); + LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo); if (msg.getSharedUpdatedCount() > 0) { msg.getSharedUpdatedList().forEach(tsKvProto -> { String pathName = tsKvProto.getKv().getKey(); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); Object valueNew = getValueFromKvProto(tsKvProto.getKv()); + log.warn("12) Shared AttributeUpdate start pathName [{}], pathIdVer [{}], valueNew [{}]", pathName, pathIdVer, valueNew); if ((FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion()))) || (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.TITLE).equals(pathName) @@ -438,121 +435,49 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler clientContext.getLwM2mClients().forEach(e -> e.deleteResources(pathIdVer, this.config.getModelProvider())); } + /** + * #1 del from rpcSubscriptions by timeout + * #2 if not present in rpcSubscriptions by requestId: create new Lwm2mClientRpcRequest, after success - add requestId, timeout + */ @Override public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) { - log.warn("4) RPC-OK finish to [{}]", toDeviceRpcRequestMsg); - Lwm2mClientRpcRequest lwm2mClientRpcRequest = null; - try { - Registration registration = clientContext.getClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).getRegistration(); - lwm2mClientRpcRequest = this.getDeviceRpcRequest(toDeviceRpcRequestMsg, sessionInfo, registration); - if (lwm2mClientRpcRequest.getErrorMsg() != null) { - lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); - this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); - } else { - lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), lwm2mClientRpcRequest.getContentFormatName(), - lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), - this.config.getTimeout(), lwm2mClientRpcRequest); - } - } catch (Exception e) { - if (lwm2mClientRpcRequest == null) { - lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(); - } - lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); - if (lwm2mClientRpcRequest.getErrorMsg() == null) { - lwm2mClientRpcRequest.setErrorMsg(e.getMessage()); - } - this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); - } - } - - private Lwm2mClientRpcRequest getDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest, - SessionInfoProto sessionInfo, Registration registration) throws IllegalArgumentException { - Lwm2mClientRpcRequest lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(); - try { - lwm2mClientRpcRequest.setRequestId(toDeviceRequest.getRequestId()); - lwm2mClientRpcRequest.setSessionInfo(sessionInfo); - lwm2mClientRpcRequest.setValidTypeOper(toDeviceRequest.getMethodName()); - JsonObject rpcRequest = LwM2mTransportUtil.validateJson(toDeviceRequest.getParams()); - if (rpcRequest != null) { - if (rpcRequest.has(lwm2mClientRpcRequest.keyNameKey)) { - String targetIdVer = this.getPresentPathIntoProfile(sessionInfo, - rpcRequest.get(lwm2mClientRpcRequest.keyNameKey).getAsString()); - if (targetIdVer != null) { - lwm2mClientRpcRequest.setTargetIdVer(targetIdVer); - lwm2mClientRpcRequest.setInfoMsg(String.format("Changed by: key - %s, pathIdVer - %s", - rpcRequest.get(lwm2mClientRpcRequest.keyNameKey).getAsString(), targetIdVer)); - } - } - if (lwm2mClientRpcRequest.getTargetIdVer() == null) { - lwm2mClientRpcRequest.setValidTargetIdVerKey(rpcRequest, registration); - } - if (rpcRequest.has(lwm2mClientRpcRequest.contentFormatNameKey)) { - lwm2mClientRpcRequest.setValidContentFormatName(rpcRequest); - } - if (rpcRequest.has(lwm2mClientRpcRequest.timeoutInMsKey) && rpcRequest.get(lwm2mClientRpcRequest.timeoutInMsKey).getAsLong() > 0) { - lwm2mClientRpcRequest.setTimeoutInMs(rpcRequest.get(lwm2mClientRpcRequest.timeoutInMsKey).getAsLong()); - } - if (rpcRequest.has(lwm2mClientRpcRequest.valueKey)) { - lwm2mClientRpcRequest.setValue(rpcRequest.get(lwm2mClientRpcRequest.valueKey).getAsString()); - } - if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) { - ConcurrentHashMap params = new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey) - .getAsJsonObject().toString(), new TypeToken>() { - }.getType()); - if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()) { - ConcurrentHashMap paramsResourceId = convertParamsToResourceId(params, sessionInfo); - if (paramsResourceId.size() > 0) { - lwm2mClientRpcRequest.setParams(paramsResourceId); - } - } else { - lwm2mClientRpcRequest.setParams(params); - } - } else if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonArray()) { - new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey) - .getAsJsonObject().toString(), new TypeToken>() { - }.getType()); + // #1 + this.checkRpcRequestTimeout(); + String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null"; + LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName()); + UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB()); + log.warn("4) RPC-OK finish to [{}], keys: [{}]", requestUUID, this.rpcSubscriptions.keySet()); + if (!this.rpcSubscriptions.containsKey(requestUUID)) { + this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime()); + Lwm2mClientRpcRequest lwm2mClientRpcRequest = null; + try { + Registration registration = clientContext.getClient(sessionInfo).getRegistration(); + lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this); + if (lwm2mClientRpcRequest.getErrorMsg() != null) { + lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); + this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); + } else { + lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), + null, + lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), + this.config.getTimeout(), lwm2mClientRpcRequest); } - lwm2mClientRpcRequest.setSessionInfo(sessionInfo); - if (!(OBSERVE_READ_ALL == lwm2mClientRpcRequest.getTypeOper() - || DISCOVER_All == lwm2mClientRpcRequest.getTypeOper() - || OBSERVE_CANCEL == lwm2mClientRpcRequest.getTypeOper()) - && lwm2mClientRpcRequest.getTargetIdVer() == null) { - lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " + - lwm2mClientRpcRequest.keyNameKey + " is null or bad format"); + } catch (Exception e) { + if (lwm2mClientRpcRequest == null) { + lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(); } - /** - * EXECUTE && WRITE_REPLACE - only for Resource or ResourceInstance - */ - else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper() - || WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper()) - && lwm2mClientRpcRequest.getTargetIdVer() != null - && !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource() - || new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) { - lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey - + ". Only Resource or ResourceInstance can be this operation"); + lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); + if (lwm2mClientRpcRequest.getErrorMsg() == null) { + lwm2mClientRpcRequest.setErrorMsg(e.getMessage()); } - } else { - lwm2mClientRpcRequest.setErrorMsg("Params of request is bad Json format."); + this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); } - } catch (Exception e) { - throw new IllegalArgumentException(lwm2mClientRpcRequest.getErrorMsg()); } - return lwm2mClientRpcRequest; } - private ConcurrentHashMap convertParamsToResourceId(ConcurrentHashMap params, - SessionInfoProto sessionInfo) { - ConcurrentHashMap paramsIdVer = new ConcurrentHashMap<>(); - params.forEach((k, v) -> { - String targetIdVer = this.getPresentPathIntoProfile(sessionInfo, k); - if (targetIdVer != null) { - LwM2mPath targetId = new LwM2mPath(convertPathFromIdVerToObjectId(targetIdVer)); - if (targetId.isResource()) { - paramsIdVer.put(String.valueOf(targetId.getResourceId()), v); - } - } - }); - return paramsIdVer; + private void checkRpcRequestTimeout() { + Set rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> System.currentTimeMillis() > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet()); + rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove); } public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { @@ -722,10 +647,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * set setClient_fw_info... = value **/ if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getFwUpdate().initReadValue(this, lwM2mTransportRequest, path); + lwM2MClient.getFwUpdate().initReadValue(this, this.lwM2mTransportRequest, path); } if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getSwUpdate().initReadValue(this, lwM2mTransportRequest, path); + lwM2MClient.getSwUpdate().initReadValue(this, this.lwM2mTransportRequest, path); } /** @@ -742,7 +667,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler && (convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { if (DOWNLOADED.name().equals(lwM2MClient.getFwUpdate().getStateUpdate()) && lwM2MClient.getFwUpdate().conditionalFwExecuteStart()) { - lwM2MClient.getFwUpdate().executeFwSwWare(this, lwM2mTransportRequest); + lwM2MClient.getFwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest); } else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate()) && lwM2MClient.getFwUpdate().conditionalFwExecuteAfterSuccess()) { lwM2MClient.getFwUpdate().finishFwSwUpdate(this, true); @@ -767,7 +692,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler && (convertPathFromObjectIdToIdVer(SW_RESULT_ID, registration).equals(path))) { if (DOWNLOADED.name().equals(lwM2MClient.getSwUpdate().getStateUpdate()) && lwM2MClient.getSwUpdate().conditionalSwUpdateExecute()) { - lwM2MClient.getSwUpdate().executeFwSwWare(this, lwM2mTransportRequest); + lwM2MClient.getSwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest); } else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate()) && lwM2MClient.getSwUpdate().conditionalSwExecuteAfterSuccess()) { lwM2MClient.getSwUpdate().finishFwSwUpdate(this, true); @@ -970,11 +895,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @return - return value of Resource by idPath */ private LwM2mResource getResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) { - LwM2mResource resourceValue = null; - if (new LwM2mPath(convertPathFromIdVerToObjectId(path)).isResource()) { - resourceValue = lwM2MClient.getResources().get(path).getLwM2mResource(); + LwM2mResource lwm2mResourceValue = null; + ResourceValue resourceValue = lwM2MClient.getResources().get(path); + if (resourceValue != null) { + if (new LwM2mPath(convertPathFromIdVerToObjectId(path)).isResource()) { + lwm2mResourceValue = lwM2MClient.getResources().get(path).getLwM2mResource(); + } } - return resourceValue; + return lwm2mResourceValue; } /** @@ -1281,7 +1209,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @param name - * @return - */ - private String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) { + public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) { LwM2mClientProfile profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); LwM2mClient lwM2mClient = clientContext.getClient(sessionInfo); return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream() @@ -1304,7 +1232,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler this.updateAttributeFromThingsboard(tsKvProtos, sessionInfo); } catch (Exception e) { - log.error(String.valueOf(e)); + log.error("", e); } } @@ -1378,6 +1306,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler private void reportActivityAndRegister(SessionInfoProto sessionInfo) { if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) { transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); + this.reportActivitySubscription(sessionInfo); } } @@ -1510,4 +1439,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler return this.config; } + private void reportActivitySubscription(TransportProtos.SessionInfoProto sessionInfo) { + transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder() + .setAttributeSubscription(true) + .setRpcSubscription(true) + .setLastActivityTime(System.currentTimeMillis()) + .build(), TransportServiceCallback.EMPTY); + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index 93424b44e5..f0e11aceb4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -94,12 +94,6 @@ public class LwM2mServerListener { @Override public void onResponse(Observation observation, Registration registration, ObserveResponse response) { if (registration != null) { -// if (observation.getPath().isResource() || observation.getPath().isResourceInstance()) { -// String msg = String.format("%s: Successful Observation %s.", LOG_LW2M_INFO, -// observation.getPath()); -// log.warn(msg); -// service.sendLogsToThingsboard(msg, registration.getId()); -// } service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), response, null); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index 6a0b255a5b..e5cd289758 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -22,6 +22,8 @@ import org.eclipse.californium.core.coap.Response; import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mNode; +import org.eclipse.leshan.core.node.LwM2mObject; +import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mSingleResource; @@ -34,6 +36,7 @@ import org.eclipse.leshan.core.request.DownlinkRequest; import org.eclipse.leshan.core.request.ExecuteRequest; import org.eclipse.leshan.core.request.ObserveRequest; import org.eclipse.leshan.core.request.ReadRequest; +import org.eclipse.leshan.core.request.WriteAttributesRequest; import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.request.exception.ClientSleepingException; import org.eclipse.leshan.core.response.DeleteResponse; @@ -79,10 +82,12 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_All; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_REQUEST_CHANNEL; @@ -123,7 +128,7 @@ public class LwM2mTransportRequest { */ public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper, - String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { + String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) { try { String target = convertPathFromIdVerToObjectId(targetIdVer); ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT; @@ -132,45 +137,42 @@ public class LwM2mTransportRequest { if (!OBSERVE_READ_ALL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) { if (lwM2MClient.isValidObjectVersion(targetIdVer)) { timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; - DownlinkRequest request = createRequest (registration, lwM2MClient, typeOper, contentFormat, target, - targetIdVer, resultIds, params, rpcRequest); + DownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target, + targetIdVer, resultIds, params, lwm2mClientRpcRequest); if (request != null) { try { - this.sendRequest(registration, lwM2MClient, request, timeoutInMs, rpcRequest); + this.sendRequest(registration, lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest); } catch (ClientSleepingException e) { DownlinkRequest finalRequest = request; long finalTimeoutInMs = timeoutInMs; - Lwm2mClientRpcRequest finalRpcRequest = rpcRequest; + Lwm2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest; lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest)); } catch (Exception e) { log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e); } - } - else if (WRITE_UPDATE.name().equals(typeOper.name())) { - if (rpcRequest != null) { + } else if (WRITE_UPDATE.name().equals(typeOper.name())) { + if (lwm2mClientRpcRequest != null) { String errorMsg = String.format("Path %s params is not valid", targetIdVer); - handler.sentRpcRequest(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); + handler.sentRpcRequest(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); } - } - else if (WRITE_REPLACE.name().equals(typeOper.name()) || EXECUTE.name().equals(typeOper.name()) ) { - if (rpcRequest != null) { + } else if (WRITE_REPLACE.name().equals(typeOper.name()) || EXECUTE.name().equals(typeOper.name())) { + if (lwm2mClientRpcRequest != null) { String errorMsg = String.format("Path %s object model is absent", targetIdVer); - handler.sentRpcRequest(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); + handler.sentRpcRequest(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); } - } - else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) { + } else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) { log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer); - if (rpcRequest != null) { + if (lwm2mClientRpcRequest != null) { ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()); String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; - handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); + this.handler.sentRpcRequest(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); } } - } else if (rpcRequest != null) { + } else if (lwm2mClientRpcRequest != null) { String errorMsg = String.format("Path %s not found in object version", targetIdVer); - handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); + this.handler.sentRpcRequest(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); } - } else if (OBSERVE_READ_ALL.name().equals(typeOper.name()) || DISCOVER_All.name().equals(typeOper.name())) { + } else if (OBSERVE_READ_ALL.name().equals(typeOper.name()) || DISCOVER_ALL.name().equals(typeOper.name())) { Set paths; if (OBSERVE_READ_ALL.name().equals(typeOper.name())) { Set observations = context.getServer().getObservationService().getObservations(registration); @@ -179,34 +181,34 @@ public class LwM2mTransportRequest { assert registration != null; Link[] objectLinks = registration.getSortedObjectLinks(); paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet()); - String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, - typeOper.name(), paths); - handler.sendLogsToThingsboard(msg, registration.getId()); } - if (rpcRequest != null) { + String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, + typeOper.name(), paths); + this.handler.sendLogsToThingsboard(msg, registration.getId()); + if (lwm2mClientRpcRequest != null) { String valueMsg = String.format("Paths - %s", paths); - handler.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); + this.handler.sentRpcRequest(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); } - } else if (OBSERVE_CANCEL.name().equals(typeOper.name())) { + } else if (OBSERVE_CANCEL_ALL.name().equals(typeOper.name())) { int observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration); String observeCancelMsgAll = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, OBSERVE_CANCEL.name(), observeCancelCnt); - this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsgAll, rpcRequest); + this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsgAll, lwm2mClientRpcRequest); } } catch (Exception e) { String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, typeOper.name(), e.getMessage()); handler.sendLogsToThingsboard(msg, registration.getId()); - if (rpcRequest != null) { + if (lwm2mClientRpcRequest != null) { String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage()); - handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); + handler.sentRpcRequest(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); } } } - private DownlinkRequest createRequest (Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, - ContentFormat contentFormat, String target, String targetIdVer, - LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) { + private DownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, + ContentFormat contentFormat, String target, String targetIdVer, + LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) { DownlinkRequest request = null; switch (typeOper) { case READ: @@ -216,7 +218,7 @@ public class LwM2mTransportRequest { request = new DiscoverRequest(target); break; case OBSERVE: - String msg = String.format("%s: Send Observation %s.", LOG_LW2M_INFO, targetIdVer); + String msg = String.format("%s: Send Observation %s.", LOG_LW2M_INFO, targetIdVer); log.warn(msg); if (resultIds.isResource()) { Set observations = context.getServer().getObservationService().getObservations(registration); @@ -305,7 +307,7 @@ public class LwM2mTransportRequest { } break; case WRITE_ATTRIBUTES: - request = createWriteAttributeRequest(target, params); + request = createWriteAttributeRequest(target, params, this.handler); break; case DELETE: request = new DeleteRequest(target); @@ -347,10 +349,10 @@ public class LwM2mTransportRequest { set setClient_fw_info... = empty **/ if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getFwUpdate().initReadValue(handler, request.getPath().toString()); + lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); } if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getSwUpdate().initReadValue(handler, request.getPath().toString()); + lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); } if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage()); @@ -364,10 +366,10 @@ public class LwM2mTransportRequest { set setClient_fw_info... = empty **/ if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getFwUpdate().initReadValue(handler, request.getPath().toString()); + lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); } if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getSwUpdate().initReadValue(handler, request.getPath().toString()); + lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); } if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { this.afterWriteFwSWUpdateError(registration, request, e.getMessage()); @@ -473,7 +475,13 @@ public class LwM2mTransportRequest { } else if (response instanceof ExecuteResponse) { log.warn("[{}] Path [{}] ExecuteResponse 7_Send", pathIdVer, response); } else if (response instanceof WriteAttributesResponse) { + msgLog = String.format("%s: type operation: %s path: %s value: %s", + LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString()); + handler.sendLogsToThingsboard(msgLog, registration.getId()); log.warn("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response); + if (rpcRequest != null) { + handler.sentRpcRequest(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE); + } } else if (response instanceof WriteResponse) { log.warn("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response); this.infoWriteResponse(registration, response, request); @@ -494,29 +502,37 @@ public class LwM2mTransportRequest { private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request) { try { LwM2mNode node = ((WriteRequest) request).getNode(); - String msg; + String msg = null; Object value; - LwM2mSingleResource singleResource = (LwM2mSingleResource) node; - if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) { - int valueLength; - if (singleResource.getType() == ResourceModel.Type.STRING) { - valueLength = ((String) singleResource.getValue()).length(); - value = ((String) singleResource.getValue()) - .substring(Math.min(valueLength, config.getLogMaxLength())); + if (node instanceof LwM2mObject) { + msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s", + LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObject) node).toString()); + } else if (node instanceof LwM2mObjectInstance) { + msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s", + LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObjectInstance) node).prettyPrint()); + } else if (node instanceof LwM2mSingleResource) { + LwM2mSingleResource singleResource = (LwM2mSingleResource) node; + if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) { + int valueLength; + if (singleResource.getType() == ResourceModel.Type.STRING) { + valueLength = ((String) singleResource.getValue()).length(); + value = ((String) singleResource.getValue()) + .substring(Math.min(valueLength, config.getLogMaxLength())); + } else { + valueLength = ((byte[]) singleResource.getValue()).length; + value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()), + Math.min(valueLength, config.getLogMaxLength()))); + } + value = valueLength > config.getLogMaxLength() ? value + "..." : value; + msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s", + LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value); } else { - valueLength = ((byte[]) singleResource.getValue()).length; - value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()), - Math.min(valueLength, config.getLogMaxLength()))); + value = this.converter.convertValue(singleResource.getValue(), + singleResource.getType(), ResourceModel.Type.STRING, request.getPath()); + msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s", + LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value); } - value = valueLength > config.getLogMaxLength() ? value + "..." : value; - msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s", - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value); - } else { - value = this.converter.convertValue(singleResource.getValue(), - singleResource.getType(), ResourceModel.Type.STRING, request.getPath()); - msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s", - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value); } if (msg != null) { handler.sendLogsToThingsboard(msg, registration.getId()); @@ -538,11 +554,11 @@ public class LwM2mTransportRequest { LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) { lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name()); - lwM2MClient.getFwUpdate().sendLogs(WRITE_REPLACE.name(), LOG_LW2M_INFO, null); + lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); } if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) { lwM2MClient.getSwUpdate().setStateUpdate(DOWNLOADED.name()); - lwM2MClient.getSwUpdate().sendLogs(WRITE_REPLACE.name(), LOG_LW2M_INFO, null); + lwM2MClient.getSwUpdate().sendLogs(this.handler,WRITE_REPLACE.name(), LOG_LW2M_INFO, null); } } @@ -553,21 +569,21 @@ public class LwM2mTransportRequest { LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) { lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name()); - lwM2MClient.getFwUpdate().sendLogs(WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); + lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); } if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) { lwM2MClient.getSwUpdate().setStateUpdate(FAILED.name()); - lwM2MClient.getSwUpdate().sendLogs(WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); + lwM2MClient.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); } } private void afterExecuteFwSwUpdateError(Registration registration, DownlinkRequest request, String msgError) { LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) { - lwM2MClient.getFwUpdate().sendLogs(EXECUTE.name(), LOG_LW2M_ERROR, msgError); + lwM2MClient.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); } if (request.getPath().toString().equals(SW_INSTALL_ID) && lwM2MClient.getSwUpdate() != null) { - lwM2MClient.getSwUpdate().sendLogs(EXECUTE.name(), LOG_LW2M_ERROR, msgError); + lwM2MClient.getSwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index c84b4a52b2..eeedc4c275 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -61,9 +61,12 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION; +import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN; +import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN; import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD; import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; +import static org.eclipse.leshan.core.attributes.Attribute.STEP; import static org.eclipse.leshan.core.model.ResourceModel.Type.BOOLEAN; import static org.eclipse.leshan.core.model.ResourceModel.Type.FLOAT; import static org.eclipse.leshan.core.model.ResourceModel.Type.INTEGER; @@ -104,8 +107,7 @@ public class LwM2mTransportUtil { public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms - public static final String - LOG_LW2M_TELEMETRY = "LwM2MLog"; + public static final String LOG_LW2M_TELEMETRY = "logLwm2m"; public static final String LOG_LW2M_INFO = "info"; public static final String LOG_LW2M_ERROR = "error"; public static final String LOG_LW2M_WARN = "warn"; @@ -117,6 +119,23 @@ public class LwM2mTransportUtil { public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized"; public static final String LWM2M_VERSION_DEFAULT = "1.0"; + // RPC + public static final String TYPE_OPER_KEY = "typeOper"; + public static final String TARGET_ID_VER_KEY = "targetIdVer"; + public static final String KEY_NAME_KEY = "key"; + public static final String VALUE_KEY = "value"; + public static final String PARAMS_KEY = "params"; + public static final String SEPARATOR_KEY = ":"; + public static final String FINISH_VALUE_KEY = ","; + public static final String START_JSON_KEY = "{"; + public static final String FINISH_JSON_KEY = "}"; + // public static final String contentFormatNameKey = "contentFormatName"; + public static final String INFO_KEY = "info"; + // public static final String TIME_OUT_IN_MS = "timeOutInMs"; + public static final String RESULT_KEY = "result"; + public static final String ERROR_KEY = "error"; + public static final String METHOD_KEY = "methodName"; + // FirmWare public static final String FW_UPDATE = "Firmware update"; public static final Integer FW_ID = 5; @@ -182,20 +201,21 @@ public class LwM2mTransportUtil { */ READ(0, "Read"), DISCOVER(1, "Discover"), - DISCOVER_All(2, "DiscoverAll"), + DISCOVER_ALL(2, "DiscoverAll"), OBSERVE_READ_ALL(3, "ObserveReadAll"), /** * POST */ OBSERVE(4, "Observe"), OBSERVE_CANCEL(5, "ObserveCancel"), - EXECUTE(6, "Execute"), + OBSERVE_CANCEL_ALL(6, "ObserveCancelAll"), + EXECUTE(7, "Execute"), /** * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see * section 5.3.3 of the LW M2M spec). * if all resources are to be replaced */ - WRITE_REPLACE(7, "WriteReplace"), + WRITE_REPLACE(8, "WriteReplace"), /* PUT */ @@ -204,18 +224,16 @@ public class LwM2mTransportUtil { * 5.3.3 of the LW M2M spec). * if this is a partial update request */ - WRITE_UPDATE(8, "WriteUpdate"), - WRITE_ATTRIBUTES(9, "WriteAttributes"), - DELETE(10, "Delete"), + WRITE_UPDATE(9, "WriteUpdate"), + WRITE_ATTRIBUTES(10, "WriteAttributes"), + DELETE(11, "Delete"); // only for RPC - FW_READ_INFO(11, "FirmwareReadInfo"), - FW_UPDATE(12, "FirmwareUpdate"), - FW_UPDATE_URL(14, "FirmwareUpdateUrl"), - SW_READ_INFO(15, "SoftwareReadInfo"), - SW_UPDATE(16, "SoftwareUpdate"), - SW_UPDATE_URL(17, "SoftwareUpdateUrl"), - SW_UNINSTALL(18, "SoftwareUninstall"); +// FW_READ_INFO(12, "FirmwareReadInfo"), +// FW_UPDATE(13, "FirmwareUpdate"), +// SW_READ_INFO(15, "SoftwareReadInfo"), +// SW_UPDATE(16, "SoftwareUpdate"), +// SW_UNINSTALL(18, "SoftwareUninstall"); public int code; public String type; @@ -817,26 +835,29 @@ public class LwM2mTransportUtil { * Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60"); * Attribute [] attrs = {gt, st}; */ - public static DownlinkRequest createWriteAttributeRequest(String target, Object params) { - AttributeSet attrSet = new AttributeSet(createWriteAttributes(params)); + public static DownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MTransportMsgHandler serviceImpl) { + AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target)); return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null; } - private static Attribute[] createWriteAttributes(Object params) { + private static Attribute[] createWriteAttributes(Object params, DefaultLwM2MTransportMsgHandler serviceImpl, String target) { List attributeLists = new ArrayList<>(); ObjectMapper oMapper = new ObjectMapper(); Map map = oMapper.convertValue(params, ConcurrentHashMap.class); map.forEach((k, v) -> { - if (!v.toString().isEmpty() || (v.toString().isEmpty() && OBJECT_VERSION.equals(k))) { - attributeLists.add(new Attribute(k, - (DIMENSION.equals(k) || MINIMUM_PERIOD.equals(k) || MAXIMUM_PERIOD.equals(k)) ? - ((Double) v).longValue() : v)); + if (StringUtils.trimToNull(v.toString()) != null) { + Object attrValue = convertWriteAttributes(k, v, serviceImpl, target); + if (attrValue != null) { + Attribute attribute = createAttribute(k, attrValue); + if (attribute != null) { + attributeLists.add(new Attribute(k, attrValue)); + } + } } }); return attributeLists.toArray(Attribute[]::new); } - public static Set convertJsonArrayToSet(JsonArray jsonArray) { List attributeListOld = new Gson().fromJson(jsonArray, new TypeToken>() { }.getType()); @@ -863,4 +884,47 @@ public class LwM2mTransportUtil { return null; } } + + public static LwM2mTypeOper setValidTypeOper(String typeOper) { + try { + return LwM2mTransportUtil.LwM2mTypeOper.fromLwLwM2mTypeOper(typeOper); + } catch (Exception e) { + return null; + } + } + + public static Object convertWriteAttributes(String type, Object value, DefaultLwM2MTransportMsgHandler serviceImpl, String target) { + switch (type) { + /** Integer [0:255]; */ + case DIMENSION: + Long dim = (Long) serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), INTEGER, new LwM2mPath(target)); + return dim >= 0 && dim <= 255 ? dim : null; + /**String;*/ + case OBJECT_VERSION: + return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), STRING, new LwM2mPath(target)); + /**INTEGER */ + case MINIMUM_PERIOD: + case MAXIMUM_PERIOD: + return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), INTEGER, new LwM2mPath(target)); + /**Float; */ + case GREATER_THAN: + case LESSER_THAN: + case STEP: + if (value.getClass().getSimpleName().equals("String") ) { + value = Double.valueOf((String) value); + } + return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), FLOAT, new LwM2mPath(target)); + default: + return null; + } + } + + private static Attribute createAttribute(String key, Object attrValue) { + try { + return new Attribute(key, attrValue); + } catch (Exception e) { + log.error("CreateAttribute, not valid parameter key: [{}], attrValue: [{}], error: [{}]", key, attrValue, e.getMessage()); + return null; + } + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index d3f1fb4343..9ef45c3b3b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -31,8 +31,8 @@ import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.security.SecurityInfo; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler; @@ -117,7 +117,6 @@ public class LwM2mClient implements Cloneable { this.pendingReadRequests = new CopyOnWriteArrayList<>(); this.resources = new ConcurrentHashMap<>(); this.profileId = profileId; - this.sessionId = sessionId; this.init = false; this.queuedRequests = new ConcurrentLinkedQueue<>(); @@ -194,17 +193,31 @@ public class LwM2mClient implements Cloneable { public Object getResourceValue(String pathRezIdVer, String pathRezId) { String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer; if (this.resources.get(pathRez) != null) { - return this.resources.get(pathRez).getLwM2mResource().isMultiInstances() ? + return this.resources.get(pathRez).getLwM2mResource().isMultiInstances() ? this.resources.get(pathRez).getLwM2mResource().getValues() : this.resources.get(pathRez).getLwM2mResource().getValue(); } return null; } - public Object getResourceName (String pathRezIdVer, String pathRezId) { + public Object getResourceNameByRezId(String pathRezIdVer, String pathRezId) { String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer; if (this.resources.get(pathRez) != null) { - return this.resources.get(pathRez).getResourceModel().name; + return this.resources.get(pathRez).getResourceModel().name; + } + return null; + } + + public String getRezIdByResourceNameAndObjectInstanceId(String resourceName, String pathObjectInstanceIdVer, LwM2mModelProvider modelProvider) { + LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathObjectInstanceIdVer)); + if (pathIds.isObjectInstance()) { + Set rezIds = modelProvider.getObjectModel(registration) + .getObjectModel(pathIds.getObjectId()).resources.entrySet() + .stream() + .filter(map -> resourceName.equals(map.getValue().name)) + .map(map -> map.getKey()) + .collect(Collectors.toSet()); + return rezIds.size() > 0 ? String.valueOf(rezIds.stream().findFirst().get()) : null; } return null; } @@ -225,11 +238,11 @@ public class LwM2mClient implements Cloneable { .getObjectModel(pathIds.getObjectId()) : null; } - public String objectToString (LwM2mObject lwM2mObject, LwM2mValueConverterImpl converter, String pathIdVer) { + public String objectToString(LwM2mObject lwM2mObject, LwM2mValueConverterImpl converter, String pathIdVer) { StringBuilder builder = new StringBuilder(); builder.append("LwM2mObject [id=").append(lwM2mObject.getId()).append(", instances={"); lwM2mObject.getInstances().forEach((instId, inst) -> { - builder.append(instId).append("=").append(this.instanceToString(inst, converter, pathIdVer)).append(", "); + builder.append(instId).append("=").append(this.instanceToString(inst, converter, pathIdVer)).append(", "); }); int startInd = builder.lastIndexOf(", "); if (startInd > 0) { @@ -238,11 +251,12 @@ public class LwM2mClient implements Cloneable { builder.append("}]"); return builder.toString(); } - public String instanceToString (LwM2mObjectInstance objectInstance, LwM2mValueConverterImpl converter, String pathIdVer) { + + public String instanceToString(LwM2mObjectInstance objectInstance, LwM2mValueConverterImpl converter, String pathIdVer) { StringBuilder builder = new StringBuilder(); builder.append("LwM2mObjectInstance [id=").append(objectInstance.getId()).append(", resources={"); objectInstance.getResources().forEach((resId, res) -> { - builder.append(resId).append("=").append(this.resourceToString (res, converter, pathIdVer)).append(", "); + builder.append(resId).append("=").append(this.resourceToString(res, converter, pathIdVer)).append(", "); }); int startInd = builder.lastIndexOf(", "); if (startInd > 0) { @@ -252,12 +266,11 @@ public class LwM2mClient implements Cloneable { return builder.toString(); } - public String resourceToString (LwM2mResource lwM2mResource, LwM2mValueConverterImpl converter, String pathIdVer) { + public String resourceToString(LwM2mResource lwM2mResource, LwM2mValueConverterImpl converter, String pathIdVer) { if (!OPAQUE.equals(lwM2mResource.getType())) { return lwM2mResource.isMultiInstances() ? ((LwM2mMultipleResource) lwM2mResource).toString() : ((LwM2mSingleResource) lwM2mResource).toString(); - } - else { + } else { return String.format("LwM2mSingleResource [id=%s, value=%s, type=%s]", lwM2mResource.getId(), converter.convertValue(lwM2mResource.getValue(), OPAQUE, STRING, new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))), lwM2mResource.getType().name()); @@ -275,7 +288,8 @@ public class LwM2mClient implements Cloneable { resources.add(LwM2mSingleResource.newResource(resId, converter.convertValue(params, equalsResourceTypeGetSimpleName(params), resourceModel.type, pathIds), resourceModel.type)); - }}); + } + }); return resources; } @@ -291,7 +305,8 @@ public class LwM2mClient implements Cloneable { resources.add(LwM2mSingleResource.newResource(resId, converter.convertValue(value, equalsResourceTypeGetSimpleName(value), resourceModel.type, pathIds), resourceModel.type)); - }}); + } + }); return resources; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java index 554a6e650c..bb3e97b7e4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java @@ -35,8 +35,6 @@ public interface LwM2mClientContext { LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo); - LwM2mClient getClient(UUID sessionId); - LwM2mClient getOrRegister(Registration registration); LwM2mClient registerOrUpdate(Registration registration); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index 0a49ea82b2..1c94c0c21b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -83,13 +83,11 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @Override public LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo) { - return getClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); - } + return lwM2mClientsByEndpoint.values().stream().filter(c -> + (new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())) + .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB()))) - @Override - public LwM2mClient getClient(UUID sessionId) { - //TODO: refactor this to search by sessionId efficiently. - return lwM2mClientsByEndpoint.values().stream().filter(c -> c.getSessionId().equals(sessionId)).findAny().get(); + ).findAny().get(); } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 499d48a794..d2e4c66939 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -286,7 +286,7 @@ public class LwM2mFwSwUpdate { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); String value = FIRMWARE.equals(this.type) ? LwM2mTransportUtil.UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type : LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type; - String key = splitCamelCaseString((String) this.lwM2MClient.getResourceName(null, this.pathResultId)); + String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId)); if (success) { this.stateUpdate = FirmwareUpdateStatus.UPDATED.name(); this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java index 248d4ee733..b31f841065 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java @@ -15,98 +15,265 @@ */ package org.thingsboard.server.transport.lwm2m.server.client; +import com.google.gson.Gson; import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; import lombok.Data; import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.request.ContentFormat; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; +import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeoutException; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.ERROR_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FINISH_JSON_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FINISH_VALUE_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.INFO_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.KEY_NAME_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_ALL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.METHOD_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.PARAMS_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESULT_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SEPARATOR_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.START_JSON_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TARGET_ID_VER_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.VALUE_KEY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validPathIdVer; @Slf4j @Data public class Lwm2mClientRpcRequest { - public final String targetIdVerKey = "targetIdVer"; - public final String keyNameKey = "key"; - public final String typeOperKey = "typeOper"; - public final String contentFormatNameKey = "contentFormatName"; - public final String valueKey = "value"; - public final String infoKey = "info"; - public final String paramsKey = "params"; - public final String timeoutInMsKey = "timeOutInMs"; - public final String resultKey = "result"; - public final String errorKey = "error"; - public final String methodKey = "methodName"; + + private Registration registration; + private TransportProtos.SessionInfoProto sessionInfo; + private String bodyParams; + private int requestId; private LwM2mTypeOper typeOper; + private String key; private String targetIdVer; - private String contentFormatName; - private long timeoutInMs; private Object value; - private ConcurrentHashMap params; - private SessionInfoProto sessionInfo; - private int requestId; + private Map params; + private String errorMsg; private String valueMsg; private String infoMsg; private String responseCode; - public void setValidTypeOper(String typeOper) { + public Lwm2mClientRpcRequest() { + } + + public Lwm2mClientRpcRequest(LwM2mTypeOper lwM2mTypeOper, String bodyParams, int requestId, + TransportProtos.SessionInfoProto sessionInfo, Registration registration, DefaultLwM2MTransportMsgHandler handler) { + this.registration = registration; + this.sessionInfo = sessionInfo; + this.requestId = requestId; + if (lwM2mTypeOper != null) { + this.typeOper = lwM2mTypeOper; + } else { + this.errorMsg = METHOD_KEY + " - " + typeOper + " is not valid."; + } + if (this.errorMsg == null && !bodyParams.equals("null")) { + this.bodyParams = bodyParams; + this.init(handler); + } + } + + public TransportProtos.ToDeviceRpcResponseMsg getDeviceRpcResponseResultMsg() { + JsonObject payloadResp = new JsonObject(); + payloadResp.addProperty(RESULT_KEY, this.responseCode); + if (this.errorMsg != null) { + payloadResp.addProperty(ERROR_KEY, this.errorMsg); + } else if (this.valueMsg != null) { + payloadResp.addProperty(VALUE_KEY, this.valueMsg); + } else if (this.infoMsg != null) { + payloadResp.addProperty(INFO_KEY, this.infoMsg); + } + return TransportProtos.ToDeviceRpcResponseMsg.newBuilder() + .setPayload(payloadResp.getAsJsonObject().toString()) + .setRequestId(this.requestId) + .build(); + } + + private void init(DefaultLwM2MTransportMsgHandler handler) { try { - this.typeOper = LwM2mTypeOper.fromLwLwM2mTypeOper(typeOper); + // #1 + if (this.bodyParams.contains(KEY_NAME_KEY)) { + String targetIdVerStr = this.getValueKeyFromBody(KEY_NAME_KEY); + if (targetIdVerStr != null) { + String targetIdVer = handler.getPresentPathIntoProfile(sessionInfo, targetIdVerStr); + if (targetIdVer != null) { + this.targetIdVer = targetIdVer; + this.setInfoMsg(String.format("Changed by: key - %s, pathIdVer - %s", + targetIdVerStr, targetIdVer)); + } + } + } + if (this.getTargetIdVer() == null && this.bodyParams.contains(TARGET_ID_VER_KEY)) { + this.setValidTargetIdVerKey(); + } + if (this.bodyParams.contains(VALUE_KEY)) { + this.value = this.getValueKeyFromBody(VALUE_KEY); + } + try { + if (this.bodyParams.contains(PARAMS_KEY)) { + this.setValidParamsKey(handler); + } + } catch (Exception e) { + this.setErrorMsg(String.format("Params of request is bad Json format. %s", e.getMessage())); + } + + if (this.getTargetIdVer() == null + && !(OBSERVE_READ_ALL == this.getTypeOper() + || DISCOVER_ALL == this.getTypeOper() + || OBSERVE_CANCEL == this.getTypeOper())) { + this.setErrorMsg(TARGET_ID_VER_KEY + " and " + + KEY_NAME_KEY + " is null or bad format"); + } + /** + * EXECUTE && WRITE_REPLACE - only for Resource or ResourceInstance + */ + else if (this.getTargetIdVer() != null + && (EXECUTE == this.getTypeOper() + || WRITE_REPLACE == this.getTypeOper()) + && !(new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.getTargetIdVer()))).isResource() + || new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.getTargetIdVer()))).isResourceInstance())) { + this.setErrorMsg("Invalid parameter " + TARGET_ID_VER_KEY + + ". Only Resource or ResourceInstance can be this operation"); + } } catch (Exception e) { - this.errorMsg = this.methodKey + " - " + typeOper + " is not valid."; + this.setErrorMsg(String.format("Bad format request. %s", e.getMessage())); } + } - public void setValidContentFormatName(JsonObject rpcRequest) { + private void setValidTargetIdVerKey() { + String targetIdVerStr = this.getValueKeyFromBody(TARGET_ID_VER_KEY); + // targetIdVer without ver - ok try { - if (ContentFormat.fromName(rpcRequest.get(this.contentFormatNameKey).getAsString()) != null) { - this.contentFormatName = rpcRequest.get(this.contentFormatNameKey).getAsString(); - } else { - this.errorMsg = this.contentFormatNameKey + " - " + rpcRequest.get(this.contentFormatNameKey).getAsString() + " is not valid."; + // targetIdVer with/without ver - ok + this.targetIdVer = validPathIdVer(targetIdVerStr, this.registration); + if (this.targetIdVer != null) { + this.infoMsg = String.format("Changed by: pathIdVer - %s", this.targetIdVer); } } catch (Exception e) { - this.errorMsg = this.contentFormatNameKey + " - " + rpcRequest.get(this.contentFormatNameKey).getAsString() + " is not valid."; + if (this.targetIdVer == null) { + this.errorMsg = TARGET_ID_VER_KEY + " - " + targetIdVerStr + " is not valid."; + } } } - public void setValidTargetIdVerKey(JsonObject rpcRequest, Registration registration) { - if (rpcRequest.has(this.targetIdVerKey)) { - String targetIdVerStr = rpcRequest.get(targetIdVerKey).getAsString(); - // targetIdVer without ver - ok - try { - // targetIdVer with/without ver - ok - this.targetIdVer = validPathIdVer(targetIdVerStr, registration); + private void setValidParamsKey(DefaultLwM2MTransportMsgHandler handler) { + String paramsStr = this.getValueKeyFromBody(PARAMS_KEY); + if (paramsStr != null) { + String params2Json = + START_JSON_KEY + + "\"" + + paramsStr + .replaceAll(SEPARATOR_KEY, "\"" + SEPARATOR_KEY + "\"") + .replaceAll(FINISH_VALUE_KEY, "\"" + FINISH_VALUE_KEY + "\"") + + "\"" + + FINISH_JSON_KEY; + // jsonObject + Map params = new Gson().fromJson(params2Json, new TypeToken>() { + }.getType()); + if (WRITE_UPDATE == this.getTypeOper()) { if (this.targetIdVer != null) { - this.infoMsg = String.format("Changed by: pathIdVer - %s", this.targetIdVer); - } - } catch (Exception e) { - if (this.targetIdVer == null) { - this.errorMsg = this.targetIdVerKey + " - " + targetIdVerStr + " is not valid."; + Map paramsResourceId = this.convertParamsToResourceId((ConcurrentHashMap) params, handler); + if (paramsResourceId.size() > 0) { + this.setParams(paramsResourceId); + } } + } else if (WRITE_ATTRIBUTES == this.getTypeOper()) { + this.setParams(params); } } } - public TransportProtos.ToDeviceRpcResponseMsg getDeviceRpcResponseResultMsg() { - JsonObject payloadResp = new JsonObject(); - payloadResp.addProperty(this.resultKey, this.responseCode); - if (this.errorMsg != null) { - payloadResp.addProperty(this.errorKey, this.errorMsg); - } else if (this.valueMsg != null) { - payloadResp.addProperty(this.valueKey, this.valueMsg); - } else if (this.infoMsg != null) { - payloadResp.addProperty(this.infoKey, this.infoMsg); + private String getValueKeyFromBody(String key) { + String valueKey = null; + int startInd = -1; + int finishInd = -1; + try { + switch (key) { + case KEY_NAME_KEY: + case TARGET_ID_VER_KEY: + case VALUE_KEY: + startInd = this.bodyParams.indexOf(SEPARATOR_KEY, this.bodyParams.indexOf(key)); + finishInd = this.bodyParams.indexOf(FINISH_VALUE_KEY, this.bodyParams.indexOf(key)); + if (startInd >= 0 && finishInd < 0) { + finishInd = this.bodyParams.indexOf(FINISH_JSON_KEY, this.bodyParams.indexOf(key)); + } + break; + case PARAMS_KEY: + startInd = this.bodyParams.indexOf(START_JSON_KEY, this.bodyParams.indexOf(key)); + finishInd = this.bodyParams.indexOf(FINISH_JSON_KEY, this.bodyParams.indexOf(key)); + } + if (startInd >= 0 && finishInd > 0) { + valueKey = this.bodyParams.substring(startInd + 1, finishInd); + } + } catch (Exception e) { + log.error("", new TimeoutException()); } - return TransportProtos.ToDeviceRpcResponseMsg.newBuilder() - .setPayload(payloadResp.getAsJsonObject().toString()) - .setRequestId(this.requestId) - .build(); + /** + * ReplaceAll "\"" + */ + if (StringUtils.trimToNull(valueKey) != null) { + char[] chars = valueKey.toCharArray(); + for (int i = 0; i < chars.length; i++) { + if (chars[i] == 92 || chars[i] == 34) chars[i] = 32; + } + return key.equals(PARAMS_KEY) ? String.valueOf(chars) : String.valueOf(chars).replaceAll(" ", ""); + } + return null; + } + + private ConcurrentHashMap convertParamsToResourceId(ConcurrentHashMap params, + DefaultLwM2MTransportMsgHandler serviceImpl) { + Map paramsIdVer = new ConcurrentHashMap<>(); + LwM2mPath targetId = new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.targetIdVer))); + if (targetId.isObjectInstance()) { + params.forEach((k, v) -> { + try { + int id = Integer.parseInt(k); + paramsIdVer.put(String.valueOf(id), v); + } catch (NumberFormatException e) { + String targetIdVer = serviceImpl.getPresentPathIntoProfile(sessionInfo, k); + if (targetIdVer != null) { + LwM2mPath lwM2mPath = new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(targetIdVer))); + paramsIdVer.put(String.valueOf(lwM2mPath.getResourceId()), v); + } + /** WRITE_UPDATE*/ + else { + String rezId = this.getRezIdByResourceNameAndObjectInstanceId(k, serviceImpl); + if (rezId != null) { + paramsIdVer.put(rezId, v); + } + } + } + }); + } + return (ConcurrentHashMap) paramsIdVer; + } + + private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MTransportMsgHandler handler) { + LwM2mClient lwM2mClient = handler.clientContext.getClient(this.sessionInfo); + return lwM2mClient != null ? + lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) : + null; } }