Lwm2m Fix bug review 1-10

This commit is contained in:
nickAS21 2021-09-17 14:56:07 +03:00
parent 9651b93574
commit f86a30a8ed
20 changed files with 107 additions and 90 deletions

View File

@ -126,7 +126,6 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.edge.EdgeLicenseService;
import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.lwm2m.LwM2MServiceImpl;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.resource.TbResourceService;
@ -185,9 +184,6 @@ public abstract class BaseController {
@Autowired
protected DeviceProfileService deviceProfileService;
@Autowired
protected LwM2MServiceImpl lwM2MService;
@Autowired
protected AssetService assetService;

View File

@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@ -31,6 +32,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.lwm2m.LwM2MServiceImpl;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.Map;
@ -41,6 +43,9 @@ import java.util.Map;
@RequestMapping("/api")
public class Lwm2mController extends BaseController {
@Autowired
protected LwM2MServiceImpl lwM2MService;
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{isBootstrapServer}", method = RequestMethod.GET)
@ResponseBody

View File

@ -693,6 +693,8 @@ transport:
ota_pool_size: "${LWM2M_OTA_POOL_SIZE:10}"
clean_period_in_sec: "${LWM2M_CLEAN_PERIOD_IN_SEC:2}"
log_max_length: "${LWM2M_LOG_MAX_LENGTH:1024}"
psm_activity_timer: "${LWM2M_PSM_ACTIVITY_TIMER:10000}"
paging_transmission_window: "${LWM2M_PAGING_TRANSMISSION_WINDOW:10000}"
# Use redis for Security and Registration stores
redis.enabled: "${LWM2M_REDIS_ENABLED:false}"
snmp:

View File

@ -83,7 +83,7 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
@Override
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceId, LwM2mResource value) {
log.warn("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) {
case 0:

View File

@ -83,7 +83,7 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
@Override
public WriteResponse write(ServerIdentity identity, boolean replace, int resourceId, LwM2mResource value) {
log.warn("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) {
case 2:

View File

@ -15,9 +15,6 @@
*/
package org.thingsboard.server.common.data.security;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.id.DeviceCredentialsId;

View File

@ -17,6 +17,8 @@ package org.thingsboard.server.transport.lwm2m.bootstrap.secure;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.eclipse.leshan.core.SecurityMode;
import org.eclipse.leshan.core.request.BindingMode;
import org.eclipse.leshan.core.util.Hex;
@ -41,7 +43,9 @@ public class LwM2MBootstrapConfig implements Serializable {
* notifIfDisabled: boolean,
* binding: string
* */
LwM2MBootstrapServers servers;
@Getter
@Setter
private LwM2MBootstrapServers servers;
/** -bootstrapServer, lwm2mServer
* interface ServerSecurityConfig
@ -56,9 +60,13 @@ public class LwM2MBootstrapConfig implements Serializable {
* serverId?: number,
* bootstrapServerAccountTimeout: number
* */
LwM2MServerBootstrap bootstrapServer;
@Getter
@Setter
private LwM2MServerBootstrap bootstrapServer;
LwM2MServerBootstrap lwm2mServer;
@Getter
@Setter
private LwM2MServerBootstrap lwm2mServer;
@JsonIgnore
public BootstrapConfig getLwM2MBootstrapConfig() {

View File

@ -43,11 +43,11 @@ import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_ERROR;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_INFO;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.getBootstrapParametersFromThingsboard;
import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP;
@Slf4j
@Service("LwM2MBootstrapSecurityStore")
@ -86,10 +86,10 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
bootstrapConfigStore.add(endPoint, bsConfigNew);
} catch (InvalidConfigurationException e) {
if (e.getMessage().contains("Psk identity") && e.getMessage().contains("already used for this bootstrap server")) {
log.trace("", e);
log.trace("Invalid Bootstrap Configuration", e);
}
else {
log.error("", e);
log.error("Invalid Bootstrap Configuration", e);
}
}
return store.getSecurityInfo() == null ? null : Collections.singletonList(store.getSecurityInfo()).iterator();
@ -109,7 +109,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
try {
bootstrapConfigStore.add(store.getEndpoint(), bsConfig);
} catch (InvalidConfigurationException e) {
log.error("", e);
log.error("Invalid Bootstrap Configuration", e);
}
return store.getSecurityInfo();
}
@ -158,11 +158,11 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig();
if (lwM2MBootstrapConfig != null) {
BootstrapConfiguration bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile());
lwM2MBootstrapConfig.servers = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getServers()), LwM2MBootstrapServers.class);
LwM2MServerBootstrap profileServerBootstrap = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getBootstrapServer()), LwM2MServerBootstrap.class);
if (SecurityMode.NO_SEC != profileServerBootstrap.getSecurityMode() && profileServerBootstrap != null) {
profileServerBootstrap.setSecurityHost( profileServerBootstrap.getHost());
profileServerBootstrap.setSecurityPort( profileServerBootstrap.getPort());
lwM2MBootstrapConfig.setServers(JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getServers()), LwM2MBootstrapServers.class));
LwM2MServerBootstrap bootstrapServerProfile = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getBootstrapServer()), LwM2MServerBootstrap.class);
if (SecurityMode.NO_SEC != bootstrapServerProfile.getSecurityMode() && bootstrapServerProfile != null) {
bootstrapServerProfile.setSecurityHost( bootstrapServerProfile.getHost());
bootstrapServerProfile.setSecurityPort( bootstrapServerProfile.getPort());
}
LwM2MServerBootstrap profileLwm2mServer = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getLwm2mServer()), LwM2MServerBootstrap.class);
if (SecurityMode.NO_SEC != profileLwm2mServer.getSecurityMode() && profileLwm2mServer != null) {
@ -173,9 +173,9 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
bsSessions.put(store.getEndpoint(), sessionInfo);
context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, null, sessionInfo, context.getTransportService()));
if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.getBootstrapServer(), bootstrapServerProfile, lwM2MBootstrapConfig.getLwm2mServer(), profileLwm2mServer)) {
lwM2MBootstrapConfig.setBootstrapServer(new LwM2MServerBootstrap(lwM2MBootstrapConfig.getBootstrapServer(), bootstrapServerProfile));
lwM2MBootstrapConfig.setLwm2mServer(new LwM2MServerBootstrap(lwM2MBootstrapConfig.getLwm2mServer(), profileLwm2mServer));
String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LWM2M_INFO, store.getEndpoint());
helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo);
return lwM2MBootstrapConfig;
@ -187,7 +187,6 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
return null;
}
}
log.error("Unable to decode Json or Certificate for [{}]", store.getEndpoint());
return null;
}
@ -197,13 +196,13 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
* and (lwm2mServer in credential and lwm2mServer in profile
*
* @param bootstrapFromCredential - Bootstrap -> Security of bootstrapServer in credential
* @param profileServerBootstrap - Bootstrap -> Security of bootstrapServer in profile
* @param bootstrapServerProfile - Bootstrap -> Security of bootstrapServer in profile
* @param lwm2mFromCredential - Bootstrap -> Security of lwm2mServer in credential
* @param profileLwm2mServer - Bootstrap -> Security of lwm2mServer in profile
* @return false if not sync between SecurityMode of Bootstrap credential and profile
*/
private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap profileServerBootstrap, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) {
return (bootstrapFromCredential.getSecurityMode().equals(profileServerBootstrap.getSecurityMode()) &&
private boolean getValidatedSecurityMode(LwM2MServerBootstrap bootstrapFromCredential, LwM2MServerBootstrap bootstrapServerProfile, LwM2MServerBootstrap lwm2mFromCredential, LwM2MServerBootstrap profileLwm2mServer) {
return (bootstrapFromCredential.getSecurityMode().equals(bootstrapServerProfile.getSecurityMode()) &&
lwm2mFromCredential.getSecurityMode().equals(profileLwm2mServer.getSecurityMode()));
}

View File

@ -200,21 +200,20 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession
public BootstrapPolicy onRequestFailure(BootstrapSession bsSession,
BootstrapDownlinkRequest<? extends LwM2mResponse> request, Throwable cause) {
this.sendLogs (bsSession.getEndpoint(),
String.format("%s: %s %s failed because of %s for %s : %s", LOG_LWM2M_INFO,
request.getClass().getSimpleName(), request.getPath().toString(), cause.toString(), bsSession.toString(), request.toString()));
String.format("%s: %s %s failed because of %s for %s : %s", LOG_LWM2M_INFO, request.getClass().getSimpleName(),
request.getPath().toString(), cause.toString(), bsSession.toString(), request.toString()));
return BootstrapPolicy.failed();
}
@Override
public void end(BootstrapSession bsSession) {
this.sendLogs (bsSession.getEndpoint(),
String.format("%s: Bootstrap session finished : %s", LOG_LWM2M_INFO, bsSession.toString()));
this.sendLogs (bsSession.getEndpoint(), String.format("%s: Bootstrap session finished : %s", LOG_LWM2M_INFO, bsSession.toString()));
}
@Override
public void failed(BootstrapSession bsSession, BootstrapFailureCause cause) {
this.sendLogs (bsSession.getEndpoint(),
String.format("%s: Bootstrap session failed by %s: %s", LOG_LWM2M_INFO, cause.toString(), bsSession.toString()));
this.sendLogs (bsSession.getEndpoint(), String.format("%s: Bootstrap session failed by %s: %s", LOG_LWM2M_INFO,
cause.toString(), bsSession.toString()));
}
private void sendLogs (String endpointName, String logMsg) {

View File

@ -50,16 +50,13 @@ public enum LwM2mOperationType {
WRITE_COMPOSITE(14, "WriteComposite", false, true),
WRITE_ATTRIBUTES(15, "WriteAttributes", true),
DELETE(16, "Delete", true),
// only for RPC
FW_UPDATE(17, "FirmwareUpdate", false),
// FW_READ_INFO(18, "FirmwareReadInfo", false),
SW_UPDATE(19, "SoftwareUpdate", false),
// SW_READ_INFO(20, "SoftwareReadInfo", false),
SW_UNINSTALL(21, "SoftwareUninstall", false),
CREATE(11, "Create", true);
CREATE(17, "Create", true);
// only for RPC - future
// FW_UPDATE(18, "FirmwareUpdate", false),
// FW_READ_INFO(19, "FirmwareReadInfo", false),
// SW_UPDATE(20, "SoftwareUpdate", false),
// SW_READ_INFO(21, "SoftwareReadInfo", false),
// SW_UNINSTALL(22, "SoftwareUninstall", false);
@Getter

View File

@ -98,7 +98,9 @@ public class LwM2mServerListener {
@Override
public void onError(Observation observation, Registration registration, Exception error) {
log.error("Unable to handle notification of [{}:{}] [{}]", observation.getRegistrationId(), observation.getPath(), error.getMessage());
if (error != null) {
log.debug("Unable to handle notification of [{}:{}] [{}]", observation.getRegistrationId(), observation.getPath(), error.getMessage());
}
}
@Override

View File

@ -51,7 +51,7 @@ import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.
public class LwM2mTransportServerHelper {
private final LwM2mTransportContext context;
private final static JsonParser JSON_PARSER = new JsonParser();;
private final static JsonParser JSON_PARSER = new JsonParser();
public void sendParametersOnThingsboardAttribute(List<TransportProtos.KeyValueProto> result, SessionInfoProto sessionInfo) {
PostAttributeMsg.Builder request = PostAttributeMsg.newBuilder();
@ -231,7 +231,11 @@ public class LwM2mTransportServerHelper {
case STRING_V:
return kv.getStringV();
case JSON_V:
return JSON_PARSER.parse(kv.getJsonV());
try {
return JSON_PARSER.parse(kv.getJsonV());
} catch (Exception e) {
return null;
}
}
return null;
}

View File

@ -40,7 +40,6 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHa
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest;
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback;
import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
import org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService;
import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService;
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
@ -225,7 +224,7 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
this.pushUpdateToClientIfNeeded(lwM2MClient, oldResourceValue, newValProto, pathIdVer, logFailedUpdateOfNonChangedValue, resourceModel.type);
} else {
pushUpdateMultiToClientIfNeeded(lwM2MClient, resourceModel, (JsonElement) newValProto,
(Map<Integer, LwM2mResourceInstance>) oldResourceValue, pathIdVer);
(Map<Integer, LwM2mResourceInstance>) oldResourceValue, pathIdVer, logFailedUpdateOfNonChangedValue);
}
});
}
@ -251,7 +250,7 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
}
private void pushUpdateMultiToClientIfNeeded(LwM2mClient client, ResourceModel resourceModel, JsonElement newValProto,
Map<Integer, LwM2mResourceInstance> valueOld, String versionedId) {
Map<Integer, LwM2mResourceInstance> valueOld, String versionedId, boolean logFailedUpdateOfNonChangedValue) {
Map newValues = convertMultiResourceValuesFromJson((JsonObject) newValProto, resourceModel.type, versionedId);
if (newValues.size() > 0 && valueOld != null && valueOld.size() > 0) {
valueOld.values().stream().forEach((v) -> {
@ -272,7 +271,7 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
if (newValues.size() > 0) {
TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValues).timeout(this.config.getTimeout()).build();
downlinkHandler.sendWriteReplaceRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId));
} else {
} else if (logFailedUpdateOfNonChangedValue) {
log.warn("Didn't update resource [{}] [{}]", versionedId, newValProto);
String logMsg = String.format("%s: Didn't update resource versionedId - %s value - %s. Value is not changed",
LOG_LWM2M_WARN, versionedId, newValProto);

View File

@ -46,7 +46,6 @@ import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@ -106,7 +105,6 @@ public class LwM2mClient implements Serializable {
@Getter
private Long pagingTransmissionWindow;
@Getter
@Setter
private Long edrxCycle;
@Getter
private Registration registration;
@ -122,9 +120,9 @@ public class LwM2mClient implements Serializable {
private boolean firstEdrxDownlink = true;
@Getter
Set<ContentFormat> clientSupportContentFormats;
private Set<ContentFormat> clientSupportContentFormats;
@Getter
ContentFormat defaultContentFormat;
private ContentFormat defaultContentFormat;
@Getter
private final AtomicInteger retryAttempts;
@ -327,15 +325,18 @@ public class LwM2mClient implements Serializable {
Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet();
Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration)
.getObjectModel(pathIds.getObjectId()).resources;
if (params != null) {
if (params != null && params instanceof Map && ((Map<?, ?>) params).size() > 0) {
resourceModels.forEach((resourceId, resourceModel) -> {
if (((Map) params).containsKey(String.valueOf(resourceId))) {
Object value = ((Map) params).get((String.valueOf(resourceId)));
LwM2mResource resource = null;
LwM2mResource resource;
if (resourceModel.multiple) {
if (value instanceof LinkedHashMap) {
Map values = convertMultiResourceValuesFromRpcBody((LinkedHashMap) value, resourceModel.type, pathRezIdVer);
try {
Map values = convertMultiResourceValuesFromRpcBody(value, resourceModel.type, pathRezIdVer);
resource = LwM2mMultipleResource.newResource(resourceId, (Map<Integer, ?>) values, resourceModel.type);
} catch (Exception e) {
throw new IllegalArgumentException("Resource id=" + resourceId + ", value = " + value + ", class = " +
value.getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!");
}
} else {
Object valueRez = value.getClass().getSimpleName().equals("Integer") ? ((Integer) value).longValue() : value;
@ -353,9 +354,12 @@ public class LwM2mClient implements Serializable {
}
});
}
else {
else if (params == null) {
throw new IllegalArgumentException("The value of this resource must not be null.");
}
else {
throw new IllegalArgumentException("The value of this resource must be in Map format and size > 0");
}
return resources;
}

View File

@ -19,7 +19,7 @@ import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_WARN;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2mTransportUtil.LOG_LWM2M_ERROR;
@Slf4j
public abstract class AbstractTbLwM2MRequestCallback<R, T> implements DownlinkRequestCallback<R, T> {
@ -35,12 +35,12 @@ public abstract class AbstractTbLwM2MRequestCallback<R, T> implements DownlinkRe
@Override
public void onValidationError(String params, String msg) {
log.trace("[{}] Request [{}] validation failed. Reason: {}", client.getEndpoint(), params, msg);
logService.log(client, String.format("[%s]: Request [%s] validation failed. Reason: %s", LOG_LWM2M_WARN, params, msg));
logService.log(client, String.format("[%s]: Request [%s] validation failed. Reason: %s", LOG_LWM2M_ERROR, params, msg));
}
@Override
public void onError(String params, Exception e) {
log.trace("[{}] Request [{}] processing failed", client.getEndpoint(), params, e);
logService.log(client, String.format("[%s]: Request [%s] processing failed. Reason: %s", LOG_LWM2M_WARN, params, e));
logService.log(client, String.format("[%s]: Request [%s] processing failed. Reason: %s", LOG_LWM2M_ERROR, params, e));
}
}

View File

@ -19,7 +19,6 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.Link;
import org.eclipse.leshan.core.LwM2m;
import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.attributes.Attribute;
import org.eclipse.leshan.core.attributes.AttributeSet;
import org.eclipse.leshan.core.model.LwM2mModel;
@ -79,7 +78,6 @@ import javax.annotation.PreDestroy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -302,11 +300,13 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
try {
WriteRequest downlink = null;
if (resourceModelWrite.multiple) {
if (request.getValue() instanceof Map && ((Map) request.getValue()).size() > 0) {
try {
Map value = convertMultiResourceValuesFromRpcBody(request.getValue(), resourceModelWrite.type, request.getObjectId());
downlink = new WriteRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(),
(Map<Integer, ?>) request.getValue(), resourceModelWrite.type);
} else {
callback.onValidationError(toString(request), "Resource value is: " + request.getValue().getClass().getSimpleName() + ". Value of Multi-Instance Resource must be in Json format!");
value, resourceModelWrite.type);
} catch (Exception e) {
callback.onValidationError(toString(request), "Resource id=" + resultIds.toString() + ", value = " + request.getValue() +
", class = " + request.getValue().getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!");
}
} else {
downlink = this.getWriteRequestSingleResource(resourceModelWrite.type, contentFormat,
@ -368,12 +368,12 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider());
if (resourceModelWrite != null) {
if (resourceModelWrite.multiple) {
if (request.getValue() instanceof Map && ((Map) request.getValue()).size() > 0) {
Map value = convertMultiResourceValuesFromRpcBody((LinkedHashMap) request.getValue(), resourceModelWrite.type, request.getObjectId());
try {
Map value = convertMultiResourceValuesFromRpcBody(request.getValue(), resourceModelWrite.type, request.getObjectId());
downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(),
value, resourceModelWrite.type);
} else {
callback.onValidationError(toString(request), "Resource value is bad. Format: " + request.getValue().getClass().getSimpleName() + ". Value of Multi-Instance Resource must be in Json format!");
} catch (Exception e1) {
callback.onValidationError(toString(request), "Resource id=" + resultIds.toString() + ", value = " + request.getValue() + ", class = " + request.getValue().getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!");
}
}
} else {

View File

@ -169,8 +169,6 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
case DISCOVER_ALL:
sendDiscoverAllRequest(client, rpcRequest);
break;
case FW_UPDATE:
//TODO: implement and add break statement
default:
throw new IllegalArgumentException("Unsupported operation: " + operationType.name());
}
@ -257,11 +255,16 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
private void sendWriteReplaceRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) {
RpcWriteReplaceRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteReplaceRequest.class);
LwM2mPath path = new LwM2mPath(fromVersionedIdToObjectId(versionedId));
if (path.isResource() && requestBody.getValue() instanceof LinkedHashMap) {
if (path.isResource()) {
ResourceModel resourceModel = client.getResourceModel(versionedId, this.config.getModelProvider());
if (resourceModel != null && resourceModel.multiple) {
Map value = convertMultiResourceValuesFromRpcBody((LinkedHashMap) requestBody.getValue(), resourceModel.type, versionedId);
requestBody.setValue(value);
try {
Map value = convertMultiResourceValuesFromRpcBody(requestBody.getValue(), resourceModel.type, versionedId);
requestBody.setValue(value);
} catch (Exception e) {
throw new IllegalArgumentException("Resource id=" + versionedId + ", value = " + requestBody.getValue() + ", class = " +
requestBody.getValue().getClass().getSimpleName() + "is bad. Value of Multi-Instance Resource must be in Json format!");
}
}
}
TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId)

View File

@ -16,10 +16,7 @@
package org.thingsboard.server.transport.lwm2m.utils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.leshan.core.attributes.Attribute;
@ -47,6 +44,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.ota.OtaPackageKey;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.transport.util.JsonUtils;
import org.thingsboard.server.transport.lwm2m.config.LwM2mVersion;
import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
@ -57,18 +55,15 @@ import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdate
import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult;
import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState;
import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
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;
@ -367,11 +362,9 @@ public class LwM2mTransportUtil {
return String.format("%s %s", keyName, String.join(",", msgException)).trim();
}
public static Map convertMultiResourceValuesFromRpcBody(LinkedHashMap value, ResourceModel.Type type, String versionedId) {
Gson gson = new Gson();
JsonParser JSON_PARSER = new JsonParser();
String json = gson.toJson(value, LinkedHashMap.class);
return convertMultiResourceValuesFromJson((JsonObject) JSON_PARSER.parse(json), type, versionedId);
public static Map convertMultiResourceValuesFromRpcBody(Object value, ResourceModel.Type type, String versionedId) throws Exception {
String valueJsonStr = JsonUtils.riteValueAsString(value);
return convertMultiResourceValuesFromJson((JsonObject) JsonUtils.parse(valueJsonStr), type, versionedId);
}
public static Map convertMultiResourceValuesFromJson(JsonObject newValProto, ResourceModel.Type type, String versionedId) {

View File

@ -15,15 +15,19 @@
*/
package org.thingsboard.server.common.transport.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
import java.util.List;
public class JsonUtils {
private static final JsonParser jsonParser = new JsonParser();
private static final ObjectMapper json = new ObjectMapper();
public static JsonObject getJsonObject(List<KeyValueProto> tsKv) {
JsonObject json = new JsonObject();
@ -52,4 +56,9 @@ public class JsonUtils {
public static JsonElement parse(String params) {
return jsonParser.parse(params);
}
public static String riteValueAsString (Object value) throws JsonProcessingException {
return json.writeValueAsString(value);
}
}

View File

@ -97,10 +97,10 @@ transport:
timeout: "${CLIENT_SIDE_RPC_TIMEOUT:60000}"
# Enable/disable http/mqtt/coap transport protocols (has higher priority than certain protocol's 'enabled' property)
api_enabled: "${TB_TRANSPORT_API_ENABLED:true}"
# Local LwM2M transport parameters
log:
enabled: "${TB_TRANSPORT_LOG_ENABLED:true}"
max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}"
# Local LwM2M transport parameters
lwm2m:
# Enable/disable lvm2m transport protocol.
enabled: "${LWM2M_ENABLED:true}"