Commit ea3d273625158946094b1267328d16d5ac1388d5

Authored by YevhenBondarenko
2 parents b1204ebe e83064ec

Merge branch 'master' of https://github.com/thingsboard/thingsboard into feature/power-mode

Showing 73 changed files with 1476 additions and 1549 deletions

Too many changes to show.

To preserve performance only 73 of 185 files are displayed.

  1 +{
  2 + "providerId": "Apple",
  3 + "additionalInfo": null,
  4 + "accessTokenUri": "https://appleid.apple.com/auth/token",
  5 + "authorizationUri": "https://appleid.apple.com/auth/authorize?response_mode=form_post",
  6 + "scope": ["email","openid","name"],
  7 + "jwkSetUri": "https://appleid.apple.com/auth/keys",
  8 + "userInfoUri": null,
  9 + "clientAuthenticationMethod": "POST",
  10 + "userNameAttributeName": "email",
  11 + "mapperConfig": {
  12 + "type": "APPLE",
  13 + "basic": {
  14 + "emailAttributeKey": "email",
  15 + "firstNameAttributeKey": "firstName",
  16 + "lastNameAttributeKey": "lastName",
  17 + "tenantNameStrategy": "DOMAIN"
  18 + }
  19 + },
  20 + "comment": null,
  21 + "loginButtonIcon": "apple-logo",
  22 + "loginButtonLabel": "Apple",
  23 + "helpLink": "https://developer.apple.com/sign-in-with-apple/get-started/"
  24 +}
... ...
... ... @@ -92,7 +92,7 @@ CREATE TABLE IF NOT EXISTS oauth2_registration (
92 92 created_time bigint NOT NULL,
93 93 additional_info varchar,
94 94 client_id varchar(255),
95   - client_secret varchar(255),
  95 + client_secret varchar(2048),
96 96 authorization_uri varchar(255),
97 97 token_uri varchar(255),
98 98 scope varchar(255),
... ...
... ... @@ -782,15 +782,17 @@ public class DeviceController extends BaseController {
782 782 }
783 783
784 784 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
785   - @RequestMapping(value = "/devices/count/{otaPackageType}", method = RequestMethod.GET)
  785 + @RequestMapping(value = "/devices/count/{otaPackageType}/{deviceProfileId}", method = RequestMethod.GET)
786 786 @ResponseBody
787   - public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType,
788   - @RequestParam String deviceProfileId) throws ThingsboardException {
  787 + public Long countByDeviceProfileAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType,
  788 + @PathVariable("deviceProfileId") String deviceProfileId) throws ThingsboardException {
789 789 checkParameter("OtaPackageType", otaPackageType);
790 790 checkParameter("DeviceProfileId", deviceProfileId);
791 791 try {
792 792 return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(
793   - getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(otaPackageType));
  793 + getTenantId(),
  794 + new DeviceProfileId(UUID.fromString(deviceProfileId)),
  795 + OtaPackageType.valueOf(otaPackageType));
794 796 } catch (Exception e) {
795 797 throw handleException(e);
796 798 }
... ...
... ... @@ -128,7 +128,7 @@ public class OtaPackageController extends BaseController {
128 128 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
129 129 @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.POST)
130 130 @ResponseBody
131   - public OtaPackage saveOtaPackageData(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId,
  131 + public OtaPackageInfo saveOtaPackageData(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId,
132 132 @RequestParam(required = false) String checksum,
133 133 @RequestParam(CHECKSUM_ALGORITHM) String checksumAlgorithmStr,
134 134 @RequestBody MultipartFile file) throws ThingsboardException {
... ... @@ -160,7 +160,7 @@ public class OtaPackageController extends BaseController {
160 160 otaPackage.setContentType(file.getContentType());
161 161 otaPackage.setData(ByteBuffer.wrap(bytes));
162 162 otaPackage.setDataSize((long) bytes.length);
163   - OtaPackage savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage);
  163 + OtaPackageInfo savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage);
164 164 logEntityAction(savedOtaPackage.getId(), savedOtaPackage, null, ActionType.UPDATED, null);
165 165 return savedOtaPackage;
166 166 } catch (Exception e) {
... ...
... ... @@ -74,6 +74,7 @@ import java.util.List;
74 74 import java.util.Map;
75 75 import java.util.Set;
76 76 import java.util.concurrent.ConcurrentMap;
  77 +import java.util.concurrent.TimeUnit;
77 78 import java.util.stream.Collectors;
78 79
79 80 @Slf4j
... ... @@ -86,6 +87,7 @@ public class RuleChainController extends BaseController {
86 87 public static final String RULE_NODE_ID = "ruleNodeId";
87 88
88 89 private static final ObjectMapper objectMapper = new ObjectMapper();
  90 + public static final int TIMEOUT = 20;
89 91
90 92 @Autowired
91 93 private InstallScripts installScripts;
... ... @@ -388,25 +390,25 @@ public class RuleChainController extends BaseController {
388 390 TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data);
389 391 switch (scriptType) {
390 392 case "update":
391   - output = msgToOutput(engine.executeUpdate(inMsg));
  393 + output = msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
392 394 break;
393 395 case "generate":
394   - output = msgToOutput(engine.executeGenerate(inMsg));
  396 + output = msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
395 397 break;
396 398 case "filter":
397   - boolean result = engine.executeFilter(inMsg);
  399 + boolean result = engine.executeFilterAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
398 400 output = Boolean.toString(result);
399 401 break;
400 402 case "switch":
401   - Set<String> states = engine.executeSwitch(inMsg);
  403 + Set<String> states = engine.executeSwitchAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
402 404 output = objectMapper.writeValueAsString(states);
403 405 break;
404 406 case "json":
405   - JsonNode json = engine.executeJson(inMsg);
  407 + JsonNode json = engine.executeJsonAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
406 408 output = objectMapper.writeValueAsString(json);
407 409 break;
408 410 case "string":
409   - output = engine.executeToString(inMsg);
  411 + output = engine.executeToStringAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
410 412 break;
411 413 default:
412 414 throw new IllegalArgumentException("Unsupported script type: " + scriptType);
... ...
... ... @@ -30,6 +30,7 @@ import java.util.UUID;
30 30 import java.util.concurrent.ConcurrentHashMap;
31 31 import java.util.concurrent.Executors;
32 32 import java.util.concurrent.ScheduledExecutorService;
  33 +import java.util.concurrent.TimeoutException;
33 34 import java.util.concurrent.atomic.AtomicInteger;
34 35
35 36 /**
... ... @@ -84,8 +85,10 @@ public abstract class AbstractJsInvokeService implements JsInvokeService {
84 85 apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.JS_EXEC_COUNT, 1);
85 86 return doInvokeFunction(scriptId, functionName, args);
86 87 } else {
87   - return Futures.immediateFailedFuture(
88   - new RuntimeException("Script invocation is blocked due to maximum error count " + getMaxErrors() + "!"));
  88 + String message = "Script invocation is blocked due to maximum error count "
  89 + + getMaxErrors() + ", scriptId " + scriptId + "!";
  90 + log.warn(message);
  91 + return Futures.immediateFailedFuture(new RuntimeException(message));
89 92 }
90 93 } else {
91 94 return Futures.immediateFailedFuture(new RuntimeException("JS Execution is disabled due to API limits!"));
... ... @@ -117,8 +120,11 @@ public abstract class AbstractJsInvokeService implements JsInvokeService {
117 120
118 121 protected abstract long getMaxBlacklistDuration();
119 122
120   - protected void onScriptExecutionError(UUID scriptId) {
121   - disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo()).incrementAndGet();
  123 + protected void onScriptExecutionError(UUID scriptId, Throwable t, String scriptBody) {
  124 + DisableListInfo disableListInfo = disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo());
  125 + log.warn("Script has exception and will increment counter {} on disabledFunctions for id {}, exception {}, cause {}, scriptBody {}",
  126 + disableListInfo.get(), scriptId, t, t.getCause(), scriptBody);
  127 + disableListInfo.incrementAndGet();
122 128 }
123 129
124 130 private String generateJsScript(JsScriptType scriptType, String functionName, String scriptBody, String... argNames) {
... ...
... ... @@ -160,7 +160,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
160 160 return ((Invocable) engine).invokeFunction(functionName, args);
161 161 }
162 162 } catch (Exception e) {
163   - onScriptExecutionError(scriptId);
  163 + onScriptExecutionError(scriptId, e, functionName);
164 164 throw new ExecutionException(e);
165 165 }
166 166 });
... ...
... ... @@ -18,7 +18,6 @@ package org.thingsboard.server.service.script;
18 18 import com.google.common.util.concurrent.FutureCallback;
19 19 import com.google.common.util.concurrent.Futures;
20 20 import com.google.common.util.concurrent.ListenableFuture;
21   -import com.google.common.util.concurrent.MoreExecutors;
22 21 import lombok.Getter;
23 22 import lombok.extern.slf4j.Slf4j;
24 23 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -26,6 +25,7 @@ import org.springframework.beans.factory.annotation.Value;
26 25 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
27 26 import org.springframework.scheduling.annotation.Scheduled;
28 27 import org.springframework.stereotype.Service;
  28 +import org.springframework.util.StopWatch;
29 29 import org.thingsboard.common.util.ThingsBoardThreadFactory;
30 30 import org.thingsboard.server.gen.js.JsInvokeProtos;
31 31 import org.thingsboard.server.queue.TbQueueRequestTemplate;
... ... @@ -161,7 +161,8 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
161 161
162 162 @Override
163 163 protected ListenableFuture<Object> doInvokeFunction(UUID scriptId, String functionName, Object[] args) {
164   - String scriptBody = scriptIdToBodysMap.get(scriptId);
  164 + log.trace("doInvokeFunction js-request for uuid {} with timeout {}ms", scriptId, maxRequestsTimeout);
  165 + final String scriptBody = scriptIdToBodysMap.get(scriptId);
165 166 if (scriptBody == null) {
166 167 return Futures.immediateFailedFuture(new RuntimeException("No script body found for scriptId: [" + scriptId + "]!"));
167 168 }
... ... @@ -170,7 +171,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
170 171 .setScriptIdLSB(scriptId.getLeastSignificantBits())
171 172 .setFunctionName(functionName)
172 173 .setTimeout((int) maxRequestsTimeout)
173   - .setScriptBody(scriptIdToBodysMap.get(scriptId));
  174 + .setScriptBody(scriptBody);
174 175
175 176 for (Object arg : args) {
176 177 jsRequestBuilder.addArgs(arg.toString());
... ... @@ -180,6 +181,9 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
180 181 .setInvokeRequest(jsRequestBuilder.build())
181 182 .build();
182 183
  184 + StopWatch stopWatch = new StopWatch();
  185 + stopWatch.start();
  186 +
183 187 ListenableFuture<TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>> future = requestTemplate.send(new TbProtoJsQueueMsg<>(UUID.randomUUID(), jsRequestWrapper));
184 188 if (maxRequestsTimeout > 0) {
185 189 future = Futures.withTimeout(future, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService);
... ... @@ -193,7 +197,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
193 197
194 198 @Override
195 199 public void onFailure(Throwable t) {
196   - onScriptExecutionError(scriptId);
  200 + onScriptExecutionError(scriptId, t, scriptBody);
197 201 if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) {
198 202 queueTimeoutMsgs.incrementAndGet();
199 203 }
... ... @@ -201,13 +205,16 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
201 205 }
202 206 }, callbackExecutor);
203 207 return Futures.transform(future, response -> {
  208 + stopWatch.stop();
  209 + log.trace("doInvokeFunction js-response took {}ms for uuid {}", stopWatch.getTotalTimeMillis(), response.getKey());
204 210 JsInvokeProtos.JsInvokeResponse invokeResult = response.getValue().getInvokeResponse();
205 211 if (invokeResult.getSuccess()) {
206 212 return invokeResult.getResult();
207 213 } else {
208   - onScriptExecutionError(scriptId);
  214 + final RuntimeException e = new RuntimeException(invokeResult.getErrorDetails());
  215 + onScriptExecutionError(scriptId, e, scriptBody);
209 216 log.debug("[{}] Failed to compile script due to [{}]: {}", scriptId, invokeResult.getErrorCode().name(), invokeResult.getErrorDetails());
210   - throw new RuntimeException(invokeResult.getErrorDetails());
  217 + throw e;
211 218 }
212 219 }, callbackExecutor);
213 220 }
... ...
... ... @@ -18,12 +18,12 @@ package org.thingsboard.server.service.script;
18 18 import com.fasterxml.jackson.core.type.TypeReference;
19 19 import com.fasterxml.jackson.databind.JsonNode;
20 20 import com.fasterxml.jackson.databind.ObjectMapper;
21   -import com.google.common.collect.Sets;
22 21 import com.google.common.util.concurrent.Futures;
23 22 import com.google.common.util.concurrent.ListenableFuture;
24 23 import com.google.common.util.concurrent.MoreExecutors;
25 24 import lombok.extern.slf4j.Slf4j;
26 25 import org.apache.commons.lang3.StringUtils;
  26 +import org.thingsboard.server.common.data.id.CustomerId;
27 27 import org.thingsboard.server.common.data.id.EntityId;
28 28 import org.thingsboard.server.common.data.id.TenantId;
29 29 import org.thingsboard.server.common.msg.TbMsg;
... ... @@ -32,6 +32,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
32 32 import javax.script.ScriptException;
33 33 import java.util.ArrayList;
34 34 import java.util.Collections;
  35 +import java.util.HashSet;
35 36 import java.util.List;
36 37 import java.util.Map;
37 38 import java.util.Set;
... ... @@ -102,140 +103,115 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S
102 103 String newMessageType = !StringUtils.isEmpty(messageType) ? messageType : msg.getType();
103 104 return TbMsg.transformMsg(msg, newMessageType, msg.getOriginator(), newMetadata, newData);
104 105 } catch (Throwable th) {
105   - th.printStackTrace();
106 106 throw new RuntimeException("Failed to unbind message data from javascript result", th);
107 107 }
108 108 }
109 109
110 110 @Override
111   - public List<TbMsg> executeUpdate(TbMsg msg) throws ScriptException {
112   - JsonNode result = executeScript(msg);
113   - if (result.isObject()) {
114   - return Collections.singletonList(unbindMsg(result, msg));
115   - } else if (result.isArray()){
116   - List<TbMsg> res = new ArrayList<>(result.size());
117   - result.forEach(jsonObject -> res.add(unbindMsg(jsonObject, msg)));
118   - return res;
119   - } else {
120   - log.warn("Wrong result type: {}", result.getNodeType());
121   - throw new ScriptException("Wrong result type: " + result.getNodeType());
  111 + public ListenableFuture<List<TbMsg>> executeUpdateAsync(TbMsg msg) {
  112 + ListenableFuture<JsonNode> result = executeScriptAsync(msg);
  113 + return Futures.transformAsync(result,
  114 + json -> executeUpdateTransform(msg, json),
  115 + MoreExecutors.directExecutor());
  116 + }
  117 +
  118 + ListenableFuture<List<TbMsg>> executeUpdateTransform(TbMsg msg, JsonNode json) {
  119 + if (json.isObject()) {
  120 + return Futures.immediateFuture(Collections.singletonList(unbindMsg(json, msg)));
  121 + } else if (json.isArray()) {
  122 + List<TbMsg> res = new ArrayList<>(json.size());
  123 + json.forEach(jsonObject -> res.add(unbindMsg(jsonObject, msg)));
  124 + return Futures.immediateFuture(res);
122 125 }
  126 + log.warn("Wrong result type: {}", json.getNodeType());
  127 + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + json.getNodeType()));
123 128 }
124 129
125 130 @Override
126   - public ListenableFuture<List<TbMsg>> executeUpdateAsync(TbMsg msg) {
127   - ListenableFuture<JsonNode> result = executeScriptAsync(msg);
128   - return Futures.transformAsync(result, json -> {
129   - if (json.isObject()) {
130   - return Futures.immediateFuture(Collections.singletonList(unbindMsg(json, msg)));
131   - } else if (json.isArray()){
132   - List<TbMsg> res = new ArrayList<>(json.size());
133   - json.forEach(jsonObject -> res.add(unbindMsg(jsonObject, msg)));
134   - return Futures.immediateFuture(res);
135   - }
136   - else{
137   - log.warn("Wrong result type: {}", json.getNodeType());
138   - return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + json.getNodeType()));
139   - }
140   - }, MoreExecutors.directExecutor());
  131 + public ListenableFuture<TbMsg> executeGenerateAsync(TbMsg prevMsg) {
  132 + return Futures.transformAsync(executeScriptAsync(prevMsg),
  133 + result -> executeGenerateTransform(prevMsg, result),
  134 + MoreExecutors.directExecutor());
141 135 }
142 136
143   - @Override
144   - public TbMsg executeGenerate(TbMsg prevMsg) throws ScriptException {
145   - JsonNode result = executeScript(prevMsg);
  137 + ListenableFuture<TbMsg> executeGenerateTransform(TbMsg prevMsg, JsonNode result) {
146 138 if (!result.isObject()) {
147 139 log.warn("Wrong result type: {}", result.getNodeType());
148   - throw new ScriptException("Wrong result type: " + result.getNodeType());
  140 + Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + result.getNodeType()));
149 141 }
150   - return unbindMsg(result, prevMsg);
  142 + return Futures.immediateFuture(unbindMsg(result, prevMsg));
151 143 }
152 144
153 145 @Override
154   - public JsonNode executeJson(TbMsg msg) throws ScriptException {
155   - return executeScript(msg);
156   - }
157   -
158   - @Override
159   - public ListenableFuture<JsonNode> executeJsonAsync(TbMsg msg) throws ScriptException {
  146 + public ListenableFuture<JsonNode> executeJsonAsync(TbMsg msg) {
160 147 return executeScriptAsync(msg);
161 148 }
162 149
163 150 @Override
164   - public String executeToString(TbMsg msg) throws ScriptException {
165   - JsonNode result = executeScript(msg);
166   - if (!result.isTextual()) {
167   - log.warn("Wrong result type: {}", result.getNodeType());
168   - throw new ScriptException("Wrong result type: " + result.getNodeType());
169   - }
170   - return result.asText();
  151 + public ListenableFuture<String> executeToStringAsync(TbMsg msg) {
  152 + return Futures.transformAsync(executeScriptAsync(msg),
  153 + this::executeToStringTransform,
  154 + MoreExecutors.directExecutor());
171 155 }
172 156
173   - @Override
174   - public boolean executeFilter(TbMsg msg) throws ScriptException {
175   - JsonNode result = executeScript(msg);
176   - if (!result.isBoolean()) {
177   - log.warn("Wrong result type: {}", result.getNodeType());
178   - throw new ScriptException("Wrong result type: " + result.getNodeType());
  157 + ListenableFuture<String> executeToStringTransform(JsonNode result) {
  158 + if (result.isTextual()) {
  159 + return Futures.immediateFuture(result.asText());
179 160 }
180   - return result.asBoolean();
  161 + log.warn("Wrong result type: {}", result.getNodeType());
  162 + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + result.getNodeType()));
181 163 }
182 164
183 165 @Override
184 166 public ListenableFuture<Boolean> executeFilterAsync(TbMsg msg) {
185   - ListenableFuture<JsonNode> result = executeScriptAsync(msg);
186   - return Futures.transformAsync(result, json -> {
187   - if (!json.isBoolean()) {
188   - log.warn("Wrong result type: {}", json.getNodeType());
189   - return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + json.getNodeType()));
190   - } else {
191   - return Futures.immediateFuture(json.asBoolean());
192   - }
193   - }, MoreExecutors.directExecutor());
  167 + return Futures.transformAsync(executeScriptAsync(msg),
  168 + this::executeFilterTransform,
  169 + MoreExecutors.directExecutor());
194 170 }
195 171
196   - @Override
197   - public Set<String> executeSwitch(TbMsg msg) throws ScriptException {
198   - JsonNode result = executeScript(msg);
  172 + ListenableFuture<Boolean> executeFilterTransform(JsonNode json) {
  173 + if (json.isBoolean()) {
  174 + return Futures.immediateFuture(json.asBoolean());
  175 + }
  176 + log.warn("Wrong result type: {}", json.getNodeType());
  177 + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + json.getNodeType()));
  178 + }
  179 +
  180 + ListenableFuture<Set<String>> executeSwitchTransform(JsonNode result) {
199 181 if (result.isTextual()) {
200   - return Collections.singleton(result.asText());
201   - } else if (result.isArray()) {
202   - Set<String> nextStates = Sets.newHashSet();
  182 + return Futures.immediateFuture(Collections.singleton(result.asText()));
  183 + }
  184 + if (result.isArray()) {
  185 + Set<String> nextStates = new HashSet<>();
203 186 for (JsonNode val : result) {
204 187 if (!val.isTextual()) {
205 188 log.warn("Wrong result type: {}", val.getNodeType());
206   - throw new ScriptException("Wrong result type: " + val.getNodeType());
  189 + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + val.getNodeType()));
207 190 } else {
208 191 nextStates.add(val.asText());
209 192 }
210 193 }
211   - return nextStates;
212   - } else {
213   - log.warn("Wrong result type: {}", result.getNodeType());
214   - throw new ScriptException("Wrong result type: " + result.getNodeType());
  194 + return Futures.immediateFuture(nextStates);
215 195 }
  196 + log.warn("Wrong result type: {}", result.getNodeType());
  197 + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + result.getNodeType()));
216 198 }
217 199
218   - private JsonNode executeScript(TbMsg msg) throws ScriptException {
219   - try {
220   - String[] inArgs = prepareArgs(msg);
221   - String eval = sandboxService.invokeFunction(tenantId, msg.getCustomerId(), this.scriptId, inArgs[0], inArgs[1], inArgs[2]).get().toString();
222   - return mapper.readTree(eval);
223   - } catch (ExecutionException e) {
224   - if (e.getCause() instanceof ScriptException) {
225   - throw (ScriptException) e.getCause();
226   - } else if (e.getCause() instanceof RuntimeException) {
227   - throw new ScriptException(e.getCause().getMessage());
228   - } else {
229   - throw new ScriptException(e);
230   - }
231   - } catch (Exception e) {
232   - throw new ScriptException(e);
233   - }
  200 + @Override
  201 + public ListenableFuture<Set<String>> executeSwitchAsync(TbMsg msg) {
  202 + return Futures.transformAsync(executeScriptAsync(msg),
  203 + this::executeSwitchTransform,
  204 + MoreExecutors.directExecutor()); //usually runs in a callbackExecutor
234 205 }
235 206
236   - private ListenableFuture<JsonNode> executeScriptAsync(TbMsg msg) {
  207 + ListenableFuture<JsonNode> executeScriptAsync(TbMsg msg) {
  208 + log.trace("execute script async, msg {}", msg);
237 209 String[] inArgs = prepareArgs(msg);
238   - return Futures.transformAsync(sandboxService.invokeFunction(tenantId, msg.getCustomerId(), this.scriptId, inArgs[0], inArgs[1], inArgs[2]),
  210 + return executeScriptAsync(msg.getCustomerId(), inArgs[0], inArgs[1], inArgs[2]);
  211 + }
  212 +
  213 + ListenableFuture<JsonNode> executeScriptAsync(CustomerId customerId, Object... args) {
  214 + return Futures.transformAsync(sandboxService.invokeFunction(tenantId, customerId, this.scriptId, args),
239 215 o -> {
240 216 try {
241 217 return Futures.immediateFuture(mapper.readTree(o.toString()));
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.security.auth.oauth2;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
  21 +import org.springframework.stereotype.Service;
  22 +import org.springframework.util.LinkedMultiValueMap;
  23 +import org.springframework.util.MultiValueMap;
  24 +import org.springframework.util.StringUtils;
  25 +import org.thingsboard.common.util.JacksonUtil;
  26 +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
  27 +import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
  28 +import org.thingsboard.server.dao.oauth2.OAuth2User;
  29 +import org.thingsboard.server.service.security.model.SecurityUser;
  30 +
  31 +import javax.servlet.http.HttpServletRequest;
  32 +import java.util.HashMap;
  33 +import java.util.Map;
  34 +
  35 +@Service(value = "appleOAuth2ClientMapper")
  36 +@Slf4j
  37 +public class AppleOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
  38 +
  39 + private static final String USER = "user";
  40 + private static final String NAME = "name";
  41 + private static final String FIRST_NAME = "firstName";
  42 + private static final String LAST_NAME = "lastName";
  43 + private static final String EMAIL = "email";
  44 +
  45 + @Override
  46 + public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
  47 + OAuth2MapperConfig config = registration.getMapperConfig();
  48 + Map<String, Object> attributes = updateAttributesFromRequestParams(request, token.getPrincipal().getAttributes());
  49 + String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey());
  50 + OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config);
  51 +
  52 + return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration);
  53 + }
  54 +
  55 + private static Map<String, Object> updateAttributesFromRequestParams(HttpServletRequest request, Map<String, Object> attributes) {
  56 + Map<String, Object> updated = attributes;
  57 + MultiValueMap<String, String> params = toMultiMap(request.getParameterMap());
  58 + String userValue = params.getFirst(USER);
  59 + if (StringUtils.hasText(userValue)) {
  60 + JsonNode user = null;
  61 + try {
  62 + user = JacksonUtil.toJsonNode(userValue);
  63 + } catch (Exception e) {}
  64 + if (user != null) {
  65 + updated = new HashMap<>(attributes);
  66 + if (user.has(NAME)) {
  67 + JsonNode name = user.get(NAME);
  68 + if (name.isObject()) {
  69 + JsonNode firstName = name.get(FIRST_NAME);
  70 + if (firstName != null && firstName.isTextual()) {
  71 + updated.put(FIRST_NAME, firstName.asText());
  72 + }
  73 + JsonNode lastName = name.get(LAST_NAME);
  74 + if (lastName != null && lastName.isTextual()) {
  75 + updated.put(LAST_NAME, lastName.asText());
  76 + }
  77 + }
  78 + }
  79 + if (user.has(EMAIL)) {
  80 + JsonNode email = user.get(EMAIL);
  81 + if (email != null && email.isTextual()) {
  82 + updated.put(EMAIL, email.asText());
  83 + }
  84 + }
  85 + }
  86 + }
  87 + return updated;
  88 + }
  89 +
  90 + private static MultiValueMap<String, String> toMultiMap(Map<String, String[]> map) {
  91 + MultiValueMap<String, String> params = new LinkedMultiValueMap<>(map.size());
  92 + map.forEach((key, values) -> {
  93 + if (values.length > 0) {
  94 + for (String value : values) {
  95 + params.add(key, value);
  96 + }
  97 + }
  98 + });
  99 + return params;
  100 + }
  101 +}
... ...
... ... @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
23 23 import org.thingsboard.server.dao.oauth2.OAuth2User;
24 24 import org.thingsboard.server.service.security.model.SecurityUser;
25 25
  26 +import javax.servlet.http.HttpServletRequest;
26 27 import java.util.Map;
27 28
28 29 @Service(value = "basicOAuth2ClientMapper")
... ... @@ -30,7 +31,7 @@ import java.util.Map;
30 31 public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
31 32
32 33 @Override
33   - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
  34 + public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
34 35 OAuth2MapperConfig config = registration.getMapperConfig();
35 36 Map<String, Object> attributes = token.getPrincipal().getAttributes();
36 37 String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey());
... ...
... ... @@ -29,6 +29,8 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
29 29 import org.thingsboard.server.dao.oauth2.OAuth2User;
30 30 import org.thingsboard.server.service.security.model.SecurityUser;
31 31
  32 +import javax.servlet.http.HttpServletRequest;
  33 +
32 34 @Service(value = "customOAuth2ClientMapper")
33 35 @Slf4j
34 36 public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
... ... @@ -39,7 +41,7 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme
39 41 private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
40 42
41 43 @Override
42   - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
  44 + public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
43 45 OAuth2MapperConfig config = registration.getMapperConfig();
44 46 OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom());
45 47 return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration);
... ...
... ... @@ -29,6 +29,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
29 29 import org.thingsboard.server.dao.oauth2.OAuth2User;
30 30 import org.thingsboard.server.service.security.model.SecurityUser;
31 31
  32 +import javax.servlet.http.HttpServletRequest;
32 33 import java.util.ArrayList;
33 34 import java.util.Map;
34 35 import java.util.Optional;
... ... @@ -46,7 +47,7 @@ public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme
46 47 private OAuth2Configuration oAuth2Configuration;
47 48
48 49 @Override
49   - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
  50 + public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
50 51 OAuth2MapperConfig config = registration.getMapperConfig();
51 52 Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper();
52 53 String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken);
... ...
... ... @@ -20,6 +20,8 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
20 20 import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo;
21 21 import org.thingsboard.server.service.security.model.SecurityUser;
22 22
  23 +import javax.servlet.http.HttpServletRequest;
  24 +
23 25 public interface OAuth2ClientMapper {
24   - SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration);
  26 + SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration);
25 27 }
... ...
... ... @@ -37,6 +37,10 @@ public class OAuth2ClientMapperProvider {
37 37 @Qualifier("githubOAuth2ClientMapper")
38 38 private OAuth2ClientMapper githubOAuth2ClientMapper;
39 39
  40 + @Autowired
  41 + @Qualifier("appleOAuth2ClientMapper")
  42 + private OAuth2ClientMapper appleOAuth2ClientMapper;
  43 +
40 44 public OAuth2ClientMapper getOAuth2ClientMapperByType(MapperType oauth2MapperType) {
41 45 switch (oauth2MapperType) {
42 46 case CUSTOM:
... ... @@ -45,6 +49,8 @@ public class OAuth2ClientMapperProvider {
45 49 return basicOAuth2ClientMapper;
46 50 case GITHUB:
47 51 return githubOAuth2ClientMapper;
  52 + case APPLE:
  53 + return appleOAuth2ClientMapper;
48 54 default:
49 55 throw new RuntimeException("OAuth2ClientRegistrationMapper with type " + oauth2MapperType + " is not supported!");
50 56 }
... ...
... ... @@ -36,7 +36,6 @@ import java.net.URLEncoder;
36 36 import java.nio.charset.StandardCharsets;
37 37
38 38 @Component(value = "oauth2AuthenticationFailureHandler")
39   -@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")
40 39 public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
41 40
42 41 private final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
... ...
... ... @@ -90,7 +90,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
90 90 token.getAuthorizedClientRegistrationId(),
91 91 token.getPrincipal().getName());
92 92 OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(registration.getMapperConfig().getType());
93   - SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(),
  93 + SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(request, token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(),
94 94 registration);
95 95
96 96 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
... ...
... ... @@ -332,6 +332,7 @@ actors:
332 332 cache:
333 333 # caffeine or redis
334 334 type: "${CACHE_TYPE:caffeine}"
  335 + maximumPoolSize: "${CACHE_MAXIMUM_POOL_SIZE:16}" # max pool size to process futures that calls the external cache
335 336 attributes:
336 337 # make sure that if cache.type is 'redis' and cache.attributes.enabled is 'true' that you change 'maxmemory-policy' Redis config property to 'allkeys-lru', 'allkeys-lfu' or 'allkeys-random'
337 338 enabled: "${CACHE_ATTRIBUTES_ENABLED:true}"
... ... @@ -677,12 +678,11 @@ transport:
677 678 timeout: "${LWM2M_TIMEOUT:120000}"
678 679 recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
679 680 recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}"
680   - response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}"
681   - registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}"
  681 + uplink_pool_size: "${LWM2M_UPLINK_POOL_SIZE:10}"
  682 + downlink_pool_size: "${LWM2M_DOWNLINK_POOL_SIZE:10}"
  683 + ota_pool_size: "${LWM2M_OTA_POOL_SIZE:10}"
682 684 registration_store_pool_size: "${LWM2M_REGISTRATION_STORE_POOL_SIZE:100}"
683 685 clean_period_in_sec: "${LWM2M_CLEAN_PERIOD_IN_SEC:2}"
684   - update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}"
685   - un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}"
686 686 log_max_length: "${LWM2M_LOG_MAX_LENGTH:100}"
687 687 # Use redis for Security and Registration stores
688 688 redis.enabled: "${LWM2M_REDIS_ENABLED:false}"
... ...
... ... @@ -141,10 +141,12 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
141 141
142 142 MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
143 143
144   - OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
  144 + OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
145 145
146 146 Assert.assertEquals(FILE_NAME, savedFirmware.getFileName());
147 147 Assert.assertEquals(CONTENT_TYPE, savedFirmware.getContentType());
  148 + Assert.assertEquals(CHECKSUM_ALGORITHM, savedFirmware.getChecksumAlgorithm().name());
  149 + Assert.assertEquals(CHECKSUM, savedFirmware.getChecksum());
148 150 }
149 151
150 152 @Test
... ... @@ -189,11 +191,12 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
189 191
190 192 MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
191 193
192   - OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
  194 + OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
193 195
194 196 OtaPackage foundFirmware = doGet("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString(), OtaPackage.class);
195 197 Assert.assertNotNull(foundFirmware);
196   - Assert.assertEquals(savedFirmware, foundFirmware);
  198 + Assert.assertEquals(savedFirmware, new OtaPackageInfo(foundFirmware));
  199 + Assert.assertEquals(DATA, foundFirmware.getData());
197 200 }
198 201
199 202 @Test
... ... @@ -228,8 +231,8 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
228 231 if (i > 100) {
229 232 MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
230 233
231   - OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
232   - otaPackages.add(new OtaPackageInfo(savedFirmware));
  234 + OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
  235 + otaPackages.add(savedFirmware);
233 236 } else {
234 237 otaPackages.add(savedFirmwareInfo);
235 238 }
... ... @@ -271,7 +274,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
271 274 if (i > 100) {
272 275 MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
273 276
274   - OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
  277 + OtaPackageInfo savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
275 278 savedFirmwareInfo = new OtaPackageInfo(savedFirmware);
276 279 otaPackagesWithData.add(savedFirmwareInfo);
277 280 }
... ... @@ -318,11 +321,11 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes
318 321 return doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);
319 322 }
320 323
321   - protected OtaPackage savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception {
  324 + protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception {
322 325 MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params);
323 326 postRequest.file(content);
324 327 setJwtToken(postRequest);
325   - return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackage.class);
  328 + return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackageInfo.class);
326 329 }
327 330
328 331 }
... ...
... ... @@ -103,7 +103,9 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
103 103 " }\n" +
104 104 " },\n" +
105 105 " \"clientLwM2mSettings\": {\n" +
106   - " \"clientOnlyObserveAfterConnect\": 1\n" +
  106 + " \"clientOnlyObserveAfterConnect\": 1,\n" +
  107 + " \"fwUpdateStrategy\": 1,\n" +
  108 + " \"swUpdateStrategy\": 1\n" +
107 109 " }\n" +
108 110 "}";
109 111
... ...
... ... @@ -17,11 +17,14 @@ package org.thingsboard.server.transport.lwm2m;
17 17
18 18 import org.eclipse.californium.core.network.config.NetworkConfig;
19 19 import org.eclipse.leshan.client.object.Security;
20   -import org.jetbrains.annotations.NotNull;
21 20 import org.junit.Assert;
22 21 import org.junit.Test;
  22 +import org.springframework.mock.web.MockMultipartFile;
  23 +import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
  24 +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
23 25 import org.thingsboard.common.util.JacksonUtil;
24 26 import org.thingsboard.server.common.data.Device;
  27 +import org.thingsboard.server.common.data.OtaPackageInfo;
25 28 import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials;
26 29 import org.thingsboard.server.common.data.query.EntityData;
27 30 import org.thingsboard.server.common.data.query.EntityDataPageLink;
... ... @@ -43,15 +46,15 @@ import java.util.List;
43 46
44 47 import static org.eclipse.leshan.client.object.Security.noSec;
45 48 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  49 +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
46 50
47 51 public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
48 52
49 53 private final int PORT = 5685;
50 54 private final Security SECURITY = noSec("coap://localhost:" + PORT, 123);
51 55 private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT));
52   - private final String ENDPOINT = "deviceAEndpoint";
  56 + private final String ENDPOINT = "noSecEndpoint";
53 57
54   - @NotNull
55 58 private Device createDevice() throws Exception {
56 59 Device device = new Device();
57 60 device.setName("Device A");
... ... @@ -74,6 +77,29 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
74 77 return device;
75 78 }
76 79
  80 + private OtaPackageInfo createFirmware() throws Exception {
  81 + String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a";
  82 +
  83 + OtaPackageInfo firmwareInfo = new OtaPackageInfo();
  84 + firmwareInfo.setDeviceProfileId(deviceProfile.getId());
  85 + firmwareInfo.setType(FIRMWARE);
  86 + firmwareInfo.setTitle("My firmware");
  87 + firmwareInfo.setVersion("v1.0");
  88 +
  89 + OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);
  90 +
  91 + MockMultipartFile testData = new MockMultipartFile("file", "filename.txt", "text/plain", new byte[]{1});
  92 +
  93 + return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256");
  94 + }
  95 +
  96 + protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception {
  97 + MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params);
  98 + postRequest.file(content);
  99 + setJwtToken(postRequest);
  100 + return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackageInfo.class);
  101 + }
  102 +
77 103 @Test
78 104 public void testConnectAndObserveTelemetry() throws Exception {
79 105 createDeviceProfile(TRANSPORT_CONFIGURATION);
... ... @@ -111,4 +137,53 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
111 137 client.destroy();
112 138 }
113 139
  140 + @Test
  141 + public void testFirmwareUpdateWithClientWithoutFirmwareInfo() throws Exception {
  142 + createDeviceProfile(TRANSPORT_CONFIGURATION);
  143 +
  144 + Device device = createDevice();
  145 +
  146 + OtaPackageInfo firmware = createFirmware();
  147 +
  148 + LwM2MTestClient client = new LwM2MTestClient(executor, ENDPOINT);
  149 + client.init(SECURITY, COAP_CONFIG);
  150 +
  151 + Thread.sleep(1000);
  152 +
  153 + device.setFirmwareId(firmware.getId());
  154 +
  155 + device = doPost("/api/device", device, Device.class);
  156 +
  157 + Thread.sleep(1000);
  158 +
  159 + SingleEntityFilter sef = new SingleEntityFilter();
  160 + sef.setSingleEntity(device.getId());
  161 + LatestValueCmd latestCmd = new LatestValueCmd();
  162 + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "fw_state")));
  163 + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),
  164 + Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
  165 +
  166 + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
  167 + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
  168 + wrapper.setEntityDataCmds(Collections.singletonList(cmd));
  169 +
  170 + wsClient.send(mapper.writeValueAsString(wrapper));
  171 + wsClient.waitForReply();
  172 +
  173 + wsClient.registerWaitForUpdate();
  174 +
  175 + String msg = wsClient.waitForUpdate();
  176 +
  177 + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
  178 + Assert.assertEquals(1, update.getCmdId());
  179 + List<EntityData> eData = update.getUpdate();
  180 + Assert.assertNotNull(eData);
  181 + Assert.assertEquals(1, eData.size());
  182 + Assert.assertEquals(device.getId(), eData.get(0).getEntityId());
  183 + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));
  184 + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("fw_state");
  185 + Assert.assertEquals("FAILED", tsValue.getValue());
  186 + client.destroy();
  187 + }
  188 +
114 189 }
... ...
... ... @@ -19,6 +19,7 @@ import org.eclipse.californium.core.network.config.NetworkConfig;
19 19 import org.eclipse.leshan.client.object.Security;
20 20 import org.jetbrains.annotations.NotNull;
21 21 import org.junit.Assert;
  22 +import org.junit.Ignore;
22 23 import org.junit.Test;
23 24 import org.thingsboard.common.util.JacksonUtil;
24 25 import org.thingsboard.server.common.data.Device;
... ... @@ -52,7 +53,6 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
52 53 private final String endpoint = "deviceAEndpoint";
53 54 private final String serverUri = "coaps://localhost:" + port;
54 55
55   - @NotNull
56 56 private Device createDevice(X509ClientCredentials clientCredentials) throws Exception {
57 57 Device device = new Device();
58 58 device.setName("Device A");
... ... @@ -75,11 +75,13 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
75 75 return device;
76 76 }
77 77
  78 + //TODO: use different endpoints to isolate tests.
  79 + @Ignore()
78 80 @Test
79 81 public void testConnectAndObserveTelemetry() throws Exception {
80 82 createDeviceProfile(TRANSPORT_CONFIGURATION);
81 83 X509ClientCredentials credentials = new X509ClientCredentials();
82   - credentials.setEndpoint(endpoint);
  84 + credentials.setEndpoint(endpoint+1);
83 85 Device device = createDevice(credentials);
84 86
85 87 SingleEntityFilter sef = new SingleEntityFilter();
... ... @@ -97,7 +99,7 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
97 99 wsClient.waitForReply();
98 100
99 101 wsClient.registerWaitForUpdate();
100   - LwM2MTestClient client = new LwM2MTestClient(executor, endpoint);
  102 + LwM2MTestClient client = new LwM2MTestClient(executor, endpoint+1);
101 103 Security security = x509(serverUri, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded());
102 104 client.init(security, coapConfig);
103 105 String msg = wsClient.waitForUpdate();
... ...
... ... @@ -14,7 +14,7 @@
14 14 <logger name="org.springframework.boot.test" level="WARN"/>
15 15 <logger name="org.apache.cassandra" level="WARN"/>
16 16 <logger name="org.cassandraunit" level="INFO"/>
17   - <logger name="org.eclipse.leshan" level="TRACE"/>
  17 + <logger name="org.eclipse.leshan" level="INFO"/>
18 18
19 19
20 20 <root level="WARN">
... ...
... ... @@ -41,6 +41,7 @@ public class ActorSystemTest {
41 41
42 42 public static final String ROOT_DISPATCHER = "root-dispatcher";
43 43 private static final int _100K = 100 * 1024;
  44 + public static final int TIMEOUT_AWAIT_MAX_SEC = 10;
44 45
45 46 private volatile TbActorSystem actorSystem;
46 47 private volatile ExecutorService submitPool;
... ... @@ -52,7 +53,7 @@ public class ActorSystemTest {
52 53 parallelism = Math.max(2, cores / 2);
53 54 TbActorSystemSettings settings = new TbActorSystemSettings(5, parallelism, 42);
54 55 actorSystem = new DefaultTbActorSystem(settings);
55   - submitPool = Executors.newWorkStealingPool(parallelism);
  56 + submitPool = Executors.newFixedThreadPool(parallelism); //order guaranteed
56 57 }
57 58
58 59 @After
... ... @@ -122,13 +123,23 @@ public class ActorSystemTest {
122 123 ActorTestCtx testCtx1 = getActorTestCtx(1);
123 124 ActorTestCtx testCtx2 = getActorTestCtx(1);
124 125 TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID()));
125   - submitPool.submit(() -> actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx1)));
126   - submitPool.submit(() -> actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx2)));
  126 + final CountDownLatch initLatch = new CountDownLatch(1);
  127 + final CountDownLatch actorsReadyLatch = new CountDownLatch(2);
  128 + submitPool.submit(() -> {
  129 + actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx1, initLatch));
  130 + actorsReadyLatch.countDown();
  131 + });
  132 + submitPool.submit(() -> {
  133 + actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx2, initLatch));
  134 + actorsReadyLatch.countDown();
  135 + });
  136 +
  137 + initLatch.countDown(); //replacement for Thread.wait(500) in the SlowCreateActorCreator
  138 + Assert.assertTrue(actorsReadyLatch.await(TIMEOUT_AWAIT_MAX_SEC, TimeUnit.SECONDS));
127 139
128   - Thread.sleep(1000);
129 140 actorSystem.tell(actorId, new IntTbActorMsg(42));
130 141
131   - Assert.assertTrue(testCtx1.getLatch().await(1, TimeUnit.SECONDS));
  142 + Assert.assertTrue(testCtx1.getLatch().await(TIMEOUT_AWAIT_MAX_SEC, TimeUnit.SECONDS));
132 143 Assert.assertFalse(testCtx2.getLatch().await(1, TimeUnit.SECONDS));
133 144 }
134 145
... ... @@ -137,13 +148,21 @@ public class ActorSystemTest {
137 148 actorSystem.createDispatcher(ROOT_DISPATCHER, Executors.newWorkStealingPool(parallelism));
138 149 ActorTestCtx testCtx = getActorTestCtx(1);
139 150 TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID()));
140   - for (int i = 0; i < 1000; i++) {
141   - submitPool.submit(() -> actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx)));
  151 + final int actorsCount = 1000;
  152 + final CountDownLatch initLatch = new CountDownLatch(1);
  153 + final CountDownLatch actorsReadyLatch = new CountDownLatch(actorsCount);
  154 + for (int i = 0; i < actorsCount; i++) {
  155 + submitPool.submit(() -> {
  156 + actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx, initLatch));
  157 + actorsReadyLatch.countDown();
  158 + });
142 159 }
143   - Thread.sleep(1000);
  160 + initLatch.countDown();
  161 + Assert.assertTrue(actorsReadyLatch.await(TIMEOUT_AWAIT_MAX_SEC, TimeUnit.SECONDS));
  162 +
144 163 actorSystem.tell(actorId, new IntTbActorMsg(42));
145 164
146   - Assert.assertTrue(testCtx.getLatch().await(1, TimeUnit.SECONDS));
  165 + Assert.assertTrue(testCtx.getLatch().await(TIMEOUT_AWAIT_MAX_SEC, TimeUnit.SECONDS));
147 166 //One for creation and one for message
148 167 Assert.assertEquals(2, testCtx.getInvocationCount().get());
149 168 }
... ...
... ... @@ -17,13 +17,18 @@ package org.thingsboard.server.actors;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
19 19
  20 +import java.util.concurrent.CountDownLatch;
  21 +import java.util.concurrent.TimeUnit;
  22 +
20 23 @Slf4j
21 24 public class SlowCreateActor extends TestRootActor {
22 25
23   - public SlowCreateActor(TbActorId actorId, ActorTestCtx testCtx) {
  26 + public static final int TIMEOUT_AWAIT_MAX_MS = 5000;
  27 +
  28 + public SlowCreateActor(TbActorId actorId, ActorTestCtx testCtx, CountDownLatch initLatch) {
24 29 super(actorId, testCtx);
25 30 try {
26   - Thread.sleep(500);
  31 + initLatch.await(TIMEOUT_AWAIT_MAX_MS, TimeUnit.MILLISECONDS);
27 32 } catch (InterruptedException e) {
28 33 e.printStackTrace();
29 34 }
... ... @@ -34,10 +39,12 @@ public class SlowCreateActor extends TestRootActor {
34 39
35 40 private final TbActorId actorId;
36 41 private final ActorTestCtx testCtx;
  42 + private final CountDownLatch initLatch;
37 43
38   - public SlowCreateActorCreator(TbActorId actorId, ActorTestCtx testCtx) {
  44 + public SlowCreateActorCreator(TbActorId actorId, ActorTestCtx testCtx, CountDownLatch initLatch) {
39 45 this.actorId = actorId;
40 46 this.testCtx = testCtx;
  47 + this.initLatch = initLatch;
41 48 }
42 49
43 50 @Override
... ... @@ -47,7 +54,7 @@ public class SlowCreateActor extends TestRootActor {
47 54
48 55 @Override
49 56 public TbActor createActor() {
50   - return new SlowCreateActor(actorId, testCtx);
  57 + return new SlowCreateActor(actorId, testCtx, initLatch);
51 58 }
52 59 }
53 60 }
... ...
... ... @@ -36,6 +36,8 @@ import java.util.concurrent.Executors;
36 36 import java.util.concurrent.ScheduledExecutorService;
37 37 import java.util.concurrent.TimeUnit;
38 38
  39 +import static org.eclipse.californium.core.network.config.NetworkConfigDefaults.DEFAULT_BLOCKWISE_STATUS_LIFETIME;
  40 +
39 41 @Slf4j
40 42 @Component
41 43 @TbCoapServerComponent
... ... @@ -91,7 +93,16 @@ public class DefaultCoapServerService implements CoapServerService {
91 93 InetAddress addr = InetAddress.getByName(coapServerContext.getHost());
92 94 InetSocketAddress sockAddr = new InetSocketAddress(addr, coapServerContext.getPort());
93 95 noSecCoapEndpointBuilder.setInetSocketAddress(sockAddr);
94   - noSecCoapEndpointBuilder.setNetworkConfig(NetworkConfig.getStandard());
  96 + NetworkConfig networkConfig = new NetworkConfig();
  97 + networkConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
  98 + networkConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
  99 + networkConfig.setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME);
  100 + networkConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024);
  101 + networkConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED");
  102 + networkConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024);
  103 + networkConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024);
  104 + networkConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4);
  105 + noSecCoapEndpointBuilder.setNetworkConfig(networkConfig);
95 106 CoapEndpoint noSecCoapEndpoint = noSecCoapEndpointBuilder.build();
96 107 server.addEndpoint(noSecCoapEndpoint);
97 108
... ...
... ... @@ -51,6 +51,13 @@ public class SnmpDeviceTransportConfiguration implements DeviceTransportConfigur
51 51 private String privacyPassphrase;
52 52 private String engineId;
53 53
  54 + public SnmpDeviceTransportConfiguration() {
  55 + this.host = "localhost";
  56 + this.port = 161;
  57 + this.protocolVersion = SnmpProtocolVersion.V2C;
  58 + this.community = "public";
  59 + }
  60 +
54 61 @Override
55 62 public DeviceTransportType getType() {
56 63 return DeviceTransportType.SNMP;
... ... @@ -76,7 +83,7 @@ public class SnmpDeviceTransportConfiguration implements DeviceTransportConfigur
76 83 isValid = StringUtils.isNotBlank(username) && StringUtils.isNotBlank(securityName)
77 84 && contextName != null && authenticationProtocol != null
78 85 && StringUtils.isNotBlank(authenticationPassphrase)
79   - && privacyProtocol != null && privacyPassphrase != null && engineId != null;
  86 + && privacyProtocol != null && StringUtils.isNotBlank(privacyPassphrase) && engineId != null;
80 87 break;
81 88 }
82 89 }
... ...
... ... @@ -22,10 +22,10 @@ import org.thingsboard.server.common.data.device.data.PowerMode;
22 22 public class OtherConfiguration {
23 23
24 24 private Integer fwUpdateStrategy;
25   - private String fwUpdateResource;
26 25 private Integer swUpdateStrategy;
27   - private String swUpdateResource;
28 26 private Integer clientOnlyObserveAfterConnect;
29 27 private PowerMode powerMode;
  28 + private String fwUpdateResource;
  29 + private String swUpdateResource;
30 30
31 31 }
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.common.data.device.profile;
17 17
18 18 import lombok.Data;
  19 +import org.thingsboard.server.common.data.id.DashboardId;
19 20 import org.thingsboard.server.common.data.validation.NoXss;
20 21
21 22 import javax.validation.Valid;
... ... @@ -31,5 +32,6 @@ public class AlarmRule implements Serializable {
31 32 // Advanced
32 33 @NoXss
33 34 private String alarmDetails;
  35 + private DashboardId dashboardId;
34 36
35 37 }
... ...
... ... @@ -16,5 +16,5 @@
16 16 package org.thingsboard.server.common.data.oauth2;
17 17
18 18 public enum MapperType {
19   - BASIC, CUSTOM, GITHUB;
  19 + BASIC, CUSTOM, GITHUB, APPLE;
20 20 }
... ...
... ... @@ -16,5 +16,5 @@
16 16 package org.thingsboard.server.common.msg.session;
17 17
18 18 public enum FeatureType {
19   - ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE, SOFTWARE
  19 + ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION
20 20 }
... ...
... ... @@ -30,10 +30,7 @@ public enum SessionMsgType {
30 30
31 31 SESSION_OPEN, SESSION_CLOSE,
32 32
33   - CLAIM_REQUEST(),
34   -
35   - GET_FIRMWARE_REQUEST,
36   - GET_SOFTWARE_REQUEST;
  33 + CLAIM_REQUEST();
37 34
38 35 private final boolean requiresRulesProcessing;
39 36
... ...
... ... @@ -24,6 +24,8 @@ public interface TbQueueRequestTemplate<Request extends TbQueueMsg, Response ext
24 24
25 25 ListenableFuture<Response> send(Request request);
26 26
  27 + ListenableFuture<Response> send(Request request, long timeoutNs);
  28 +
27 29 void stop();
28 30
29 31 void setMessagesStats(MessagesStats messagesStats);
... ...
... ... @@ -19,7 +19,9 @@ import com.google.common.util.concurrent.Futures;
19 19 import com.google.common.util.concurrent.ListenableFuture;
20 20 import com.google.common.util.concurrent.SettableFuture;
21 21 import lombok.Builder;
  22 +import lombok.Getter;
22 23 import lombok.extern.slf4j.Slf4j;
  24 +import org.thingsboard.common.util.TbStopWatch;
23 25 import org.thingsboard.common.util.ThingsBoardThreadFactory;
24 26 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
25 27 import org.thingsboard.server.queue.TbQueueAdmin;
... ... @@ -31,13 +33,17 @@ import org.thingsboard.server.queue.TbQueueProducer;
31 33 import org.thingsboard.server.queue.TbQueueRequestTemplate;
32 34 import org.thingsboard.server.common.stats.MessagesStats;
33 35
  36 +import javax.annotation.Nullable;
34 37 import java.util.List;
35 38 import java.util.UUID;
36 39 import java.util.concurrent.ConcurrentHashMap;
37   -import java.util.concurrent.ConcurrentMap;
38 40 import java.util.concurrent.ExecutorService;
39 41 import java.util.concurrent.Executors;
  42 +import java.util.concurrent.TimeUnit;
40 43 import java.util.concurrent.TimeoutException;
  44 +import java.util.concurrent.locks.Lock;
  45 +import java.util.concurrent.locks.LockSupport;
  46 +import java.util.concurrent.locks.ReentrantLock;
41 47
42 48 @Slf4j
43 49 public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response extends TbQueueMsg> extends AbstractTbQueueTemplate
... ... @@ -46,15 +52,15 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
46 52 private final TbQueueAdmin queueAdmin;
47 53 private final TbQueueProducer<Request> requestTemplate;
48 54 private final TbQueueConsumer<Response> responseTemplate;
49   - private final ConcurrentMap<UUID, DefaultTbQueueRequestTemplate.ResponseMetaData<Response>> pendingRequests;
50   - private final boolean internalExecutor;
51   - private final ExecutorService executor;
52   - private final long maxRequestTimeout;
53   - private final long maxPendingRequests;
54   - private final long pollInterval;
55   - private volatile long tickTs = 0L;
56   - private volatile long tickSize = 0L;
57   - private volatile boolean stopped = false;
  55 + final ConcurrentHashMap<UUID, DefaultTbQueueRequestTemplate.ResponseMetaData<Response>> pendingRequests = new ConcurrentHashMap<>();
  56 + final boolean internalExecutor;
  57 + final ExecutorService executor;
  58 + final long maxRequestTimeoutNs;
  59 + final long maxPendingRequests;
  60 + final long pollInterval;
  61 + volatile boolean stopped = false;
  62 + long nextCleanupNs = 0L;
  63 + private final Lock cleanerLock = new ReentrantLock();
58 64
59 65 private MessagesStats messagesStats;
60 66
... ... @@ -65,79 +71,113 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
65 71 long maxRequestTimeout,
66 72 long maxPendingRequests,
67 73 long pollInterval,
68   - ExecutorService executor) {
  74 + @Nullable ExecutorService executor) {
69 75 this.queueAdmin = queueAdmin;
70 76 this.requestTemplate = requestTemplate;
71 77 this.responseTemplate = responseTemplate;
72   - this.pendingRequests = new ConcurrentHashMap<>();
73   - this.maxRequestTimeout = maxRequestTimeout;
  78 + this.maxRequestTimeoutNs = TimeUnit.MILLISECONDS.toNanos(maxRequestTimeout);
74 79 this.maxPendingRequests = maxPendingRequests;
75 80 this.pollInterval = pollInterval;
76   - if (executor != null) {
77   - internalExecutor = false;
78   - this.executor = executor;
79   - } else {
80   - internalExecutor = true;
81   - this.executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-queue-request-template-" + responseTemplate.getTopic()));
82   - }
  81 + this.internalExecutor = (executor == null);
  82 + this.executor = internalExecutor ? createExecutor() : executor;
  83 + }
  84 +
  85 + ExecutorService createExecutor() {
  86 + return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-queue-request-template-" + responseTemplate.getTopic()));
83 87 }
84 88
85 89 @Override
86 90 public void init() {
87 91 queueAdmin.createTopicIfNotExists(responseTemplate.getTopic());
88   - this.requestTemplate.init();
89   - tickTs = System.currentTimeMillis();
  92 + requestTemplate.init();
90 93 responseTemplate.subscribe();
91   - executor.submit(() -> {
92   - long nextCleanupMs = 0L;
93   - while (!stopped) {
94   - try {
95   - List<Response> responses = responseTemplate.poll(pollInterval);
96   - if (responses.size() > 0) {
97   - log.trace("Polling responses completed, consumer records count [{}]", responses.size());
98   - }
99   - responses.forEach(response -> {
100   - byte[] requestIdHeader = response.getHeaders().get(REQUEST_ID_HEADER);
101   - UUID requestId;
102   - if (requestIdHeader == null) {
103   - log.error("[{}] Missing requestId in header and body", response);
104   - } else {
105   - requestId = bytesToUuid(requestIdHeader);
106   - log.trace("[{}] Response received: {}", requestId, response);
107   - ResponseMetaData<Response> expectedResponse = pendingRequests.remove(requestId);
108   - if (expectedResponse == null) {
109   - log.trace("[{}] Invalid or stale request", requestId);
110   - } else {
111   - expectedResponse.future.set(response);
112   - }
  94 + executor.submit(this::mainLoop);
  95 + }
  96 +
  97 + void mainLoop() {
  98 + while (!stopped) {
  99 + TbStopWatch sw = TbStopWatch.startNew();
  100 + try {
  101 + fetchAndProcessResponses();
  102 + } catch (Throwable e) {
  103 + long sleepNanos = TimeUnit.MILLISECONDS.toNanos(this.pollInterval) - sw.stopAndGetTotalTimeNanos();
  104 + log.warn("Failed to obtain and process responses from queue. Going to sleep " + sleepNanos + "ns", e);
  105 + sleep(sleepNanos);
  106 + }
  107 + }
  108 + }
  109 +
  110 + void fetchAndProcessResponses() {
  111 + final long pendingRequestsCount = pendingRequests.mappingCount();
  112 + log.trace("Starting template pool topic {}, for pendingRequests {}", responseTemplate.getTopic(), pendingRequestsCount);
  113 + List<Response> responses = doPoll(); //poll js responses
  114 + log.trace("Completed template poll topic {}, for pendingRequests [{}], received [{}] responses", responseTemplate.getTopic(), pendingRequestsCount, responses.size());
  115 + responses.forEach(this::processResponse); //this can take a long time
  116 + responseTemplate.commit();
  117 + tryCleanStaleRequests();
  118 + }
  119 +
  120 + private boolean tryCleanStaleRequests() {
  121 + if (!cleanerLock.tryLock()) {
  122 + return false;
  123 + }
  124 + try {
  125 + log.trace("tryCleanStaleRequest...");
  126 + final long currentNs = getCurrentClockNs();
  127 + if (nextCleanupNs < currentNs) {
  128 + pendingRequests.forEach((key, value) -> {
  129 + if (value.expTime < currentNs) {
  130 + ResponseMetaData<Response> staleRequest = pendingRequests.remove(key);
  131 + if (staleRequest != null) {
  132 + setTimeoutException(key, staleRequest, currentNs);
113 133 }
114   - });
115   - responseTemplate.commit();
116   - tickTs = System.currentTimeMillis();
117   - tickSize = pendingRequests.size();
118   - if (nextCleanupMs < tickTs) {
119   - //cleanup;
120   - pendingRequests.forEach((key, value) -> {
121   - if (value.expTime < tickTs) {
122   - ResponseMetaData<Response> staleRequest = pendingRequests.remove(key);
123   - if (staleRequest != null) {
124   - log.trace("[{}] Request timeout detected, expTime [{}], tickTs [{}]", key, staleRequest.expTime, tickTs);
125   - staleRequest.future.setException(new TimeoutException());
126   - }
127   - }
128   - });
129   - nextCleanupMs = tickTs + maxRequestTimeout;
130   - }
131   - } catch (Throwable e) {
132   - log.warn("Failed to obtain responses from queue.", e);
133   - try {
134   - Thread.sleep(pollInterval);
135   - } catch (InterruptedException e2) {
136   - log.trace("Failed to wait until the server has capacity to handle new responses", e2);
137 134 }
138   - }
  135 + });
  136 + setupNextCleanup();
139 137 }
140   - });
  138 + } finally {
  139 + cleanerLock.unlock();
  140 + }
  141 + return true;
  142 + }
  143 +
  144 + void setupNextCleanup() {
  145 + nextCleanupNs = getCurrentClockNs() + maxRequestTimeoutNs;
  146 + log.trace("setupNextCleanup {}", nextCleanupNs);
  147 + }
  148 +
  149 + List<Response> doPoll() {
  150 + return responseTemplate.poll(pollInterval);
  151 + }
  152 +
  153 + void sleep(long nanos) {
  154 + LockSupport.parkNanos(nanos);
  155 + }
  156 +
  157 + void setTimeoutException(UUID key, ResponseMetaData<Response> staleRequest, long currentNs) {
  158 + if (currentNs >= staleRequest.getSubmitTime() + staleRequest.getTimeout()) {
  159 + log.warn("Request timeout detected, currentNs [{}], {}, key [{}]", currentNs, staleRequest, key);
  160 + } else {
  161 + log.error("Request timeout detected, currentNs [{}], {}, key [{}]", currentNs, staleRequest, key);
  162 + }
  163 + staleRequest.future.setException(new TimeoutException());
  164 + }
  165 +
  166 + void processResponse(Response response) {
  167 + byte[] requestIdHeader = response.getHeaders().get(REQUEST_ID_HEADER);
  168 + UUID requestId;
  169 + if (requestIdHeader == null) {
  170 + log.error("[{}] Missing requestId in header and body", response);
  171 + } else {
  172 + requestId = bytesToUuid(requestIdHeader);
  173 + log.trace("[{}] Response received: {}", requestId, String.valueOf(response).replace("\n", " ")); //TODO remove overhead
  174 + ResponseMetaData<Response> expectedResponse = pendingRequests.remove(requestId);
  175 + if (expectedResponse == null) {
  176 + log.warn("[{}] Invalid or stale request, response: {}", requestId, String.valueOf(response).replace("\n", " "));
  177 + } else {
  178 + expectedResponse.future.set(response);
  179 + }
  180 + }
141 181 }
142 182
143 183 @Override
... ... @@ -164,17 +204,48 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
164 204
165 205 @Override
166 206 public ListenableFuture<Response> send(Request request) {
167   - if (tickSize > maxPendingRequests) {
  207 + return send(request, this.maxRequestTimeoutNs);
  208 + }
  209 +
  210 + @Override
  211 + public ListenableFuture<Response> send(Request request, long requestTimeoutNs) {
  212 + if (pendingRequests.mappingCount() >= maxPendingRequests) {
  213 + log.warn("Pending request map is full [{}]! Consider to increase maxPendingRequests or increase processing performance", maxPendingRequests);
168 214 return Futures.immediateFailedFuture(new RuntimeException("Pending request map is full!"));
169 215 }
170 216 UUID requestId = UUID.randomUUID();
171 217 request.getHeaders().put(REQUEST_ID_HEADER, uuidToBytes(requestId));
172 218 request.getHeaders().put(RESPONSE_TOPIC_HEADER, stringToBytes(responseTemplate.getTopic()));
173   - request.getHeaders().put(REQUEST_TIME, longToBytes(System.currentTimeMillis()));
  219 + request.getHeaders().put(REQUEST_TIME, longToBytes(getCurrentTimeMs()));
  220 + long currentClockNs = getCurrentClockNs();
174 221 SettableFuture<Response> future = SettableFuture.create();
175   - ResponseMetaData<Response> responseMetaData = new ResponseMetaData<>(tickTs + maxRequestTimeout, future);
176   - pendingRequests.putIfAbsent(requestId, responseMetaData);
177   - log.trace("[{}] Sending request, key [{}], expTime [{}]", requestId, request.getKey(), responseMetaData.expTime);
  222 + ResponseMetaData<Response> responseMetaData = new ResponseMetaData<>(currentClockNs + requestTimeoutNs, future, currentClockNs, requestTimeoutNs);
  223 + log.trace("pending {}", responseMetaData);
  224 + if (pendingRequests.putIfAbsent(requestId, responseMetaData) != null) {
  225 + log.warn("Pending request already exists [{}]!", maxPendingRequests);
  226 + return Futures.immediateFailedFuture(new RuntimeException("Pending request already exists !" + requestId));
  227 + }
  228 + sendToRequestTemplate(request, requestId, future, responseMetaData);
  229 + return future;
  230 + }
  231 +
  232 + /**
  233 + * MONOTONIC clock instead jumping wall clock.
  234 + * Wrapped into the method for the test purposes to travel through the time
  235 + * */
  236 + long getCurrentClockNs() {
  237 + return System.nanoTime();
  238 + }
  239 +
  240 + /**
  241 + * Wall clock to send timestamp to an external service
  242 + * */
  243 + long getCurrentTimeMs() {
  244 + return System.currentTimeMillis();
  245 + }
  246 +
  247 + void sendToRequestTemplate(Request request, UUID requestId, SettableFuture<Response> future, ResponseMetaData<Response> responseMetaData) {
  248 + log.trace("[{}] Sending request, key [{}], expTime [{}], request {}", requestId, request.getKey(), responseMetaData.expTime, request);
178 249 if (messagesStats != null) {
179 250 messagesStats.incrementTotal();
180 251 }
... ... @@ -184,7 +255,7 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
184 255 if (messagesStats != null) {
185 256 messagesStats.incrementSuccessful();
186 257 }
187   - log.trace("[{}] Request sent: {}", requestId, metadata);
  258 + log.trace("[{}] Request sent: {}, request {}", requestId, metadata, request);
188 259 }
189 260
190 261 @Override
... ... @@ -196,17 +267,32 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
196 267 future.setException(t);
197 268 }
198 269 });
199   - return future;
200 270 }
201 271
202   - private static class ResponseMetaData<T> {
  272 + @Getter
  273 + static class ResponseMetaData<T> {
  274 + private final long submitTime;
  275 + private final long timeout;
203 276 private final long expTime;
204 277 private final SettableFuture<T> future;
205 278
206   - ResponseMetaData(long ts, SettableFuture<T> future) {
  279 + ResponseMetaData(long ts, SettableFuture<T> future, long submitTime, long timeout) {
  280 + this.submitTime = submitTime;
  281 + this.timeout = timeout;
207 282 this.expTime = ts;
208 283 this.future = future;
209 284 }
  285 +
  286 + @Override
  287 + public String toString() {
  288 + return "ResponseMetaData{" +
  289 + "submitTime=" + submitTime +
  290 + ", calculatedExpTime=" + (submitTime + timeout) +
  291 + ", deltaMs=" + (expTime - submitTime) +
  292 + ", expTime=" + expTime +
  293 + ", future=" + future +
  294 + '}';
  295 + }
210 296 }
211 297
212 298 }
... ...
... ... @@ -21,6 +21,7 @@ import org.apache.kafka.clients.consumer.ConsumerConfig;
21 21 import org.apache.kafka.clients.consumer.ConsumerRecord;
22 22 import org.apache.kafka.clients.consumer.ConsumerRecords;
23 23 import org.apache.kafka.clients.consumer.KafkaConsumer;
  24 +import org.springframework.util.StopWatch;
24 25 import org.thingsboard.server.queue.TbQueueAdmin;
25 26 import org.thingsboard.server.queue.TbQueueMsg;
26 27 import org.thingsboard.server.queue.common.AbstractTbQueueConsumerTemplate;
... ... @@ -82,7 +83,16 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> extends AbstractTbQue
82 83
83 84 @Override
84 85 protected List<ConsumerRecord<String, byte[]>> doPoll(long durationInMillis) {
  86 + StopWatch stopWatch = new StopWatch();
  87 + stopWatch.start();
  88 +
  89 + log.trace("poll topic {} maxDuration {}", getTopic(), durationInMillis);
  90 +
85 91 ConsumerRecords<String, byte[]> records = consumer.poll(Duration.ofMillis(durationInMillis));
  92 +
  93 + stopWatch.stop();
  94 + log.trace("poll topic {} took {}ms", getTopic(), stopWatch.getTotalTimeMillis());
  95 +
86 96 if (records.isEmpty()) {
87 97 return Collections.emptyList();
88 98 } else {
... ... @@ -99,7 +109,7 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> extends AbstractTbQue
99 109
100 110 @Override
101 111 protected void doCommit() {
102   - consumer.commitAsync();
  112 + consumer.commitSync();
103 113 }
104 114
105 115 @Override
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.queue.common;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
  20 +import org.junit.Before;
  21 +import org.junit.Test;
  22 +import org.junit.runner.RunWith;
  23 +import org.mockito.ArgumentCaptor;
  24 +import org.mockito.Mock;
  25 +import org.mockito.junit.MockitoJUnitRunner;
  26 +import org.thingsboard.server.queue.TbQueueAdmin;
  27 +import org.thingsboard.server.queue.TbQueueConsumer;
  28 +import org.thingsboard.server.queue.TbQueueMsg;
  29 +import org.thingsboard.server.queue.TbQueueProducer;
  30 +
  31 +import java.util.Collections;
  32 +import java.util.List;
  33 +import java.util.UUID;
  34 +import java.util.concurrent.CountDownLatch;
  35 +import java.util.concurrent.ExecutorService;
  36 +import java.util.concurrent.TimeUnit;
  37 +import java.util.concurrent.atomic.AtomicLong;
  38 +
  39 +import static org.hamcrest.Matchers.equalTo;
  40 +import static org.hamcrest.Matchers.greaterThanOrEqualTo;
  41 +import static org.hamcrest.Matchers.is;
  42 +import static org.hamcrest.Matchers.lessThan;
  43 +import static org.mockito.ArgumentMatchers.any;
  44 +import static org.mockito.ArgumentMatchers.anyLong;
  45 +import static org.mockito.BDDMockito.willAnswer;
  46 +import static org.mockito.BDDMockito.willDoNothing;
  47 +import static org.mockito.BDDMockito.willReturn;
  48 +import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
  49 +import static org.mockito.Mockito.atLeastOnce;
  50 +import static org.mockito.Mockito.mock;
  51 +import static org.mockito.Mockito.never;
  52 +import static org.mockito.Mockito.spy;
  53 +import static org.mockito.Mockito.times;
  54 +import static org.mockito.Mockito.verify;
  55 +
  56 +import static org.hamcrest.MatcherAssert.assertThat;
  57 +import static org.mockito.hamcrest.MockitoHamcrest.longThat;
  58 +
  59 +@Slf4j
  60 +@RunWith(MockitoJUnitRunner.class)
  61 +public class DefaultTbQueueRequestTemplateTest {
  62 +
  63 + @Mock
  64 + TbQueueAdmin queueAdmin;
  65 + @Mock
  66 + TbQueueProducer<TbQueueMsg> requestTemplate;
  67 + @Mock
  68 + TbQueueConsumer<TbQueueMsg> responseTemplate;
  69 + @Mock
  70 + ExecutorService executorMock;
  71 +
  72 + ExecutorService executor;
  73 + String topic = "js-responses-tb-node-0";
  74 + long maxRequestTimeout = 10;
  75 + long maxPendingRequests = 32;
  76 + long pollInterval = 5;
  77 +
  78 + DefaultTbQueueRequestTemplate inst;
  79 +
  80 + @Before
  81 + public void setUp() throws Exception {
  82 + willReturn(topic).given(responseTemplate).getTopic();
  83 + inst = spy(new DefaultTbQueueRequestTemplate(
  84 + queueAdmin, requestTemplate, responseTemplate,
  85 + maxRequestTimeout, maxPendingRequests, pollInterval, executorMock));
  86 +
  87 + }
  88 +
  89 + @After
  90 + public void tearDown() throws Exception {
  91 + if (executor != null) {
  92 + executor.shutdownNow();
  93 + }
  94 + }
  95 +
  96 + @Test
  97 + public void givenInstance_whenVerifyInitialParameters_thenOK() {
  98 + assertThat(inst.maxPendingRequests, equalTo(maxPendingRequests));
  99 + assertThat(inst.maxRequestTimeoutNs, equalTo(TimeUnit.MILLISECONDS.toNanos(maxRequestTimeout)));
  100 + assertThat(inst.pollInterval, equalTo(pollInterval));
  101 + assertThat(inst.executor, is(executorMock));
  102 + assertThat(inst.stopped, is(false));
  103 + assertThat(inst.internalExecutor, is(false));
  104 + }
  105 +
  106 + @Test
  107 + public void givenExternalExecutor_whenInitStop_thenOK() {
  108 + inst.init();
  109 + assertThat(inst.nextCleanupNs, equalTo(0L));
  110 + verify(queueAdmin, times(1)).createTopicIfNotExists(topic);
  111 + verify(requestTemplate, times(1)).init();
  112 + verify(responseTemplate, times(1)).subscribe();
  113 + verify(executorMock, times(1)).submit(any(Runnable.class));
  114 +
  115 + inst.stop();
  116 + assertThat(inst.stopped, is(true));
  117 + verify(responseTemplate, times(1)).unsubscribe();
  118 + verify(requestTemplate, times(1)).stop();
  119 + verify(executorMock, never()).shutdownNow();
  120 + }
  121 +
  122 + @Test
  123 + public void givenMainLoop_whenLoopFewTimes_thenVerifyInvocationCount() throws InterruptedException {
  124 + executor = inst.createExecutor();
  125 + CountDownLatch latch = new CountDownLatch(5);
  126 + willDoNothing().given(inst).sleep(anyLong());
  127 + willAnswer(invocation -> {
  128 + if (latch.getCount() == 1) {
  129 + inst.stop(); //stop the loop in natural way
  130 + }
  131 + if (latch.getCount() == 3 || latch.getCount() == 4) {
  132 + latch.countDown();
  133 + throw new RuntimeException("test catch block");
  134 + }
  135 + latch.countDown();
  136 + return null;
  137 + }).given(inst).fetchAndProcessResponses();
  138 +
  139 + executor.submit(inst::mainLoop);
  140 + latch.await(10, TimeUnit.SECONDS);
  141 +
  142 + verify(inst, times(5)).fetchAndProcessResponses();
  143 + verify(inst, times(2)).sleep(longThat(lessThan(TimeUnit.MILLISECONDS.toNanos(inst.pollInterval))));
  144 + }
  145 +
  146 + @Test
  147 + public void givenMessages_whenSend_thenOK() {
  148 + willDoNothing().given(inst).sendToRequestTemplate(any(), any(), any(), any());
  149 + inst.init();
  150 + final int msgCount = 10;
  151 + for (int i = 0; i < msgCount; i++) {
  152 + inst.send(getRequestMsgMock());
  153 + }
  154 + assertThat(inst.pendingRequests.mappingCount(), equalTo((long) msgCount));
  155 + verify(inst, times(msgCount)).sendToRequestTemplate(any(), any(), any(), any());
  156 + }
  157 +
  158 + @Test
  159 + public void givenMessagesOverMaxPendingRequests_whenSend_thenImmediateFailedFutureForTheOfRequests() {
  160 + willDoNothing().given(inst).sendToRequestTemplate(any(), any(), any(), any());
  161 + inst.init();
  162 + int msgOverflowCount = 10;
  163 + for (int i = 0; i < inst.maxPendingRequests; i++) {
  164 + assertThat(inst.send(getRequestMsgMock()).isDone(), is(false)); //SettableFuture future - pending only
  165 + }
  166 + for (int i = 0; i < msgOverflowCount; i++) {
  167 + assertThat("max pending requests overflow", inst.send(getRequestMsgMock()).isDone(), is(true)); //overflow, immediate failed future
  168 + }
  169 + assertThat(inst.pendingRequests.mappingCount(), equalTo(inst.maxPendingRequests));
  170 + verify(inst, times((int) inst.maxPendingRequests)).sendToRequestTemplate(any(), any(), any(), any());
  171 + }
  172 +
  173 + @Test
  174 + public void givenNothing_whenSendAndFetchAndProcessResponsesWithTimeout_thenFail() {
  175 + //given
  176 + AtomicLong currentTime = new AtomicLong();
  177 + willAnswer(x -> {
  178 + log.info("currentTime={}", currentTime.get());
  179 + return currentTime.get();
  180 + }).given(inst).getCurrentClockNs();
  181 + inst.init();
  182 + inst.setupNextCleanup();
  183 + willReturn(Collections.emptyList()).given(inst).doPoll();
  184 +
  185 + //when
  186 + long stepNs = TimeUnit.MILLISECONDS.toNanos(1);
  187 + for (long i = 0; i <= inst.maxRequestTimeoutNs * 2; i = i + stepNs) {
  188 + currentTime.addAndGet(stepNs);
  189 + assertThat(inst.send(getRequestMsgMock()).isDone(), is(false)); //SettableFuture future - pending only
  190 + if (i % (inst.maxRequestTimeoutNs * 3 / 2) == 0) {
  191 + inst.fetchAndProcessResponses();
  192 + }
  193 + }
  194 +
  195 + //then
  196 + ArgumentCaptor<DefaultTbQueueRequestTemplate.ResponseMetaData> argumentCaptorResp = ArgumentCaptor.forClass(DefaultTbQueueRequestTemplate.ResponseMetaData.class);
  197 + ArgumentCaptor<UUID> argumentCaptorUUID = ArgumentCaptor.forClass(UUID.class);
  198 + ArgumentCaptor<Long> argumentCaptorLong = ArgumentCaptor.forClass(Long.class);
  199 + verify(inst, atLeastOnce()).setTimeoutException(argumentCaptorUUID.capture(), argumentCaptorResp.capture(), argumentCaptorLong.capture());
  200 +
  201 + List<DefaultTbQueueRequestTemplate.ResponseMetaData> responseMetaDataList = argumentCaptorResp.getAllValues();
  202 + List<Long> tickTsList = argumentCaptorLong.getAllValues();
  203 + for (int i = 0; i < responseMetaDataList.size(); i++) {
  204 + assertThat("tickTs >= calculatedExpTime", tickTsList.get(i), greaterThanOrEqualTo(responseMetaDataList.get(i).getSubmitTime() + responseMetaDataList.get(i).getTimeout()));
  205 + }
  206 + }
  207 +
  208 + TbQueueMsg getRequestMsgMock() {
  209 + return mock(TbQueueMsg.class, RETURNS_DEEP_STUBS);
  210 + }
  211 +}
\ No newline at end of file
... ...
... ... @@ -44,7 +44,6 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
44 44 import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
45 45 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
46 46 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
47   -import org.thingsboard.server.common.data.ota.OtaPackageType;
48 47 import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
49 48 import org.thingsboard.server.common.msg.session.FeatureType;
50 49 import org.thingsboard.server.common.msg.session.SessionMsgType;
... ... @@ -139,10 +138,6 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
139 138 processExchangeGetRequest(exchange, featureType.get());
140 139 } else if (featureType.get() == FeatureType.ATTRIBUTES) {
141 140 processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST);
142   - } else if (featureType.get() == FeatureType.FIRMWARE) {
143   - processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST);
144   - } else if (featureType.get() == FeatureType.SOFTWARE) {
145   - processRequest(exchange, SessionMsgType.GET_SOFTWARE_REQUEST);
146 141 } else {
147 142 log.trace("Invalid feature type parameter");
148 143 exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
... ... @@ -349,12 +344,6 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
349 344 coapTransportAdaptor.convertToGetAttributes(sessionId, request),
350 345 new CoapNoOpCallback(exchange));
351 346 break;
352   - case GET_FIRMWARE_REQUEST:
353   - getOtaPackageCallback(sessionInfo, exchange, OtaPackageType.FIRMWARE);
354   - break;
355   - case GET_SOFTWARE_REQUEST:
356   - getOtaPackageCallback(sessionInfo, exchange, OtaPackageType.SOFTWARE);
357   - break;
358 347 }
359 348 } catch (AdaptorException e) {
360 349 log.trace("[{}] Failed to decode message: ", sessionId, e);
... ... @@ -366,16 +355,6 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
366 355 return new UUID(sessionInfoProto.getSessionIdMSB(), sessionInfoProto.getSessionIdLSB());
367 356 }
368 357
369   - private void getOtaPackageCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, OtaPackageType firmwareType) {
370   - TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder()
371   - .setTenantIdMSB(sessionInfo.getTenantIdMSB())
372   - .setTenantIdLSB(sessionInfo.getTenantIdLSB())
373   - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
374   - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
375   - .setType(firmwareType.name()).build();
376   - transportContext.getTransportService().process(sessionInfo, requestMsg, new OtaPackageCallback(exchange));
377   - }
378   -
379 358 private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) {
380 359 tokenToObserveNotificationSeqMap.remove(token);
381 360 return tokenToSessionInfoMap.remove(token);
... ... @@ -470,57 +449,6 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
470 449 }
471 450 }
472 451
473   - private class OtaPackageCallback implements TransportServiceCallback<TransportProtos.GetOtaPackageResponseMsg> {
474   - private final CoapExchange exchange;
475   -
476   - OtaPackageCallback(CoapExchange exchange) {
477   - this.exchange = exchange;
478   - }
479   -
480   - @Override
481   - public void onSuccess(TransportProtos.GetOtaPackageResponseMsg msg) {
482   - String title = exchange.getQueryParameter("title");
483   - String version = exchange.getQueryParameter("version");
484   - if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) {
485   - String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString();
486   - if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) {
487   - String strChunkSize = exchange.getQueryParameter("size");
488   - String strChunk = exchange.getQueryParameter("chunk");
489   - int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize);
490   - int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk);
491   - exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk));
492   - }
493   - else if (firmwareId != null) {
494   - sendOtaData(exchange, firmwareId);
495   - } else {
496   - exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
497   - }
498   - } else {
499   - exchange.respond(CoAP.ResponseCode.NOT_FOUND);
500   - }
501   - }
502   -
503   - @Override
504   - public void onError(Throwable e) {
505   - log.warn("Failed to process request", e);
506   - exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
507   - }
508   - }
509   -
510   - private void sendOtaData(CoapExchange exchange, String firmwareId) {
511   - Response response = new Response(CoAP.ResponseCode.CONTENT);
512   - byte[] fwData = transportContext.getOtaPackageDataCache().get(firmwareId);
513   - if (fwData != null && fwData.length > 0) {
514   - response.setPayload(fwData);
515   - if (exchange.getRequestOptions().getBlock2() != null) {
516   - int chunkSize = exchange.getRequestOptions().getBlock2().getSzx();
517   - boolean moreFlag = fwData.length > chunkSize;
518   - response.getOptions().setBlock2(chunkSize, moreFlag, 0);
519   - }
520   - exchange.respond(response);
521   - }
522   - }
523   -
524 452 private static class CoapSessionListener implements SessionMsgListener {
525 453
526 454 private final CoapTransportResource coapTransportResource;
... ...
... ... @@ -18,17 +18,21 @@ package org.thingsboard.server.transport.coap;
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.eclipse.californium.core.CoapResource;
20 20 import org.eclipse.californium.core.CoapServer;
  21 +import org.eclipse.californium.core.network.config.NetworkConfig;
21 22 import org.springframework.beans.factory.annotation.Autowired;
22 23 import org.springframework.stereotype.Service;
23   -import org.thingsboard.server.common.data.TbTransportService;
24 24 import org.thingsboard.server.coapserver.CoapServerService;
25 25 import org.thingsboard.server.coapserver.TbCoapServerComponent;
  26 +import org.thingsboard.server.common.data.TbTransportService;
  27 +import org.thingsboard.server.common.data.ota.OtaPackageType;
26 28 import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource;
27 29
28 30 import javax.annotation.PostConstruct;
29 31 import javax.annotation.PreDestroy;
30 32 import java.net.UnknownHostException;
31 33
  34 +import static org.eclipse.californium.core.network.config.NetworkConfigDefaults.DEFAULT_BLOCKWISE_STATUS_LIFETIME;
  35 +
32 36 @Service("CoapTransportService")
33 37 @TbCoapServerComponent
34 38 @Slf4j
... ... @@ -51,6 +55,14 @@ public class CoapTransportService implements TbTransportService {
51 55 public void init() throws UnknownHostException {
52 56 log.info("Starting CoAP transport...");
53 57 coapServer = coapServerService.getCoapServer();
  58 + coapServer.getConfig().setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
  59 + coapServer.getConfig().setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
  60 + coapServer.getConfig().setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME);
  61 + coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024);
  62 + coapServer.getConfig().setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED");
  63 + coapServer.getConfig().setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024);
  64 + coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024);
  65 + coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 10);
54 66 CoapResource api = new CoapResource(API);
55 67 api.add(new CoapTransportResource(coapTransportContext, coapServerService, V1));
56 68
... ... @@ -59,6 +71,8 @@ public class CoapTransportService implements TbTransportService {
59 71 efento.add(efentoMeasurementsTransportResource);
60 72 coapServer.add(api);
61 73 coapServer.add(efento);
  74 + coapServer.add(new OtaPackageTransportResource(coapTransportContext, OtaPackageType.FIRMWARE));
  75 + coapServer.add(new OtaPackageTransportResource(coapTransportContext, OtaPackageType.SOFTWARE));
62 76 log.info("CoAP transport started!");
63 77 }
64 78
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.coap;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.californium.core.coap.CoAP;
  20 +import org.eclipse.californium.core.coap.Request;
  21 +import org.eclipse.californium.core.coap.Response;
  22 +import org.eclipse.californium.core.network.Exchange;
  23 +import org.eclipse.californium.core.observe.ObserveRelation;
  24 +import org.eclipse.californium.core.server.resources.CoapExchange;
  25 +import org.eclipse.californium.core.server.resources.Resource;
  26 +import org.eclipse.californium.core.server.resources.ResourceObserver;
  27 +import org.thingsboard.common.util.ThingsBoardExecutors;
  28 +import org.thingsboard.server.common.data.DeviceTransportType;
  29 +import org.thingsboard.server.common.data.StringUtils;
  30 +import org.thingsboard.server.common.data.ota.OtaPackageType;
  31 +import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
  32 +import org.thingsboard.server.common.transport.TransportServiceCallback;
  33 +import org.thingsboard.server.gen.transport.TransportProtos;
  34 +
  35 +import java.util.List;
  36 +import java.util.Optional;
  37 +import java.util.UUID;
  38 +import java.util.concurrent.ExecutorService;
  39 +
  40 +@Slf4j
  41 +public class OtaPackageTransportResource extends AbstractCoapTransportResource {
  42 + private static final int ACCESS_TOKEN_POSITION = 2;
  43 +
  44 + private final OtaPackageType otaPackageType;
  45 +
  46 + public OtaPackageTransportResource(CoapTransportContext ctx, OtaPackageType otaPackageType) {
  47 + super(ctx, otaPackageType.getKeyPrefix());
  48 + this.otaPackageType = otaPackageType;
  49 +
  50 + this.setObservable(true);
  51 + }
  52 +
  53 + @Override
  54 + protected void processHandleGet(CoapExchange exchange) {
  55 + log.trace("Processing {}", exchange.advanced().getRequest());
  56 + exchange.accept();
  57 + Exchange advanced = exchange.advanced();
  58 + Request request = advanced.getRequest();
  59 + processAccessTokenRequest(exchange, request);
  60 + }
  61 +
  62 + @Override
  63 + protected void processHandlePost(CoapExchange exchange) {
  64 + exchange.respond(CoAP.ResponseCode.METHOD_NOT_ALLOWED);
  65 + }
  66 +
  67 + private void processAccessTokenRequest(CoapExchange exchange, Request request) {
  68 + Optional<DeviceTokenCredentials> credentials = decodeCredentials(request);
  69 + if (credentials.isEmpty()) {
  70 + exchange.respond(CoAP.ResponseCode.UNAUTHORIZED);
  71 + return;
  72 + }
  73 + transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(),
  74 + new CoapDeviceAuthCallback(transportContext, exchange, (sessionInfo, deviceProfile) -> {
  75 + getOtaPackageCallback(sessionInfo, exchange, otaPackageType);
  76 + }));
  77 + }
  78 +
  79 + private void getOtaPackageCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, OtaPackageType firmwareType) {
  80 + TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder()
  81 + .setTenantIdMSB(sessionInfo.getTenantIdMSB())
  82 + .setTenantIdLSB(sessionInfo.getTenantIdLSB())
  83 + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
  84 + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
  85 + .setType(firmwareType.name()).build();
  86 + transportContext.getTransportService().process(sessionInfo, requestMsg, new OtaPackageCallback(exchange));
  87 + }
  88 +
  89 + private Optional<DeviceTokenCredentials> decodeCredentials(Request request) {
  90 + List<String> uriPath = request.getOptions().getUriPath();
  91 + if (uriPath.size() == ACCESS_TOKEN_POSITION) {
  92 + return Optional.of(new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1)));
  93 + } else {
  94 + return Optional.empty();
  95 + }
  96 + }
  97 +
  98 + @Override
  99 + public Resource getChild(String name) {
  100 + return this;
  101 + }
  102 +
  103 + private class OtaPackageCallback implements TransportServiceCallback<TransportProtos.GetOtaPackageResponseMsg> {
  104 + private final CoapExchange exchange;
  105 +
  106 + OtaPackageCallback(CoapExchange exchange) {
  107 + this.exchange = exchange;
  108 + }
  109 +
  110 + @Override
  111 + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg msg) {
  112 + String title = exchange.getQueryParameter("title");
  113 + String version = exchange.getQueryParameter("version");
  114 + if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) {
  115 + String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString();
  116 + if ((title == null || msg.getTitle().equals(title)) && (version == null || msg.getVersion().equals(version))) {
  117 + String strChunkSize = exchange.getQueryParameter("size");
  118 + String strChunk = exchange.getQueryParameter("chunk");
  119 + int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize);
  120 + int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk);
  121 + respondOtaPackage(exchange, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk));
  122 + } else {
  123 + exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
  124 + }
  125 + } else {
  126 + exchange.respond(CoAP.ResponseCode.NOT_FOUND);
  127 + }
  128 + }
  129 +
  130 + @Override
  131 + public void onError(Throwable e) {
  132 + log.warn("Failed to process request", e);
  133 + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
  134 + }
  135 + }
  136 +
  137 + private void respondOtaPackage(CoapExchange exchange, byte[] data) {
  138 + Response response = new Response(CoAP.ResponseCode.CONTENT);
  139 + if (data != null && data.length > 0) {
  140 + response.setPayload(data);
  141 + if (exchange.getRequestOptions().getBlock2() != null) {
  142 + int chunkSize = exchange.getRequestOptions().getBlock2().getSzx();
  143 + boolean lastFlag = data.length <= chunkSize;
  144 + response.getOptions().setBlock2(chunkSize, lastFlag, 0);
  145 + }
  146 + transportContext.getExecutor().submit(() -> exchange.respond(response));
  147 + }
  148 + }
  149 +
  150 +}
... ...
... ... @@ -29,12 +29,11 @@ import org.springframework.stereotype.Service;
29 29 import org.thingsboard.common.util.JacksonUtil;
30 30 import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration;
31 31 import org.thingsboard.server.gen.transport.TransportProtos;
32   -import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
33 32 import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
  33 +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
34 34 import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener;
35 35 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
36 36 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
37   -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
38 37
39 38 import java.io.IOException;
40 39 import java.security.GeneralSecurityException;
... ... @@ -46,6 +45,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L
46 45 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO;
47 46 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY;
48 47 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getBootstrapParametersFromThingsboard;
  48 +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP;
49 49
50 50 @Slf4j
51 51 @Service("LwM2MBootstrapSecurityStore")
... ... @@ -68,7 +68,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
68 68
69 69 @Override
70 70 public Iterator<SecurityInfo> getAllByEndpoint(String endPoint) {
71   - TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
  71 + TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP);
72 72 if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
73 73 /* add value to store from BootstrapJson */
74 74 this.setBootstrapConfigScurityInfo(store);
... ... @@ -92,7 +92,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
92 92
93 93 @Override
94 94 public SecurityInfo getByIdentity(String identity) {
95   - TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
  95 + TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, BOOTSTRAP);
96 96 if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
97 97 /* add value to store from BootstrapJson */
98 98 this.setBootstrapConfigScurityInfo(store);
... ...
... ... @@ -57,30 +57,22 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig {
57 57 private boolean recommendedSupportedGroups;
58 58
59 59 @Getter
60   - @Value("${transport.lwm2m.response_pool_size:}")
61   - private int responsePoolSize;
  60 + @Value("${transport.lwm2m.downlink_pool_size:}")
  61 + private int downlinkPoolSize;
62 62
63 63 @Getter
64   - @Value("${transport.lwm2m.registered_pool_size:}")
65   - private int registeredPoolSize;
  64 + @Value("${transport.lwm2m.uplink_pool_size:}")
  65 + private int uplinkPoolSize;
66 66
67 67 @Getter
68   - @Value("${transport.lwm2m.registration_store_pool_size:}")
69   - private int registrationStorePoolSize;
  68 + @Value("${transport.lwm2m.ota_pool_size:}")
  69 + private int otaPoolSize;
70 70
71 71 @Getter
72 72 @Value("${transport.lwm2m.clean_period_in_sec:}")
73 73 private int cleanPeriodInSec;
74 74
75 75 @Getter
76   - @Value("${transport.lwm2m.update_registered_pool_size:}")
77   - private int updateRegisteredPoolSize;
78   -
79   - @Getter
80   - @Value("${transport.lwm2m.un_registered_pool_size:}")
81   - private int unRegisteredPoolSize;
82   -
83   - @Getter
84 76 @Value("${transport.lwm2m.security.key_store_type:}")
85 77 private String keyStoreType;
86 78
... ... @@ -150,7 +142,7 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig {
150 142 URI uri = null;
151 143 try {
152 144 uri = Resources.getResource(keyStorePathFile).toURI();
153   - log.error("URI: {}", uri);
  145 + log.info("URI: {}", uri);
154 146 File keyStoreFile = new File(uri);
155 147 InputStream inKeyStore = new FileInputStream(keyStoreFile);
156 148 keyStoreValue = KeyStore.getInstance(keyStoreType);
... ...
... ... @@ -33,7 +33,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
33 33 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
34 34 import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
35 35 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
36   -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
  36 +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer;
37 37
38 38 import java.io.IOException;
39 39 import java.security.GeneralSecurityException;
... ... @@ -45,6 +45,7 @@ import static org.eclipse.leshan.core.SecurityMode.NO_SEC;
45 45 import static org.eclipse.leshan.core.SecurityMode.PSK;
46 46 import static org.eclipse.leshan.core.SecurityMode.RPK;
47 47 import static org.eclipse.leshan.core.SecurityMode.X509;
  48 +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP;
48 49
49 50 @Slf4j
50 51 @Component
... ... @@ -55,13 +56,15 @@ public class LwM2mCredentialsSecurityInfoValidator {
55 56 private final LwM2mTransportContext context;
56 57 private final LwM2MTransportServerConfig config;
57 58
58   - public TbLwM2MSecurityInfo getEndpointSecurityInfoByCredentialsId(String credentialsId, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
  59 + public TbLwM2MSecurityInfo getEndpointSecurityInfoByCredentialsId(String credentialsId, LwM2mTypeServer keyValue) {
59 60 CountDownLatch latch = new CountDownLatch(1);
60 61 final TbLwM2MSecurityInfo[] resultSecurityStore = new TbLwM2MSecurityInfo[1];
  62 + log.trace("Validating credentials [{}]", credentialsId);
61 63 context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(credentialsId).build(),
62 64 new TransportServiceCallback<>() {
63 65 @Override
64 66 public void onSuccess(ValidateDeviceCredentialsResponse msg) {
  67 + log.trace("Validated credentials: [{}] [{}]", credentialsId, msg);
65 68 String credentialsBody = msg.getCredentials();
66 69 resultSecurityStore[0] = createSecurityInfo(credentialsId, credentialsBody, keyValue);
67 70 resultSecurityStore[0].setMsg(msg);
... ... @@ -91,11 +94,11 @@ public class LwM2mCredentialsSecurityInfoValidator {
91 94 * @param keyValue -
92 95 * @return SecurityInfo
93 96 */
94   - private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
  97 + private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTypeServer keyValue) {
95 98 TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo();
96 99 LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class);
97 100 if (credentials != null) {
98   - if (keyValue.equals(LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP)) {
  101 + if (keyValue.equals(BOOTSTRAP)) {
99 102 result.setBootstrapCredentialConfig(credentials.getBootstrap());
100 103 if (LwM2MSecurityMode.PSK.equals(credentials.getClient().getSecurityConfigClientMode())) {
101 104 PSKClientCredentials pskClientConfig = (PSKClientCredentials) credentials.getClient();
... ...
... ... @@ -42,7 +42,6 @@ import org.thingsboard.server.common.transport.util.SslUtil;
42 42 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
43 43 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
44 44 import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
45   -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
46 45 import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore;
47 46 import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
48 47
... ... @@ -57,6 +56,8 @@ import java.security.cert.X509Certificate;
57 56 import java.util.Arrays;
58 57 import java.util.List;
59 58
  59 +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT;
  60 +
60 61 @Slf4j
61 62 @Component
62 63 @TbLwM2mTransportComponent
... ... @@ -117,7 +118,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
117 118
118 119 String strCert = SslUtil.getCertificateString(cert);
119 120 String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
120   - TbLwM2MSecurityInfo securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
  121 + TbLwM2MSecurityInfo securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, CLIENT);
121 122 ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null;
122 123 if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
123 124 LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class);
... ...
... ... @@ -27,6 +27,7 @@ import org.eclipse.leshan.server.californium.LeshanServerBuilder;
27 27 import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
28 28 import org.eclipse.leshan.server.model.LwM2mModelProvider;
29 29 import org.springframework.stereotype.Component;
  30 +import org.thingsboard.server.cache.ota.OtaPackageDataCache;
30 31 import org.thingsboard.server.common.data.StringUtils;
31 32 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
32 33 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
... ... @@ -64,7 +65,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
64 65 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
65 66 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
66 67 import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
67   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RESOURCE;
  68 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RESOURCE;
68 69
69 70 @Slf4j
70 71 @Component
... ... @@ -80,6 +81,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
80 81 private final LwM2mTransportContext context;
81 82 private final LwM2MTransportServerConfig config;
82 83 private final LwM2mTransportServerHelper helper;
  84 + private final OtaPackageDataCache otaPackageDataCache;
83 85 private final DefaultLwM2MUplinkMsgHandler handler;
84 86 private final CaliforniumRegistrationStore registrationStore;
85 87 private final TbSecurityStore securityStore;
... ... @@ -103,8 +105,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
103 105 * "coap://host:port/{path}/{token}/{nameFile}"
104 106 */
105 107
106   -
107   - LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(handler, FIRMWARE_UPDATE_COAP_RESOURCE);
  108 + LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE);
108 109 this.server.coap().getServer().add(otaCoapResource);
109 110 this.startLhServer();
110 111 this.context.setServer(server);
... ...
... ... @@ -84,10 +84,10 @@ public class LwM2mNetworkConfig {
84 84 Create new instance of udp endpoint context matcher.
85 85 Params:
86 86 checkAddress
87   - – true with address check, (STRICT, UDP)
  87 + – true with address check, (STRICT, UDP) - if port Registration of client is changed - it is bad
88 88 - false, without
89 89 */
90   - coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT");
  90 + coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED");
91 91 /**
92 92 https://tools.ietf.org/html/rfc7959#section-2.9.3
93 93 The block size (number of bytes) to use when doing a blockwise transfer. \
... ... @@ -103,7 +103,7 @@ public class LwM2mNetworkConfig {
103 103 */
104 104 coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024);
105 105
106   - coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4);
  106 + coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 10);
107 107
108 108 return coapConfig;
109 109 }
... ...
... ... @@ -27,8 +27,7 @@ import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandle
27 27
28 28 import java.util.Collection;
29 29
30   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO;
31   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
  30 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId;
32 31
33 32 @Slf4j
34 33 public class LwM2mServerListener {
... ... @@ -93,7 +92,7 @@ public class LwM2mServerListener {
93 92 @Override
94 93 public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
95 94 if (registration != null) {
96   - service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), response);
  95 + service.onUpdateValueAfterReadResponse(registration, convertObjectIdToVersionedId(observation.getPath().toString(), registration), response);
97 96 }
98 97 }
99 98
... ...
... ... @@ -33,7 +33,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseM
33 33 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
34 34 import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService;
35 35 import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler;
36   -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;
37 36 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
38 37
39 38 import java.util.Optional;
... ... @@ -54,7 +53,7 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s
54 53
55 54 @Override
56 55 public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
57   - this.attributesService.onAttributeUpdate(attributeUpdateNotification, this.sessionInfo);
  56 + this.attributesService.onAttributesUpdate(attributeUpdateNotification, this.sessionInfo);
58 57 }
59 58
60 59 @Override
... ...
... ... @@ -24,26 +24,25 @@ import org.eclipse.californium.core.observe.ObserveRelation;
24 24 import org.eclipse.californium.core.server.resources.CoapExchange;
25 25 import org.eclipse.californium.core.server.resources.Resource;
26 26 import org.eclipse.californium.core.server.resources.ResourceObserver;
27   -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;
28   -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
  27 +import org.thingsboard.server.cache.ota.OtaPackageDataCache;
29 28
30 29 import java.util.UUID;
31 30 import java.util.concurrent.ConcurrentHashMap;
32 31 import java.util.concurrent.ConcurrentMap;
33 32 import java.util.concurrent.atomic.AtomicInteger;
34 33
35   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RESOURCE;
36   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SOFTWARE_UPDATE_COAP_RESOURCE;
  34 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RESOURCE;
  35 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SOFTWARE_UPDATE_COAP_RESOURCE;
37 36
38 37 @Slf4j
39 38 public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
40 39 private final ConcurrentMap<String, ObserveRelation> tokenToObserveRelationMap = new ConcurrentHashMap<>();
41 40 private final ConcurrentMap<String, AtomicInteger> tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>();
42   - private final LwM2mUplinkMsgHandler handler;
  41 + private final OtaPackageDataCache otaPackageDataCache;
43 42
44   - public LwM2mTransportCoapResource(LwM2mUplinkMsgHandler handler, String name) {
  43 + public LwM2mTransportCoapResource(OtaPackageDataCache otaPackageDataCache, String name) {
45 44 super(name);
46   - this.handler = handler;
  45 + this.otaPackageDataCache = otaPackageDataCache;
47 46 this.setObservable(true); // enable observing
48 47 this.addObserver(new CoapResourceObserver());
49 48 }
... ... @@ -72,8 +71,8 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
72 71 protected void processHandleGet(CoapExchange exchange) {
73 72 log.warn("90) processHandleGet [{}]", exchange);
74 73 if (exchange.getRequestOptions().getUriPath().size() >= 2 &&
75   - (FIRMWARE_UPDATE_COAP_RESOURCE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)) ||
76   - SOFTWARE_UPDATE_COAP_RESOURCE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)))) {
  74 + (FIRMWARE_UPDATE_COAP_RESOURCE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size() - 2)) ||
  75 + SOFTWARE_UPDATE_COAP_RESOURCE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size() - 2)))) {
77 76 this.sendOtaData(exchange);
78 77 }
79 78 }
... ... @@ -132,7 +131,7 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
132 131 }
133 132
134 133 private void sendOtaData(CoapExchange exchange) {
135   - String idStr = exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-1
  134 + String idStr = exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size() - 1
136 135 );
137 136 UUID currentId = UUID.fromString(idStr);
138 137 Response response = new Response(CoAP.ResponseCode.CONTENT);
... ... @@ -142,11 +141,10 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
142 141 response.setPayload(fwData);
143 142 if (exchange.getRequestOptions().getBlock2() != null) {
144 143 int chunkSize = exchange.getRequestOptions().getBlock2().getSzx();
145   - boolean moreFlag = fwData.length > chunkSize;
146   - response.getOptions().setBlock2(chunkSize, moreFlag, 0);
147   - log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, moreFlag);
148   - }
149   - else {
  144 + boolean lastFlag = fwData.length <= chunkSize;
  145 + response.getOptions().setBlock2(chunkSize, lastFlag, 0);
  146 + log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, lastFlag);
  147 + } else {
150 148 log.warn("92) with block1 Send currentId: [{}], length: [{}], ", currentId.toString(), fwData.length);
151 149 }
152 150 exchange.respond(response);
... ... @@ -154,7 +152,7 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
154 152 }
155 153
156 154 private byte[] getOtaData(UUID currentId) {
157   - return ((DefaultLwM2MUplinkMsgHandler) handler).otaPackageDataCache.get(currentId.toString());
  155 + return otaPackageDataCache.get(currentId.toString());
158 156 }
159 157
160 158 }
... ...
... ... @@ -37,6 +37,8 @@ import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
37 37 import org.eclipse.leshan.core.model.InvalidDDFFileException;
38 38 import org.eclipse.leshan.core.model.ObjectModel;
39 39 import org.eclipse.leshan.core.model.ResourceModel;
  40 +import org.eclipse.leshan.core.node.LwM2mPath;
  41 +import org.eclipse.leshan.core.node.LwM2mResource;
40 42 import org.eclipse.leshan.core.node.codec.CodecException;
41 43 import org.eclipse.leshan.core.request.ContentFormat;
42 44 import org.springframework.stereotype.Component;
... ... @@ -49,6 +51,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
49 51 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
50 52 import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor;
51 53 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  54 +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
52 55
53 56 import java.io.ByteArrayInputStream;
54 57 import java.io.IOException;
... ... @@ -58,6 +61,7 @@ import java.util.concurrent.TimeUnit;
58 61 import java.util.concurrent.atomic.AtomicInteger;
59 62
60 63 import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.BOOLEAN_V;
  64 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId;
61 65
62 66 @Slf4j
63 67 @Component
... ... @@ -68,37 +72,15 @@ public class LwM2mTransportServerHelper {
68 72 private final LwM2mTransportContext context;
69 73 private final AtomicInteger atomicTs = new AtomicInteger(0);
70 74
71   -
72 75 public long getTS() {
73 76 return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) * 1000L + (atomicTs.getAndIncrement() % 1000);
74 77 }
75 78
76   - /**
77   - * send to Thingsboard Attribute || Telemetry
78   - *
79   - * @param msg - JsonObject: [{name: value}]
80   - * @return - dummyWriteReplace {\"targetIdVer\":\"/19_1.0/0/0\",\"value\":0082}
81   - */
82   - private <T> TransportServiceCallback<Void> getPubAckCallbackSendAttrTelemetry(final T msg) {
83   - return new TransportServiceCallback<>() {
84   - @Override
85   - public void onSuccess(Void dummy) {
86   - log.trace("Success to publish msg: {}, dummy: {}", msg, dummy);
87   - }
88   -
89   - @Override
90   - public void onError(Throwable e) {
91   - log.trace("[{}] Failed to publish msg: {}", msg, e);
92   - }
93   - };
94   - }
95   -
96 79 public void sendParametersOnThingsboardAttribute(List<TransportProtos.KeyValueProto> result, SessionInfoProto sessionInfo) {
97 80 PostAttributeMsg.Builder request = PostAttributeMsg.newBuilder();
98 81 request.addAllKv(result);
99 82 PostAttributeMsg postAttributeMsg = request.build();
100   - TransportServiceCallback call = this.getPubAckCallbackSendAttrTelemetry(postAttributeMsg);
101   - context.getTransportService().process(sessionInfo, postAttributeMsg, this.getPubAckCallbackSendAttrTelemetry(call));
  83 + context.getTransportService().process(sessionInfo, postAttributeMsg, TransportServiceCallback.EMPTY);
102 84 }
103 85
104 86 public void sendParametersOnThingsboardTelemetry(List<TransportProtos.KeyValueProto> result, SessionInfoProto sessionInfo) {
... ... @@ -108,8 +90,7 @@ public class LwM2mTransportServerHelper {
108 90 builder.addAllKv(result);
109 91 request.addTsKvList(builder.build());
110 92 PostTelemetryMsg postTelemetryMsg = request.build();
111   - TransportServiceCallback call = this.getPubAckCallbackSendAttrTelemetry(postTelemetryMsg);
112   - context.getTransportService().process(sessionInfo, postTelemetryMsg, this.getPubAckCallbackSendAttrTelemetry(call));
  93 + context.getTransportService().process(sessionInfo, postTelemetryMsg, TransportServiceCallback.EMPTY);
113 94 }
114 95
115 96 /**
... ... @@ -226,4 +207,5 @@ public class LwM2mTransportServerHelper {
226 207 }
227 208 return null;
228 209 }
  210 +
229 211 }
... ...
... ... @@ -30,6 +30,7 @@ import org.eclipse.leshan.core.node.LwM2mNode;
30 30 import org.eclipse.leshan.core.node.LwM2mObject;
31 31 import org.eclipse.leshan.core.node.LwM2mObjectInstance;
32 32 import org.eclipse.leshan.core.node.LwM2mPath;
  33 +import org.eclipse.leshan.core.node.LwM2mResource;
33 34 import org.eclipse.leshan.core.node.LwM2mSingleResource;
34 35 import org.eclipse.leshan.core.node.codec.CodecException;
35 36 import org.eclipse.leshan.core.request.SimpleDownlinkRequest;
... ... @@ -42,12 +43,13 @@ import org.thingsboard.server.common.data.DeviceTransportType;
42 43 import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration;
43 44 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
44 45 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
45   -import org.thingsboard.server.common.data.ota.OtaPackageKey;
46   -import org.thingsboard.server.common.data.ota.OtaPackageType;
47   -import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus;
48   -import org.thingsboard.server.common.data.ota.OtaPackageUtil;
49 46 import org.thingsboard.server.common.transport.TransportServiceCallback;
50 47 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  48 +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
  49 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult;
  50 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateState;
  51 +import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult;
  52 +import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState;
51 53 import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;
52 54
53 55 import java.util.ArrayList;
... ... @@ -75,407 +77,20 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING;
75 77 import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME;
76 78 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
77 79 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
78   -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
79   -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING;
80   -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED;
81   -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED;
82   -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
83   -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED;
  80 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_RESULT_ID;
  81 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_STATE_ID;
  82 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_RESULT_ID;
  83 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_UPDATE_STATE_ID;
84 84
85 85 @Slf4j
86 86 public class LwM2mTransportUtil {
87 87
88   - public static final String EVENT_AWAKE = "AWAKE";
89   - public static final String RESPONSE_REQUEST_CHANNEL = "RESP_REQ";
90   - public static final String RESPONSE_CHANNEL = "RESP";
91   - public static final String OBSERVE_CHANNEL = "OBSERVE";
92   -
93   - public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0";
94   - public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";
95   - public static final String BOOTSTRAP = "bootstrap";
96   - public static final String SERVERS = "servers";
97   - public static final String LWM2M_SERVER = "lwm2mServer";
98   - public static final String BOOTSTRAP_SERVER = "bootstrapServer";
99   - public static final String OBSERVE_ATTRIBUTE_TELEMETRY = "observeAttr";
100   - public static final String ATTRIBUTE = "attribute";
101   - public static final String TELEMETRY = "telemetry";
102   - public static final String KEY_NAME = "keyName";
103   - public static final String OBSERVE_LWM2M = "observe";
104   - public static final String ATTRIBUTE_LWM2M = "attributeLwm2m";
105   -
106   - private static final String REQUEST = "/request";
107   - private static final String ATTRIBUTES = "/" + ATTRIBUTE;
108   - public static final String TELEMETRIES = "/" + TELEMETRY;
109   - public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST;
110   - public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/";
111   -
112   - public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
  88 + public static final String LWM2M_VERSION_DEFAULT = "1.0";
113 89
114 90 public static final String LOG_LWM2M_TELEMETRY = "logLwm2m";
115 91 public static final String LOG_LWM2M_INFO = "info";
116 92 public static final String LOG_LWM2M_ERROR = "error";
117 93 public static final String LOG_LWM2M_WARN = "warn";
118   - public static final String LOG_LWM2M_VALUE = "value";
119   -
120   - public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized";
121   - public static final String LWM2M_VERSION_DEFAULT = "1.0";
122   -
123   - // RPC
124   - public static final String TYPE_OPER_KEY = "typeOper";
125   - public static final String TARGET_ID_VER_KEY = "targetIdVer";
126   - public static final String KEY_NAME_KEY = "key";
127   - public static final String VALUE_KEY = "value";
128   - public static final String PARAMS_KEY = "params";
129   - public static final String SEPARATOR_KEY = ":";
130   - public static final String FINISH_VALUE_KEY = ",";
131   - public static final String START_JSON_KEY = "{";
132   - public static final String FINISH_JSON_KEY = "}";
133   - public static final String INFO_KEY = "info";
134   - public static final String RESULT_KEY = "result";
135   - public static final String ERROR_KEY = "error";
136   - public static final String METHOD_KEY = "methodName";
137   -
138   -
139   - // Firmware
140   - public static final String FIRMWARE_UPDATE_COAP_RESOURCE = "firmwareUpdateCoapResource";
141   - public static final String FW_UPDATE = "Firmware update";
142   - public static final Integer FW_5_ID = 5;
143   - public static final Integer FW_19_ID = 19;
144   -
145   - // Package W
146   - public static final String FW_PACKAGE_5_ID = "/5/0/0";
147   - public static final String FW_PACKAGE_19_ID = "/19/0/0";
148   - // Package URI
149   - public static final String FW_PACKAGE_URI_ID = "/5/0/1";
150   - // State R
151   - public static final String FW_STATE_ID = "/5/0/3";
152   - // Update Result R
153   - public static final String FW_RESULT_ID = "/5/0/5";
154   - // PkgName R
155   - public static final String FW_NAME_ID = "/5/0/6";
156   - // PkgVersion R
157   - public static final String FW_5_VER_ID = "/5/0/7";
158   - /**
159   - * Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8
160   - * BC68JAR01A10
161   - * # Request prodct type number
162   - * ATI
163   - * Quectel
164   - * BC68
165   - * Revision:BC68JAR01A10
166   - */
167   - public static final String FW_3_VER_ID = "/3/0/3";
168   - // Update E
169   - public static final String FW_UPDATE_ID = "/5/0/2";
170   -
171   - // Software
172   - public static final String SOFTWARE_UPDATE_COAP_RESOURCE = "softwareUpdateCoapResource";
173   - public static final String SW_UPDATE = "Software update";
174   - public static final Integer SW_ID = 9;
175   - // Package W
176   - public static final String SW_PACKAGE_ID = "/9/0/2";
177   - // Package URI
178   - public static final String SW_PACKAGE_URI_ID = "/9/0/3";
179   - // Update State R
180   - public static final String SW_UPDATE_STATE_ID = "/9/0/7";
181   - // Update Result R
182   - public static final String SW_RESULT_ID = "/9/0/9";
183   - // PkgName R
184   - public static final String SW_NAME_ID = "/9/0/0";
185   - // PkgVersion R
186   - public static final String SW_VER_ID = "/9/0/1";
187   - // Install E
188   - public static final String SW_INSTALL_ID = "/9/0/4";
189   - // Uninstall E
190   - public static final String SW_UN_INSTALL_ID = "/9/0/6";
191   -
192   - public enum LwM2mTypeServer {
193   - BOOTSTRAP(0, "bootstrap"),
194   - CLIENT(1, "client");
195   -
196   - public int code;
197   - public String type;
198   -
199   - LwM2mTypeServer(int code, String type) {
200   - this.code = code;
201   - this.type = type;
202   - }
203   -
204   - public static LwM2mTypeServer fromLwM2mTypeServer(String type) {
205   - for (LwM2mTypeServer sm : LwM2mTypeServer.values()) {
206   - if (sm.type.equals(type)) {
207   - return sm;
208   - }
209   - }
210   - throw new IllegalArgumentException(String.format("Unsupported typeServer type : %d", type));
211   - }
212   - }
213   -
214   - /**
215   - * /** State R
216   - * 0: Idle (before downloading or after successful updating)
217   - * 1: Downloading (The data sequence is on the way)
218   - * 2: Downloaded
219   - * 3: Updating
220   - */
221   - public enum StateFw {
222   - IDLE(0, "Idle"),
223   - DOWNLOADING(1, "Downloading"),
224   - DOWNLOADED(2, "Downloaded"),
225   - UPDATING(3, "Updating");
226   -
227   - public int code;
228   - public String type;
229   -
230   - StateFw(int code, String type) {
231   - this.code = code;
232   - this.type = type;
233   - }
234   -
235   - public static StateFw fromStateFwByType(String type) {
236   - for (StateFw to : StateFw.values()) {
237   - if (to.type.equals(type)) {
238   - return to;
239   - }
240   - }
241   - throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type));
242   - }
243   -
244   - public static StateFw fromStateFwByCode(int code) {
245   - for (StateFw to : StateFw.values()) {
246   - if (to.code == code) {
247   - return to;
248   - }
249   - }
250   - throw new IllegalArgumentException(String.format("Unsupported FW State code : %s", code));
251   - }
252   - }
253   -
254   - /**
255   - * FW Update Result
256   - * 0: Initial value. Once the updating process is initiated (Download /Update), this Resource MUST be reset to Initial value.
257   - * 1: Firmware updated successfully.
258   - * 2: Not enough flash memory for the new firmware package.
259   - * 3: Out of RAM during downloading process.
260   - * 4: Connection lost during downloading process.
261   - * 5: Integrity check failure for new downloaded package.
262   - * 6: Unsupported package type.
263   - * 7: Invalid URI.
264   - * 8: Firmware update failed.
265   - * 9: Unsupported protocol.
266   - */
267   - public enum UpdateResultFw {
268   - INITIAL(0, "Initial value", false),
269   - UPDATE_SUCCESSFULLY(1, "Firmware updated successfully", false),
270   - NOT_ENOUGH(2, "Not enough flash memory for the new firmware package", false),
271   - OUT_OFF_MEMORY(3, "Out of RAM during downloading process", false),
272   - CONNECTION_LOST(4, "Connection lost during downloading process", true),
273   - INTEGRITY_CHECK_FAILURE(5, "Integrity check failure for new downloaded package", true),
274   - UNSUPPORTED_TYPE(6, "Unsupported package type", false),
275   - INVALID_URI(7, "Invalid URI", false),
276   - UPDATE_FAILED(8, "Firmware update failed", false),
277   - UNSUPPORTED_PROTOCOL(9, "Unsupported protocol", false);
278   -
279   - public int code;
280   - public String type;
281   - public boolean isAgain;
282   -
283   - UpdateResultFw(int code, String type, boolean isAgain) {
284   - this.code = code;
285   - this.type = type;
286   - this.isAgain = isAgain;
287   - }
288   -
289   - public static UpdateResultFw fromUpdateResultFwByType(String type) {
290   - for (UpdateResultFw to : UpdateResultFw.values()) {
291   - if (to.type.equals(type)) {
292   - return to;
293   - }
294   - }
295   - throw new IllegalArgumentException(String.format("Unsupported FW Update Result type : %s", type));
296   - }
297   -
298   - public static UpdateResultFw fromUpdateResultFwByCode(int code) {
299   - for (UpdateResultFw to : UpdateResultFw.values()) {
300   - if (to.code == code) {
301   - return to;
302   - }
303   - }
304   - throw new IllegalArgumentException(String.format("Unsupported FW Update Result code : %s", code));
305   - }
306   - }
307   -
308   - /**
309   - * FirmwareUpdateStatus {
310   - * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
311   - */
312   - public static OtaPackageUpdateStatus equalsFwSateFwResultToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) {
313   - switch (updateResultFw) {
314   - case INITIAL:
315   - return equalsFwSateToFirmwareUpdateStatus(stateFw);
316   - case UPDATE_SUCCESSFULLY:
317   - return UPDATED;
318   - case NOT_ENOUGH:
319   - case OUT_OFF_MEMORY:
320   - case CONNECTION_LOST:
321   - case INTEGRITY_CHECK_FAILURE:
322   - case UNSUPPORTED_TYPE:
323   - case INVALID_URI:
324   - case UPDATE_FAILED:
325   - case UNSUPPORTED_PROTOCOL:
326   - return FAILED;
327   - default:
328   - throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", stateFw.name(), updateResultFw.name());
329   - }
330   - }
331   -
332   - public static OtaPackageUpdateStatus equalsFwResultToFirmwareUpdateStatus(UpdateResultFw updateResultFw) {
333   - switch (updateResultFw) {
334   - case INITIAL:
335   - return VERIFIED;
336   - case UPDATE_SUCCESSFULLY:
337   - return UPDATED;
338   - case NOT_ENOUGH:
339   - case OUT_OFF_MEMORY:
340   - case CONNECTION_LOST:
341   - case INTEGRITY_CHECK_FAILURE:
342   - case UNSUPPORTED_TYPE:
343   - case INVALID_URI:
344   - case UPDATE_FAILED:
345   - case UNSUPPORTED_PROTOCOL:
346   - return FAILED;
347   - default:
348   - throw new CodecException("Invalid value stateFw %s for FirmwareUpdateStatus.", updateResultFw.name());
349   - }
350   - }
351   -
352   - public static OtaPackageUpdateStatus equalsFwSateToFirmwareUpdateStatus(StateFw stateFw) {
353   - switch (stateFw) {
354   - case IDLE:
355   - return VERIFIED;
356   - case DOWNLOADING:
357   - return DOWNLOADING;
358   - case DOWNLOADED:
359   - return DOWNLOADED;
360   - case UPDATING:
361   - return UPDATING;
362   - default:
363   - throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", stateFw);
364   - }
365   - }
366   -
367   - /**
368   - * SW Update State R
369   - * 0: INITIAL Before downloading. (see 5.1.2.1)
370   - * 1: DOWNLOAD STARTED The downloading process has started and is on-going. (see 5.1.2.2)
371   - * 2: DOWNLOADED The package has been completely downloaded (see 5.1.2.3)
372   - * 3: DELIVERED In that state, the package has been correctly downloaded and is ready to be installed. (see 5.1.2.4)
373   - * If executing the Install Resource failed, the state remains at DELIVERED.
374   - * If executing the Install Resource was successful, the state changes from DELIVERED to INSTALLED.
375   - * After executing the UnInstall Resource, the state changes to INITIAL.
376   - * 4: INSTALLED
377   - */
378   - public enum UpdateStateSw {
379   - INITIAL(0, "Initial"),
380   - DOWNLOAD_STARTED(1, "DownloadStarted"),
381   - DOWNLOADED(2, "Downloaded"),
382   - DELIVERED(3, "Delivered"),
383   - INSTALLED(4, "Installed");
384   -
385   - public int code;
386   - public String type;
387   -
388   - UpdateStateSw(int code, String type) {
389   - this.code = code;
390   - this.type = type;
391   - }
392   -
393   - public static UpdateStateSw fromUpdateStateSwByType(String type) {
394   - for (UpdateStateSw to : UpdateStateSw.values()) {
395   - if (to.type.equals(type)) {
396   - return to;
397   - }
398   - }
399   - throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", type));
400   - }
401   -
402   - public static UpdateStateSw fromUpdateStateSwByCode(int code) {
403   - for (UpdateStateSw to : UpdateStateSw.values()) {
404   - if (to.code == code) {
405   - return to;
406   - }
407   - }
408   - throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", code));
409   - }
410   - }
411   -
412   - /**
413   - * SW Update Result
414   - * Contains the result of downloading or installing/uninstalling the software
415   - * 0: Initial value.
416   - * - Prior to download any new package in the Device, Update Result MUST be reset to this initial value.
417   - * - One side effect of executing the Uninstall resource is to reset Update Result to this initial value "0".
418   - * 1: Downloading.
419   - * - The package downloading process is on-going.
420   - * 2: Software successfully installed.
421   - * 3: Successfully Downloaded and package integrity verified
422   - * (( 4-49, for expansion, of other scenarios))
423   - * ** Failed
424   - * 50: Not enough storage for the new software package.
425   - * 51: Out of memory during downloading process.
426   - * 52: Connection lost during downloading process.
427   - * 53: Package integrity check failure.
428   - * 54: Unsupported package type.
429   - * 56: Invalid URI
430   - * 57: Device defined update error
431   - * 58: Software installation failure
432   - * 59: Uninstallation Failure during forUpdate(arg=0)
433   - * 60-200 : (for expansion, selection to be in blocks depending on new introduction of features)
434   - * This Resource MAY be reported by sending Observe operation.
435   - */
436   - public enum UpdateResultSw {
437   - INITIAL(0, "Initial value", false),
438   - DOWNLOADING(1, "Downloading", false),
439   - SUCCESSFULLY_INSTALLED(2, "Software successfully installed", false),
440   - SUCCESSFULLY_DOWNLOADED_VERIFIED(3, "Successfully Downloaded and package integrity verified", false),
441   - NOT_ENOUGH_STORAGE(50, "Not enough storage for the new software package", true),
442   - OUT_OFF_MEMORY(51, "Out of memory during downloading process", true),
443   - CONNECTION_LOST(52, "Connection lost during downloading process", false),
444   - PACKAGE_CHECK_FAILURE(53, "Package integrity check failure.", false),
445   - UNSUPPORTED_PACKAGE_TYPE(54, "Unsupported package type", false),
446   - INVALID_URI(56, "Invalid URI", true),
447   - UPDATE_ERROR(57, "Device defined update error", true),
448   - INSTALL_FAILURE(58, "Software installation failure", true),
449   - UN_INSTALL_FAILURE(59, "Uninstallation Failure during forUpdate(arg=0)", true);
450   -
451   - public int code;
452   - public String type;
453   - public boolean isAgain;
454   -
455   - UpdateResultSw(int code, String type, boolean isAgain) {
456   - this.code = code;
457   - this.type = type;
458   - this.isAgain = isAgain;
459   - }
460   -
461   - public static UpdateResultSw fromUpdateResultSwByType(String type) {
462   - for (UpdateResultSw to : UpdateResultSw.values()) {
463   - if (to.type.equals(type)) {
464   - return to;
465   - }
466   - }
467   - throw new IllegalArgumentException(String.format("Unsupported SW Update Result type : %s", type));
468   - }
469   -
470   - public static UpdateResultSw fromUpdateResultSwByCode(int code) {
471   - for (UpdateResultSw to : UpdateResultSw.values()) {
472   - if (to.code == code) {
473   - return to;
474   - }
475   - }
476   - throw new IllegalArgumentException(String.format("Unsupported SW Update Result code : %s", code));
477   - }
478   - }
479 94
480 95 public enum LwM2MClientStrategy {
481 96 CLIENT_STRATEGY_1(1, "Read only resources marked as observation"),
... ... @@ -508,108 +123,6 @@ public class LwM2mTransportUtil {
508 123 }
509 124 }
510 125
511   - public enum LwM2MFirmwareUpdateStrategy {
512   - OBJ_5_BINARY(1, "ObjectId 5, Binary"),
513   - OBJ_5_TEMP_URL(2, "ObjectId 5, URI"),
514   - OBJ_19_BINARY(3, "ObjectId 19, Binary");
515   -
516   - public int code;
517   - public String type;
518   -
519   - LwM2MFirmwareUpdateStrategy(int code, String type) {
520   - this.code = code;
521   - this.type = type;
522   - }
523   -
524   - public static LwM2MFirmwareUpdateStrategy fromStrategyFwByType(String type) {
525   - for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) {
526   - if (to.type.equals(type)) {
527   - return to;
528   - }
529   - }
530   - throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type));
531   - }
532   -
533   - public static LwM2MFirmwareUpdateStrategy fromStrategyFwByCode(int code) {
534   - for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) {
535   - if (to.code == code) {
536   - return to;
537   - }
538   - }
539   - throw new IllegalArgumentException(String.format("Unsupported FW Strategy code : %s", code));
540   - }
541   - }
542   -
543   - public enum LwM2MSoftwareUpdateStrategy {
544   - BINARY(1, "ObjectId 9, Binary"),
545   - TEMP_URL(2, "ObjectId 9, URI");
546   -
547   - public int code;
548   - public String type;
549   -
550   - LwM2MSoftwareUpdateStrategy(int code, String type) {
551   - this.code = code;
552   - this.type = type;
553   - }
554   -
555   - public static LwM2MSoftwareUpdateStrategy fromStrategySwByType(String type) {
556   - for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) {
557   - if (to.type.equals(type)) {
558   - return to;
559   - }
560   - }
561   - throw new IllegalArgumentException(String.format("Unsupported SW Strategy type : %s", type));
562   - }
563   -
564   - public static LwM2MSoftwareUpdateStrategy fromStrategySwByCode(int code) {
565   - for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) {
566   - if (to.code == code) {
567   - return to;
568   - }
569   - }
570   - throw new IllegalArgumentException(String.format("Unsupported SW Strategy code : %s", code));
571   - }
572   -
573   - }
574   -
575   - /**
576   - * FirmwareUpdateStatus {
577   - * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
578   - */
579   - public static OtaPackageUpdateStatus EqualsSwSateToFirmwareUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) {
580   - switch (updateResultSw) {
581   - case INITIAL:
582   - switch (updateStateSw) {
583   - case INITIAL:
584   - case DOWNLOAD_STARTED:
585   - return DOWNLOADING;
586   - case DOWNLOADED:
587   - return DOWNLOADED;
588   - case DELIVERED:
589   - return VERIFIED;
590   - }
591   - case DOWNLOADING:
592   - return DOWNLOADING;
593   - case SUCCESSFULLY_INSTALLED:
594   - return UPDATED;
595   - case SUCCESSFULLY_DOWNLOADED_VERIFIED:
596   - return VERIFIED;
597   - case NOT_ENOUGH_STORAGE:
598   - case OUT_OFF_MEMORY:
599   - case CONNECTION_LOST:
600   - case PACKAGE_CHECK_FAILURE:
601   - case UNSUPPORTED_PACKAGE_TYPE:
602   - case INVALID_URI:
603   - case UPDATE_ERROR:
604   - case INSTALL_FAILURE:
605   - case UN_INSTALL_FAILURE:
606   - return FAILED;
607   - default:
608   - throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", updateStateSw.name(), updateResultSw.name());
609   - }
610   - }
611   -
612   -
613 126 public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath
614 127 resourcePath) throws CodecException {
615 128 switch (type) {
... ... @@ -635,19 +148,19 @@ public class LwM2mTransportUtil {
635 148 if (path != null) {
636 149 if (FW_STATE_ID.equals(path)) {
637 150 lwM2mOtaConvert.setCurrentType(STRING);
638   - lwM2mOtaConvert.setValue(StateFw.fromStateFwByCode(((Long) value).intValue()).type);
  151 + lwM2mOtaConvert.setValue(FirmwareUpdateState.fromStateFwByCode(((Long) value).intValue()).type);
639 152 return lwM2mOtaConvert;
640 153 } else if (FW_RESULT_ID.equals(path)) {
641 154 lwM2mOtaConvert.setCurrentType(STRING);
642   - lwM2mOtaConvert.setValue(UpdateResultFw.fromUpdateResultFwByCode(((Long) value).intValue()).type);
  155 + lwM2mOtaConvert.setValue(FirmwareUpdateResult.fromUpdateResultFwByCode(((Long) value).intValue()).getType());
643 156 return lwM2mOtaConvert;
644 157 } else if (SW_UPDATE_STATE_ID.equals(path)) {
645 158 lwM2mOtaConvert.setCurrentType(STRING);
646   - lwM2mOtaConvert.setValue(UpdateStateSw.fromUpdateStateSwByCode(((Long) value).intValue()).type);
  159 + lwM2mOtaConvert.setValue(SoftwareUpdateState.fromUpdateStateSwByCode(((Long) value).intValue()).type);
647 160 return lwM2mOtaConvert;
648 161 } else if (SW_RESULT_ID.equals(path)) {
649 162 lwM2mOtaConvert.setCurrentType(STRING);
650   - lwM2mOtaConvert.setValue(UpdateResultSw.fromUpdateResultSwByCode(((Long) value).intValue()).type);
  163 + lwM2mOtaConvert.setValue(SoftwareUpdateResult.fromUpdateResultSwByCode(((Long) value).intValue()).type);
651 164 return lwM2mOtaConvert;
652 165 }
653 166 }
... ... @@ -669,18 +182,6 @@ public class LwM2mTransportUtil {
669 182 return null;
670 183 }
671 184
672   -// public static LwM2mClientProfile getNewProfileParameters(JsonObject profilesConfigData, TenantId tenantId) {
673   -// LwM2mClientProfile lwM2MClientProfile = new LwM2mClientProfile();
674   -// lwM2MClientProfile.setTenantId(tenantId);
675   -// lwM2MClientProfile.setPostClientLwM2mSettings(profilesConfigData.get(CLIENT_LWM2M_SETTINGS).getAsJsonObject());
676   -// lwM2MClientProfile.setPostKeyNameProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).getAsJsonObject());
677   -// lwM2MClientProfile.setPostAttributeProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).getAsJsonArray());
678   -// lwM2MClientProfile.setPostTelemetryProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).getAsJsonArray());
679   -// lwM2MClientProfile.setPostObserveProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE_LWM2M).getAsJsonArray());
680   -// lwM2MClientProfile.setPostAttributeLwm2mProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).getAsJsonObject());
681   -// return lwM2MClientProfile;
682   -// }
683   -
684 185 public static Lwm2mDeviceProfileTransportConfiguration toLwM2MClientProfile(DeviceProfile deviceProfile) {
685 186 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
686 187 if (transportConfiguration.getType().equals(DeviceTransportType.LWM2M)) {
... ... @@ -753,7 +254,7 @@ public class LwM2mTransportUtil {
753 254
754 255 public static String fromVersionedIdToObjectId(String pathIdVer) {
755 256 try {
756   - if(pathIdVer == null) {
  257 + if (pathIdVer == null) {
757 258 return null;
758 259 }
759 260 String[] keyArray = pathIdVer.split(LWM2M_SEPARATOR_PATH);
... ... @@ -795,13 +296,12 @@ public class LwM2mTransportUtil {
795 296 if (keyArray.length > 1 && keyArray[1].split(LWM2M_SEPARATOR_KEY).length == 2) {
796 297 return pathIdVer;
797 298 } else {
798   - LwM2mPath pathObjId = new LwM2mPath(pathIdVer);
799   - return convertPathFromObjectIdToIdVer(pathIdVer, registration);
  299 + return convertObjectIdToVersionedId(pathIdVer, registration);
800 300 }
801 301 }
802 302 }
803 303
804   - public static String convertPathFromObjectIdToIdVer(String path, Registration registration) {
  304 + public static String convertObjectIdToVersionedId(String path, Registration registration) {
805 305 String ver = registration.getSupportedObject().get(new LwM2mPath(path).getObjectId());
806 306 ver = ver != null ? ver : LWM2M_VERSION_DEFAULT;
807 307 try {
... ... @@ -935,16 +435,19 @@ public class LwM2mTransportUtil {
935 435 }
936 436 }
937 437
938   - public static boolean isFwSwWords(String pathName) {
939   - return OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName)
940   - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName)
941   - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM).equals(pathName)
942   - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName)
943   - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.SIZE).equals(pathName)
944   - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName)
945   - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName)
946   - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM).equals(pathName)
947   - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName)
948   - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.SIZE).equals(pathName);
  438 + /**
  439 + * @param lwM2MClient -
  440 + * @param path -
  441 + * @return - return value of Resource by idPath
  442 + */
  443 + public static LwM2mResource getResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) {
  444 + LwM2mResource lwm2mResourceValue = null;
  445 + ResourceValue resourceValue = lwM2MClient.getResources().get(path);
  446 + if (resourceValue != null) {
  447 + if (new LwM2mPath(fromVersionedIdToObjectId(path)).isResource()) {
  448 + lwm2mResourceValue = lwM2MClient.getResources().get(path).getLwM2mResource();
  449 + }
  450 + }
  451 + return lwm2mResourceValue;
949 452 }
950 453 }
... ...
... ... @@ -20,6 +20,8 @@ import com.google.common.util.concurrent.SettableFuture;
20 20 import lombok.RequiredArgsConstructor;
21 21 import lombok.extern.slf4j.Slf4j;
22 22 import org.eclipse.leshan.core.model.ResourceModel;
  23 +import org.eclipse.leshan.core.node.LwM2mPath;
  24 +import org.eclipse.leshan.core.node.LwM2mResource;
23 25 import org.springframework.stereotype.Service;
24 26 import org.thingsboard.server.common.data.ota.OtaPackageKey;
25 27 import org.thingsboard.server.common.data.ota.OtaPackageType;
... ... @@ -29,17 +31,30 @@ import org.thingsboard.server.common.transport.TransportServiceCallback;
29 31 import org.thingsboard.server.gen.transport.TransportProtos;
30 32 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
31 33 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
  34 +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
  35 +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
  36 +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
32 37 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  38 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
  39 +import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler;
  40 +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest;
  41 +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback;
  42 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
  43 +import org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService;
  44 +import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService;
  45 +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
  46 +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
33 47
  48 +import java.util.ArrayList;
34 49 import java.util.Collection;
35   -import java.util.Collections;
36 50 import java.util.List;
37 51 import java.util.Map;
  52 +import java.util.Optional;
38 53 import java.util.concurrent.atomic.AtomicInteger;
39 54
40 55 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto;
41 56 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR;
42   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.isFwSwWords;
  57 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId;
43 58
44 59 @Slf4j
45 60 @Service
... ... @@ -52,6 +67,13 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
52 67 private final Map<Integer, SettableFuture<List<TransportProtos.TsKvProto>>> futures;
53 68
54 69 private final TransportService transportService;
  70 + private final LwM2mTransportServerHelper helper;
  71 + private final LwM2mClientContext clientContext;
  72 + private final LwM2MTransportServerConfig config;
  73 + private final LwM2mUplinkMsgHandler uplinkHandler;
  74 + private final LwM2mDownlinkMsgHandler downlinkHandler;
  75 + private final LwM2MTelemetryLogService logService;
  76 + private final LwM2MOtaUpdateService otaUpdateService;
55 77
56 78 @Override
57 79 public ListenableFuture<List<TransportProtos.TsKvProto>> getSharedAttributes(LwM2mClient client, Collection<String> keys) {
... ... @@ -96,55 +118,106 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
96 118 * @param msg -
97 119 */
98 120 @Override
99   - public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
100   -// LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo);
101   -// if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) {
102   -// log.warn("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList());
103   -// msg.getSharedUpdatedList().forEach(tsKvProto -> {
104   -// String pathName = tsKvProto.getKv().getKey();
105   -// String pathIdVer = this.getObjectIdByKeyNameFromProfile(sessionInfo, pathName);
106   -// Object valueNew = getValueFromKvProto(tsKvProto.getKv());
107   -// if ((OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName)
108   -// && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())))
109   -// || (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName)
110   -// && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentTitle())))) {
111   -// this.getInfoFirmwareUpdate(lwM2MClient, null);
112   -// } else if ((OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName)
113   -// && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentVersion())))
114   -// || (OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName)
115   -// && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentTitle())))) {
116   -// this.getInfoSoftwareUpdate(lwM2MClient, null);
117   -// }
118   -// if (pathIdVer != null) {
119   -// ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config
120   -// .getModelProvider());
121   -// if (resourceModel != null && resourceModel.operations.isWritable()) {
122   -// this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), valueNew, pathIdVer);
123   -// } else {
124   -// log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew);
125   -// String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
126   -// LOG_LWM2M_ERROR, pathIdVer, valueNew);
127   -// this.logToTelemetry(lwM2MClient, logMsg);
128   -// }
129   -// } else if (!isFwSwWords(pathName)) {
130   -// log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew);
131   -// String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
132   -// LOG_LWM2M_ERROR, pathName, valueNew);
133   -// this.logToTelemetry(lwM2MClient, logMsg);
134   -// }
135   -//
136   -// });
137   -// } else if (msg.getSharedDeletedCount() > 0 && lwM2MClient != null) {
138   -// msg.getSharedUpdatedList().forEach(tsKvProto -> {
139   -// String pathName = tsKvProto.getKv().getKey();
140   -// Object valueNew = getValueFromKvProto(tsKvProto.getKv());
141   -// if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) {
142   -// lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew);
143   -// }
144   -// });
145   -// log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
146   -// } else if (lwM2MClient == null) {
147   -// log.error("OnAttributeUpdate, lwM2MClient is null");
148   -// }
  121 + public void onAttributesUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
  122 + LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo);
  123 + if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) {
  124 + String newFirmwareTitle = null;
  125 + String newFirmwareVersion = null;
  126 + String newFirmwareUrl = null;
  127 + String newSoftwareTitle = null;
  128 + String newSoftwareVersion = null;
  129 + List<TransportProtos.TsKvProto> otherAttributes = new ArrayList<>();
  130 + for (TransportProtos.TsKvProto tsKvProto : msg.getSharedUpdatedList()) {
  131 + String attrName = tsKvProto.getKv().getKey();
  132 + if (DefaultLwM2MOtaUpdateService.FIRMWARE_TITLE.equals(attrName)) {
  133 + newFirmwareTitle = getStrValue(tsKvProto);
  134 + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_VERSION.equals(attrName)) {
  135 + newFirmwareVersion = getStrValue(tsKvProto);
  136 + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_URL.equals(attrName)) {
  137 + newFirmwareUrl = getStrValue(tsKvProto);
  138 + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TITLE.equals(attrName)) {
  139 + newSoftwareTitle = getStrValue(tsKvProto);
  140 + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_VERSION.equals(attrName)) {
  141 + newSoftwareVersion = getStrValue(tsKvProto);
  142 + } else {
  143 + otherAttributes.add(tsKvProto);
  144 + }
  145 + }
  146 + if (newFirmwareTitle != null || newFirmwareVersion != null) {
  147 + otaUpdateService.onTargetFirmwareUpdate(lwM2MClient, newFirmwareTitle, newFirmwareVersion, Optional.ofNullable(newFirmwareUrl));
  148 + }
  149 + if (newSoftwareTitle != null || newSoftwareVersion != null) {
  150 + otaUpdateService.onTargetSoftwareUpdate(lwM2MClient, newSoftwareTitle, newSoftwareVersion);
  151 + }
  152 + if (!otherAttributes.isEmpty()) {
  153 + onAttributesUpdate(lwM2MClient, otherAttributes);
  154 + }
  155 + } else if (lwM2MClient == null) {
  156 + log.error("OnAttributeUpdate, lwM2MClient is null");
  157 + }
  158 + }
  159 +
  160 + /**
  161 + * #1.1 If two names have equal path => last time attribute
  162 + * #2.1 if there is a difference in values between the current resource values and the shared attribute values
  163 + * => send to client Request Update of value (new value from shared attribute)
  164 + * and LwM2MClient.delayedRequests.add(path)
  165 + * #2.1 if there is not a difference in values between the current resource values and the shared attribute values
  166 + *
  167 + */
  168 + @Override
  169 + public void onAttributesUpdate(LwM2mClient lwM2MClient, List<TransportProtos.TsKvProto> tsKvProtos) {
  170 + log.trace("[{}] onAttributesUpdate [{}]", lwM2MClient.getEndpoint(), tsKvProtos);
  171 + tsKvProtos.forEach(tsKvProto -> {
  172 + String pathIdVer = clientContext.getObjectIdByKeyNameFromProfile(lwM2MClient, tsKvProto.getKv().getKey());
  173 + if (pathIdVer != null) {
  174 + // #1.1
  175 + if (lwM2MClient.getSharedAttributes().containsKey(pathIdVer)) {
  176 + if (tsKvProto.getTs() > lwM2MClient.getSharedAttributes().get(pathIdVer).getTs()) {
  177 + lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto);
  178 + }
  179 + } else {
  180 + lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto);
  181 + }
  182 + }
  183 + });
  184 + // #2.1
  185 + lwM2MClient.getSharedAttributes().forEach((pathIdVer, tsKvProto) -> {
  186 + this.pushUpdateToClientIfNeeded(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer),
  187 + getValueFromKvProto(tsKvProto.getKv()), pathIdVer);
  188 + });
  189 + }
  190 +
  191 + private void pushUpdateToClientIfNeeded(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) {
  192 + if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) {
  193 + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(this.config.getTimeout()).build();
  194 + downlinkHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, lwM2MClient, versionedId));
  195 + } else {
  196 + log.error("Failed update resource [{}] [{}]", versionedId, newValue);
  197 + String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad",
  198 + LOG_LWM2M_ERROR, versionedId, newValue);
  199 + logService.log(lwM2MClient, logMsg);
  200 + log.info("Failed update resource [{}] [{}]", versionedId, newValue);
  201 + }
  202 + }
  203 +
  204 + /**
  205 + * @param pathIdVer - path resource
  206 + * @return - value of Resource into format KvProto or null
  207 + */
  208 + private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) {
  209 + LwM2mResource resourceValue = LwM2mTransportUtil.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer);
  210 + if (resourceValue != null) {
  211 + ResourceModel.Type currentType = resourceValue.getType();
  212 + ResourceModel.Type expectedType = helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
  213 + return LwM2mValueConverterImpl.getInstance().convertValue(resourceValue.getValue(), currentType, expectedType,
  214 + new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)));
  215 + } else {
  216 + return null;
  217 + }
  218 + }
  219 +
  220 + private String getStrValue(TransportProtos.TsKvProto tsKvProto) {
  221 + return tsKvProto.getKv().getStringV();
149 222 }
150 223 }
... ...
... ... @@ -28,5 +28,7 @@ public interface LwM2MAttributesService {
28 28
29 29 void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg getAttributesResponse, TransportProtos.SessionInfoProto sessionInfo);
30 30
31   - void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification, TransportProtos.SessionInfoProto sessionInfo);
  31 + void onAttributesUpdate(TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification, TransportProtos.SessionInfoProto sessionInfo);
  32 +
  33 + void onAttributesUpdate(LwM2mClient lwM2MClient, List<TransportProtos.TsKvProto> tsKvProtos);
32 34 }
... ...
... ... @@ -33,16 +33,12 @@ import org.eclipse.leshan.server.security.SecurityInfo;
33 33 import org.thingsboard.server.common.data.Device;
34 34 import org.thingsboard.server.common.data.DeviceProfile;
35 35 import org.thingsboard.server.common.data.id.TenantId;
36   -import org.thingsboard.server.common.data.ota.OtaPackageType;
37 36 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
38 37 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
39 38 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
40 39 import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest;
41   -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;
42   -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
43 40
44 41 import java.util.Collection;
45   -import java.util.List;
46 42 import java.util.Map;
47 43 import java.util.Optional;
48 44 import java.util.Queue;
... ... @@ -50,14 +46,13 @@ import java.util.Set;
50 46 import java.util.UUID;
51 47 import java.util.concurrent.ConcurrentHashMap;
52 48 import java.util.concurrent.ConcurrentLinkedQueue;
53   -import java.util.concurrent.CopyOnWriteArrayList;
54 49 import java.util.concurrent.locks.Lock;
55 50 import java.util.concurrent.locks.ReentrantLock;
56 51 import java.util.stream.Collectors;
57 52
58 53 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
59   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TRANSPORT_DEFAULT_LWM2M_VERSION;
60   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
  54 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_VERSION_DEFAULT;
  55 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId;
61 56 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName;
62 57 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId;
63 58 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId;
... ... @@ -98,12 +93,6 @@ public class LwM2mClient implements Cloneable {
98 93 private UUID profileId;
99 94 @Getter
100 95 @Setter
101   - private volatile LwM2mFwSwUpdate fwUpdate;
102   - @Getter
103   - @Setter
104   - private volatile LwM2mFwSwUpdate swUpdate;
105   - @Getter
106   - @Setter
107 96 private Registration registration;
108 97
109 98 private ValidateDeviceCredentialsResponse credentials;
... ... @@ -202,7 +191,7 @@ public class LwM2mClient implements Cloneable {
202 191 }
203 192
204 193 public Object getResourceValue(String pathRezIdVer, String pathRezId) {
205   - String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
  194 + String pathRez = pathRezIdVer == null ? convertObjectIdToVersionedId(pathRezId, this.registration) : pathRezIdVer;
206 195 if (this.resources.get(pathRez) != null) {
207 196 return this.resources.get(pathRez).getLwM2mResource().getValue();
208 197 }
... ... @@ -210,7 +199,7 @@ public class LwM2mClient implements Cloneable {
210 199 }
211 200
212 201 public Object getResourceNameByRezId(String pathRezIdVer, String pathRezId) {
213   - String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
  202 + String pathRez = pathRezIdVer == null ? convertObjectIdToVersionedId(pathRezId, this.registration) : pathRezIdVer;
214 203 if (this.resources.get(pathRez) != null) {
215 204 return this.resources.get(pathRez).getResourceModel().name;
216 205 }
... ... @@ -316,7 +305,7 @@ public class LwM2mClient implements Cloneable {
316 305 LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(path));
317 306 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
318 307 String verRez = getVerFromPathIdVerOrId(path);
319   - return verRez == null ? TRANSPORT_DEFAULT_LWM2M_VERSION.equals(verSupportedObject) : verRez.equals(verSupportedObject);
  308 + return verRez == null ? LWM2M_VERSION_DEFAULT.equals(verSupportedObject) : verRez.equals(verSupportedObject);
320 309 }
321 310
322 311 /**
... ... @@ -368,21 +357,5 @@ public class LwM2mClient implements Cloneable {
368 357 }
369 358 }
370 359
371   - public LwM2mFwSwUpdate getFwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClientContext clientContext) {
372   - if (this.fwUpdate == null) {
373   - var profile = clientContext.getProfile(this.getProfileId());
374   - this.fwUpdate = new LwM2mFwSwUpdate(handler, this, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy());
375   - }
376   - return this.fwUpdate;
377   - }
378   -
379   - public LwM2mFwSwUpdate getSwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClientContext clientContext) {
380   - if (this.swUpdate == null) {
381   - var profile = clientContext.getProfile(this.getProfileId());
382   - this.swUpdate = new LwM2mFwSwUpdate(handler, this, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy());
383   - }
384   - return this.fwUpdate;
385   - }
386   -
387 360 }
388 361
... ...
... ... @@ -53,7 +53,10 @@ public interface LwM2mClientContext {
53 53
54 54 LwM2mClient getClientByDeviceId(UUID deviceId);
55 55
56   - void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials);
  56 + String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String keyName);
  57 +
  58 + String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName);
57 59
  60 + void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials);
58 61
59 62 }
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client;
17 17
18 18 import lombok.RequiredArgsConstructor;
19 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.leshan.core.model.ResourceModel;
20 21 import org.eclipse.leshan.core.node.LwM2mPath;
21 22 import org.eclipse.leshan.server.registration.Registration;
22 23 import org.springframework.stereotype.Service;
... ... @@ -25,6 +26,7 @@ import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTrans
25 26 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
26 27 import org.thingsboard.server.gen.transport.TransportProtos;
27 28 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
  29 +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
28 30 import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
29 31 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
30 32 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
... ... @@ -40,7 +42,9 @@ import java.util.concurrent.ConcurrentHashMap;
40 42 import java.util.function.Predicate;
41 43
42 44 import static org.eclipse.leshan.core.SecurityMode.NO_SEC;
43   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
  45 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId;
  46 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId;
  47 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey;
44 48
45 49 @Slf4j
46 50 @Service
... ... @@ -49,6 +53,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.c
49 53 public class LwM2mClientContextImpl implements LwM2mClientContext {
50 54
51 55 private final LwM2mTransportContext context;
  56 + private final LwM2MTransportServerConfig config;
52 57 private final TbEditableSecurityStore securityStore;
53 58 private final Map<String, LwM2mClient> lwM2mClientsByEndpoint = new ConcurrentHashMap<>();
54 59 private final Map<String, LwM2mClient> lwM2mClientsByRegistrationId = new ConcurrentHashMap<>();
... ... @@ -160,6 +165,28 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
160 165 return lwM2mClient;
161 166 }
162 167
  168 + /**
  169 + * Get path to resource from profile equal keyName
  170 + *
  171 + * @param sessionInfo -
  172 + * @param keyName -
  173 + * @return -
  174 + */
  175 + @Override
  176 + public String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String keyName) {
  177 + return getObjectIdByKeyNameFromProfile(getClientBySessionInfo(sessionInfo), keyName);
  178 + }
  179 +
  180 + @Override
  181 + public String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName) {
  182 + Lwm2mDeviceProfileTransportConfiguration profile = getProfile(lwM2mClient.getProfileId());
  183 +
  184 + return profile.getObserveAttr().getKeyName().entrySet().stream()
  185 + .filter(e -> e.getValue().equals(keyName) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().orElseThrow(
  186 + () -> new IllegalArgumentException(keyName + " is not configured in the device profile!")
  187 + ).getKey();
  188 + }
  189 +
163 190 public Registration getRegistration(String registrationId) {
164 191 return this.lwM2mClientsByRegistrationId.get(registrationId).getRegistration();
165 192 }
... ... @@ -200,7 +227,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
200 227 Arrays.stream(client.getRegistration().getObjectLinks()).forEach(link -> {
201 228 LwM2mPath pathIds = new LwM2mPath(link.getUrl());
202 229 if (!pathIds.isRoot()) {
203   - clientObjects.add(convertPathFromObjectIdToIdVer(link.getUrl(), client.getRegistration()));
  230 + clientObjects.add(convertObjectIdToVersionedId(link.getUrl(), client.getRegistration()));
204 231 }
205 232 });
206 233 return (clientObjects.size() > 0) ? clientObjects : null;
... ... @@ -211,4 +238,14 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
211 238 return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null);
212 239 }
213 240
  241 + private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) {
  242 + ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config
  243 + .getModelProvider());
  244 + Integer objectId = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)).getObjectId();
  245 + String objectVer = validateObjectVerFromKey(pathIdVer);
  246 + return resourceModel != null && (isWritableNotOptional ?
  247 + objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() :
  248 + objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)));
  249 + }
  250 +
214 251 }
... ...
1   -/**
2   - * Copyright © 2016-2021 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -package org.thingsboard.server.transport.lwm2m.server.client;
17   -
18   -import lombok.Getter;
19   -import lombok.Setter;
20   -import lombok.extern.slf4j.Slf4j;
21   -import org.apache.commons.lang3.StringUtils;
22   -import org.eclipse.leshan.server.registration.Registration;
23   -import org.thingsboard.server.common.data.ota.OtaPackageType;
24   -import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus;
25   -import org.thingsboard.server.gen.transport.TransportProtos;
26   -import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType;
27   -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;
28   -import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler;
29   -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
30   -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
31   -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest;
32   -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback;
33   -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest;
34   -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback;
35   -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback;
36   -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest;
37   -
38   -import java.util.ArrayList;
39   -import java.util.List;
40   -import java.util.UUID;
41   -import java.util.concurrent.CopyOnWriteArrayList;
42   -
43   -import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE;
44   -import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
45   -import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
46   -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
47   -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED;
48   -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED;
49   -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED;
50   -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
51   -import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey;
52   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RESOURCE;
53   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID;
54   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID;
55   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID;
56   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_19_ID;
57   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_5_ID;
58   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_URI_ID;
59   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID;
60   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID;
61   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE;
62   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID;
63   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR;
64   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO;
65   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY;
66   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY;
67   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL;
68   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.EXECUTE;
69   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.WRITE_REPLACE;
70   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID;
71   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_NAME_ID;
72   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID;
73   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_RESULT_ID;
74   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UN_INSTALL_ID;
75   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE;
76   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE_STATE_ID;
77   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_VER_ID;
78   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
79   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsFwSateToFirmwareUpdateStatus;
80   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.splitCamelCaseString;
81   -
82   -@Slf4j
83   -public class LwM2mFwSwUpdate {
84   - // 5/0/6 PkgName
85   - // 9/0/0 PkgName
86   - @Getter
87   - @Setter
88   - private volatile String currentTitle;
89   - // 5/0/7 PkgVersion
90   - // 9/0/1 PkgVersion
91   - @Getter
92   - @Setter
93   - private volatile String currentVersion;
94   - @Getter
95   - @Setter
96   - private volatile UUID currentId;
97   - @Getter
98   - @Setter
99   - private volatile String stateUpdate;
100   - @Getter
101   - private String pathPackageId;
102   - @Getter
103   - private String pathStateId;
104   - @Getter
105   - private String pathResultId;
106   - @Getter
107   - private String pathNameId;
108   - @Getter
109   - private String pathVerId;
110   - @Getter
111   - private String pathInstallId;
112   - @Getter
113   - private String pathUnInstallId;
114   - @Getter
115   - private String wUpdate;
116   - @Getter
117   - @Setter
118   - private volatile boolean infoFwSwUpdate = false;
119   - private final OtaPackageType type;
120   -
121   - private final LwM2mUplinkMsgHandler handler;
122   -
123   - @Getter
124   - LwM2mClient lwM2MClient;
125   - @Getter
126   - private final List<String> pendingInfoRequestsStart;
127   - @Getter
128   - @Setter
129   - private volatile int updateStrategy;
130   -
131   - public LwM2mFwSwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClient lwM2MClient, OtaPackageType type, int updateStrategy) {
132   - this.handler = handler;
133   - this.lwM2MClient = lwM2MClient;
134   - this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>();
135   - this.type = type;
136   - this.stateUpdate = null;
137   - this.updateStrategy = updateStrategy;
138   - this.initPathId();
139   - }
140   -
141   - private void initPathId() {
142   - if (FIRMWARE.equals(this.type)) {
143   - this.pathPackageId = LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ?
144   - FW_PACKAGE_5_ID : LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy ?
145   - FW_PACKAGE_URI_ID : FW_PACKAGE_19_ID;
146   - this.pathStateId = FW_STATE_ID;
147   - this.pathResultId = FW_RESULT_ID;
148   - this.pathNameId = FW_NAME_ID;
149   - this.pathVerId = FW_5_VER_ID;
150   - this.pathInstallId = FW_UPDATE_ID;
151   - this.wUpdate = FW_UPDATE;
152   - } else if (SOFTWARE.equals(this.type)) {
153   - this.pathPackageId = SW_PACKAGE_ID;
154   - this.pathStateId = SW_UPDATE_STATE_ID;
155   - this.pathResultId = SW_RESULT_ID;
156   - this.pathNameId = SW_NAME_ID;
157   - this.pathVerId = SW_VER_ID;
158   - this.pathInstallId = SW_INSTALL_ID;
159   - this.pathUnInstallId = SW_UN_INSTALL_ID;
160   - this.wUpdate = SW_UPDATE;
161   - }
162   - }
163   -
164   - public void initReadValue(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request, String pathIdVer) {
165   - if (pathIdVer != null) {
166   - this.pendingInfoRequestsStart.remove(pathIdVer);
167   - }
168   - if (this.pendingInfoRequestsStart.size() == 0) {
169   - this.infoFwSwUpdate = false;
170   -// if (!FAILED.name().equals(this.stateUpdate)) {
171   - boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart(handler) :
172   - this.conditionalSwUpdateStart(handler);
173   - if (conditionalStart) {
174   - this.writeFwSwWare(handler, request);
175   - }
176   -// }
177   - }
178   - }
179   -
180   - /**
181   - * Send FsSw to Lwm2mClient:
182   - * before operation Write: fw_state = DOWNLOADING
183   - */
184   - public void writeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) {
185   - if (this.currentId != null) {
186   - this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name();
187   - this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LWM2M_INFO, null);
188   - String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration());
189   - String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LWM2M_INFO,
190   - LwM2mOperationType.FW_UPDATE.name(), this.pathPackageId);
191   - handler.logToTelemetry(fwMsg, lwM2MClient.getRegistration().getId());
192   - log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer);
193   - if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy) {
194   - int chunkSize = 0;
195   - int chunk = 0;
196   - byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk);
197   -
198   - TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(firmwareChunk).timeout(handler.config.getTimeout()).build();
199   - request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer));
200   - } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) {
201   - String apiFont = "coap://176.36.143.9:5685";
202   - String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RESOURCE + "/" + this.currentId.toString();
203   - log.warn("89) coapUri: [{}]", uri);
204   - //TODO: user this.rpcRequest???
205   - TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(uri).timeout(handler.config.getTimeout()).build();
206   - request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer));
207   - } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) {
208   -
209   - }
210   - } else {
211   - String msgError = "FirmWareId is null.";
212   - log.warn("6) [{}]", msgError);
213   -// if (this.rpcRequest != null) {
214   -// TODO: refactor.
215   -// handler.sentRpcResponse(this.rpcRequest, CONTENT.name(), msgError, LOG_LW2M_ERROR);
216   -// }
217   - log.error(msgError);
218   - this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LWM2M_ERROR, msgError);
219   - }
220   - }
221   -
222   - public void sendLogs(DefaultLwM2MUplinkMsgHandler handler, String typeOper, String typeInfo, String msgError) {
223   -// this.sendSateOnThingsBoard(handler);
224   - String msg = String.format("%s: %s, %s, pkgVer: %s: pkgName - %s state - %s.",
225   - typeInfo, this.wUpdate, typeOper, this.currentVersion, this.currentTitle, this.stateUpdate);
226   - if (LOG_LWM2M_ERROR.equals(typeInfo)) {
227   - msg = String.format("%s Error: %s", msg, msgError);
228   - }
229   - handler.logToTelemetry(lwM2MClient, msg);
230   - }
231   -
232   -
233   - /**
234   - * After inspection Update Result
235   - * fw_state/sw_state = UPDATING
236   - * send execute
237   - */
238   - public void executeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) {
239   - this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_INFO, null);
240   - //TODO: user this.rpcRequest???
241   - TbLwM2MExecuteRequest downlink = TbLwM2MExecuteRequest.builder().versionedId(pathInstallId).timeout(handler.config.getTimeout()).build();
242   - request.sendExecuteRequest(lwM2MClient, downlink, new TbLwM2MExecuteCallback(handler, lwM2MClient, pathInstallId));
243   - }
244   -
245   - /**
246   - * Firmware start: Check if the version has changed and launch a new update.
247   - * -ObjectId 5, Binary or ObjectId 5, URI
248   - * -- If the result of the update - errors (more than 1) - This means that the previous. the update failed.
249   - * - We launch the update regardless of the state of the firmware and its version.
250   - * -- If the result of the update - errors (more than 1) - This means that the previous. the update failed.
251   - * * ObjectId 5, Binary
252   - * -- If the result of the update is not errors (equal to 1 or 0) and ver in Object 5 is not empty - it means that the previous update has passed.
253   - * Compare current versions by equals.
254   - * * ObjectId 5, URI
255   - * -- If the result of the update is not errors (equal to 1 or 0) and ver in Object 5 is not empty - it means that the previous update has passed.
256   - * Compare current versions by contains.
257   - */
258   - private boolean conditionalFwUpdateStart(DefaultLwM2MUplinkMsgHandler handler) {
259   - Long updateResultFw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
260   - String ver5 = (String) this.lwM2MClient.getResourceValue(null, this.pathVerId);
261   - String pathName = (String) this.lwM2MClient.getResourceValue(null, this.pathNameId);
262   - String ver3 = (String) this.lwM2MClient.getResourceValue(null, FW_3_VER_ID);
263   - // #1/#2
264   - String fwMsg = null;
265   - if ((this.currentVersion != null && (
266   - ver5 != null && ver5.equals(this.currentVersion) ||
267   - ver3 != null && ver3.contains(this.currentVersion)
268   - )) ||
269   - (this.currentTitle != null && pathName != null && this.currentTitle.equals(pathName))) {
270   - fwMsg = String.format("%s: The update was interrupted. The device has the same version: %s.", LOG_LWM2M_ERROR,
271   - this.currentVersion);
272   - } else if (updateResultFw != null && updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) {
273   - fwMsg = String.format("%s: The update was interrupted. The device has the status UpdateResult: error (%d).", LOG_LWM2M_ERROR,
274   - updateResultFw);
275   - }
276   - if (fwMsg != null) {
277   - handler.logToTelemetry(fwMsg, lwM2MClient.getRegistration().getId());
278   - return false;
279   - } else {
280   - return true;
281   - }
282   - }
283   -
284   -
285   - /**
286   - * Before operation Execute inspection Update Result :
287   - * 0 - Initial value
288   - */
289   - public boolean conditionalFwExecuteStart() {
290   - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
291   - return LwM2mTransportUtil.UpdateResultFw.INITIAL.code == updateResult;
292   - }
293   -
294   - /**
295   - * After operation Execute success inspection Update Result :
296   - * 1 - "Firmware updated successfully"
297   - */
298   - public boolean conditionalFwExecuteAfterSuccess() {
299   - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
300   - return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code == updateResult;
301   - }
302   -
303   - /**
304   - * After operation Execute success inspection Update Result :
305   - * > 1 error: "Firmware updated successfully"
306   - */
307   - public boolean conditionalFwExecuteAfterError() {
308   - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
309   - return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code < updateResult;
310   - }
311   -
312   - /**
313   - * Software start
314   - * - If Update Result -errors (equal or more than 50) - This means that the previous. the update failed.
315   - * * - We launch the update regardless of the state of the firmware and its version.
316   - * - If Update Result is not errors (less than 50) and ver is not empty - This means that before. the update has passed.
317   - * - If Update Result is not errors and ver is empty - This means that there was no update yet or before. UnInstall update
318   - * - If Update Result is not errors and ver is not empty - This means that before unInstall update
319   - * * - Check if the version has changed and launch a new update.
320   - */
321   - private boolean conditionalSwUpdateStart(DefaultLwM2MUplinkMsgHandler handler) {
322   - Long updateResultSw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
323   - // #1/#2
324   - return updateResultSw >= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code ||
325   - (
326   - (updateResultSw <= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code
327   - ) &&
328   - (
329   - (this.currentVersion != null && !this.currentVersion.equals(this.lwM2MClient.getResourceValue(null, this.pathVerId))) ||
330   - (this.currentTitle != null && !this.currentTitle.equals(this.lwM2MClient.getResourceValue(null, this.pathNameId)))
331   - )
332   - );
333   - }
334   -
335   - /**
336   - * Before operation Execute inspection Update Result :
337   - * 3 - Successfully Downloaded and package integrity verified
338   - */
339   - public boolean conditionalSwUpdateExecute() {
340   - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
341   - return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_DOWNLOADED_VERIFIED.code == updateResult;
342   - }
343   -
344   - /**
345   - * After finish operation Execute (success):
346   - * -- inspection Update Result:
347   - * ---- FW если Update Result == 1 ("Firmware updated successfully") или SW если Update Result == 2 ("Software successfully installed.")
348   - * -- fw_state/sw_state = UPDATED
349   - * <p>
350   - * After finish operation Execute (error):
351   - * -- inspection updateResult and send to thingsboard info about error
352   - * --- send to telemetry ( key - this is name Update Result in model) (
353   - * -- fw_state/sw_state = FAILED
354   - */
355   - public void finishFwSwUpdate(DefaultLwM2MUplinkMsgHandler handler, boolean success) {
356   - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
357   - String value = FIRMWARE.equals(this.type) ? LwM2mTransportUtil.UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type :
358   - LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type;
359   - String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId));
360   - if (success) {
361   - this.stateUpdate = OtaPackageUpdateStatus.UPDATED.name();
362   - this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_INFO, null);
363   - } else {
364   - this.stateUpdate = OtaPackageUpdateStatus.FAILED.name();
365   - this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_ERROR, value);
366   - }
367   - handler.helper.sendParametersOnThingsboardTelemetry(
368   - handler.helper.getKvStringtoThingsboard(key, value), this.lwM2MClient.getSession());
369   - }
370   -
371   - /**
372   - * After operation Execute success inspection Update Result :
373   - * 2 - "Software successfully installed."
374   - */
375   - public boolean conditionalSwExecuteAfterSuccess() {
376   - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
377   - return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_INSTALLED.code == updateResult;
378   - }
379   -
380   - /**
381   - * After operation Execute success inspection Update Result :
382   - * >= 50 - error "NOT_ENOUGH_STORAGE"
383   - */
384   - public boolean conditionalSwExecuteAfterError() {
385   - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
386   - return LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code <= updateResult;
387   - }
388   -
389   -// private void observeStateUpdate(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) {
390   -// request.sendAllRequest(lwM2MClient,
391   -// convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE,
392   -// null, null, 0, null);
393   -// request.sendAllRequest(lwM2MClient,
394   -// convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE,
395   -// null, null, 0, null);
396   -// }
397   -
398   - public void sendSateOnThingsBoard(DefaultLwM2MUplinkMsgHandler handler) {
399   - if (StringUtils.trimToNull(this.stateUpdate) != null) {
400   - List<TransportProtos.KeyValueProto> result = new ArrayList<>();
401   - TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(getAttributeKey(this.type, STATE));
402   - kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(stateUpdate);
403   - result.add(kvProto.build());
404   - handler.helper.sendParametersOnThingsboardTelemetry(result,
405   - handler.getSessionInfoOrCloseSession(this.lwM2MClient.getRegistration()));
406   - }
407   - }
408   -
409   - public void sendReadObserveInfo(LwM2mDownlinkMsgHandler request) {
410   - this.infoFwSwUpdate = true;
411   - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
412   - this.pathStateId, this.lwM2MClient.getRegistration()));
413   - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
414   - this.pathResultId, this.lwM2MClient.getRegistration()));
415   - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
416   - FW_3_VER_ID, this.lwM2MClient.getRegistration()));
417   - if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ||
418   - LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy ||
419   - SOFTWARE.equals(this.type)) {
420   - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
421   - this.pathVerId, this.lwM2MClient.getRegistration()));
422   - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
423   - this.pathNameId, this.lwM2MClient.getRegistration()));
424   - }
425   - this.pendingInfoRequestsStart.forEach(versionedId -> {
426   - TbLwM2MObserveRequest downlink = TbLwM2MObserveRequest.builder().versionedId(versionedId).build();
427   - request.sendObserveRequest(this.lwM2MClient, downlink, new TbLwM2MObserveCallback(handler, lwM2MClient, versionedId));
428   - });
429   -
430   - }
431   -
432   - /**
433   - * Before operation Execute (FwUpdate) inspection Update Result :
434   - * - after finished operation Write result: success (FwUpdate): fw_state = DOWNLOADED
435   - * - before start operation Execute (FwUpdate) Update Result = 0 - Initial value
436   - * - start Execute (FwUpdate)
437   - * After finished operation Execute (FwUpdate) inspection Update Result :
438   - * - after start operation Execute (FwUpdate): fw_state = UPDATING
439   - * - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully")
440   - * - finished operation Execute (FwUpdate)
441   - */
442   - public void updateStateOta(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request,
443   - Registration registration, String path, int value) {
444   - if (OBJ_5_BINARY.code == this.getUpdateStrategy()) {
445   - if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) {
446   - if (DOWNLOADED.name().equals(this.getStateUpdate())
447   - && this.conditionalFwExecuteStart()) {
448   - this.executeFwSwWare(handler, request);
449   - } else if (UPDATING.name().equals(this.getStateUpdate())
450   - && this.conditionalFwExecuteAfterSuccess()) {
451   - this.finishFwSwUpdate(handler, true);
452   - } else if (UPDATING.name().equals(this.getStateUpdate())
453   - && this.conditionalFwExecuteAfterError()) {
454   - this.finishFwSwUpdate(handler, false);
455   - }
456   - }
457   - } else if (OBJ_5_TEMP_URL.code == this.getUpdateStrategy()) {
458   - if (this.currentId != null && (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) {
459   - String state = equalsFwSateToFirmwareUpdateStatus(LwM2mTransportUtil.StateFw.fromStateFwByCode(value)).name();
460   - if (StringUtils.isNotEmpty(state) && !FAILED.name().equals(this.stateUpdate) && !state.equals(this.stateUpdate)) {
461   - this.stateUpdate = state;
462   - this.sendSateOnThingsBoard(handler);
463   - }
464   - if (value == LwM2mTransportUtil.StateFw.DOWNLOADED.code) {
465   - this.executeFwSwWare(handler, request);
466   - }
467   - handler.firmwareUpdateState.put(lwM2MClient.getEndpoint(), value);
468   - }
469   - if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) {
470   - if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.INITIAL.code) {
471   - this.setStateUpdate(INITIATED.name());
472   - } else if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) {
473   - this.setStateUpdate(UPDATED.name());
474   - } else if (value > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) {
475   - this.setStateUpdate(FAILED.name());
476   - }
477   - this.sendSateOnThingsBoard(handler);
478   - }
479   - } else if (OBJ_19_BINARY.code == this.getUpdateStrategy()) {
480   -
481   - }
482   - }
483   -}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.common;
  17 +
  18 +import org.thingsboard.common.util.ThingsBoardExecutors;
  19 +
  20 +import javax.annotation.PreDestroy;
  21 +import java.util.concurrent.ExecutorService;
  22 +
  23 +public abstract class LwM2MExecutorAwareService {
  24 +
  25 + protected ExecutorService executor;
  26 +
  27 + protected abstract int getExecutorSize();
  28 +
  29 + protected abstract String getExecutorName();
  30 +
  31 + protected void init() {
  32 + this.executor = ThingsBoardExecutors.newWorkStealingPool(getExecutorSize(), getExecutorName());
  33 + }
  34 +
  35 + public void destroy() {
  36 + if (executor != null) {
  37 + executor.shutdownNow();
  38 + }
  39 + }
  40 +
  41 +}
... ...
... ... @@ -17,30 +17,30 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
20   -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
  20 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
21 21
22 22 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN;
23 23
24 24 @Slf4j
25 25 public abstract class AbstractTbLwM2MRequestCallback<R, T> implements DownlinkRequestCallback<R, T> {
26 26
27   - protected final LwM2mUplinkMsgHandler handler;
  27 + protected final LwM2MTelemetryLogService logService;
28 28 protected final LwM2mClient client;
29 29
30   - protected AbstractTbLwM2MRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client) {
31   - this.handler = handler;
  30 + protected AbstractTbLwM2MRequestCallback(LwM2MTelemetryLogService logService, LwM2mClient client) {
  31 + this.logService = logService;
32 32 this.client = client;
33 33 }
34 34
35 35 @Override
36 36 public void onValidationError(String params, String msg) {
37 37 log.trace("[{}] Request [{}] validation failed. Reason: {}", client.getEndpoint(), params, msg);
38   - handler.logToTelemetry(client, String.format("[%s]: Request [%s] validation failed. Reason: %s", LOG_LWM2M_WARN, params, msg));
  38 + logService.log(client, String.format("[%s]: Request [%s] validation failed. Reason: %s", LOG_LWM2M_WARN, params, msg));
39 39 }
40 40
41 41 @Override
42 42 public void onError(String params, Exception e) {
43 43 log.trace("[{}] Request [{}] processing failed", client.getEndpoint(), params, e);
44   - handler.logToTelemetry(client, String.format("[%s]: Request [%s] processing failed. Reason: %s", LOG_LWM2M_WARN, params, e));
  44 + logService.log(client, String.format("[%s]: Request [%s] processing failed. Reason: %s", LOG_LWM2M_WARN, params, e));
45 45 }
46 46 }
... ...
... ... @@ -26,7 +26,6 @@ import org.eclipse.leshan.core.node.LwM2mResource;
26 26 import org.eclipse.leshan.core.node.ObjectLink;
27 27 import org.eclipse.leshan.core.node.codec.CodecException;
28 28 import org.eclipse.leshan.core.observation.Observation;
29   -import org.eclipse.leshan.core.request.CancelObservationRequest;
30 29 import org.eclipse.leshan.core.request.ContentFormat;
31 30 import org.eclipse.leshan.core.request.DeleteRequest;
32 31 import org.eclipse.leshan.core.request.DiscoverRequest;
... ... @@ -45,7 +44,6 @@ import org.eclipse.leshan.core.response.ReadResponse;
45 44 import org.eclipse.leshan.core.response.WriteAttributesResponse;
46 45 import org.eclipse.leshan.core.response.WriteResponse;
47 46 import org.eclipse.leshan.core.util.Hex;
48   -import org.eclipse.leshan.core.util.NamedThreadFactory;
49 47 import org.eclipse.leshan.server.registration.Registration;
50 48 import org.springframework.stereotype.Service;
51 49 import org.thingsboard.common.util.JacksonUtil;
... ... @@ -54,17 +52,18 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
54 52 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
55 53 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
56 54 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  55 +import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService;
  56 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
57 57 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
58 58
59 59 import javax.annotation.PostConstruct;
  60 +import javax.annotation.PreDestroy;
60 61 import java.util.Arrays;
61 62 import java.util.Collection;
62 63 import java.util.Date;
63 64 import java.util.LinkedList;
64 65 import java.util.List;
65 66 import java.util.Set;
66   -import java.util.concurrent.ExecutorService;
67   -import java.util.concurrent.Executors;
68 67 import java.util.function.Function;
69 68 import java.util.function.Predicate;
70 69 import java.util.stream.Collectors;
... ... @@ -74,25 +73,38 @@ import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN;
74 73 import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD;
75 74 import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD;
76 75 import static org.eclipse.leshan.core.attributes.Attribute.STEP;
77   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_REQUEST_CHANNEL;
78 76
79 77 @Slf4j
80 78 @Service
81 79 @TbLwM2mTransportComponent
82 80 @RequiredArgsConstructor
83   -public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler {
84   - private ExecutorService responseRequestExecutor;
  81 +public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService implements LwM2mDownlinkMsgHandler {
85 82
86 83 public LwM2mValueConverterImpl converter;
87 84
88 85 private final LwM2mTransportContext context;
89 86 private final LwM2MTransportServerConfig config;
  87 + private final LwM2MTelemetryLogService logService;
90 88
91 89 @PostConstruct
92 90 public void init() {
  91 + super.init();
93 92 this.converter = LwM2mValueConverterImpl.getInstance();
94   - responseRequestExecutor = Executors.newFixedThreadPool(this.config.getResponsePoolSize(),
95   - new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL)));
  93 + }
  94 +
  95 + @PreDestroy
  96 + public void destroy() {
  97 + super.destroy();
  98 + }
  99 +
  100 + @Override
  101 + protected int getExecutorSize() {
  102 + return config.getDownlinkPoolSize();
  103 + }
  104 +
  105 + @Override
  106 + protected String getExecutorName() {
  107 + return "LwM2M Downlink";
96 108 }
97 109
98 110 @Override
... ... @@ -221,7 +233,8 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler {
221 233 **/
222 234 Collection<LwM2mResource> resources = client.getNewResourceForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter);
223 235 ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider());
224   - WriteRequest downlink = new WriteRequest(WriteRequest.Mode.UPDATE, convertResourceModelTypeToContentFormat(client, resourceModelWrite.type), resultIds.getObjectId(),
  236 + ContentFormat contentFormat = request.getObjectContentFormat() != null ? request.getObjectContentFormat() : convertResourceModelTypeToContentFormat(client, resourceModelWrite.type);
  237 + WriteRequest downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(),
225 238 resultIds.getObjectInstanceId(), resources);
226 239 sendRequest(client, downlink, request.getTimeout(), callback);
227 240 } else if (resultIds.isObjectInstance()) {
... ... @@ -245,19 +258,24 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler {
245 258
246 259 private <R extends SimpleDownlinkRequest<T>, T extends LwM2mResponse> void sendRequest(LwM2mClient client, R request, long timeoutInMs, DownlinkRequestCallback<R, T> callback) {
247 260 Registration registration = client.getRegistration();
248   - context.getServer().send(registration, request, timeoutInMs, response -> {
249   - responseRequestExecutor.submit(() -> {
250   - try {
251   - callback.onSuccess(request, response);
252   - } catch (Exception e) {
253   - log.error("[{}] failed to process successful response [{}] ", registration.getEndpoint(), response, e);
254   - }
  261 + try {
  262 + logService.log(client, String.format("[%s][%s] Sending request: %s to %s", registration.getId(), registration.getSocketAddress(), request.getClass().getSimpleName(), request.getPath()));
  263 + context.getServer().send(registration, request, timeoutInMs, response -> {
  264 + executor.submit(() -> {
  265 + try {
  266 + callback.onSuccess(request, response);
  267 + } catch (Exception e) {
  268 + log.error("[{}] failed to process successful response [{}] ", registration.getEndpoint(), response, e);
  269 + }
  270 + });
  271 + }, e -> {
  272 + executor.submit(() -> {
  273 + callback.onError(JacksonUtil.toString(request), e);
  274 + });
255 275 });
256   - }, e -> {
257   - responseRequestExecutor.submit(() -> {
258   - callback.onError(JacksonUtil.toString(request), e);
259   - });
260   - });
  276 + } catch (Exception e) {
  277 + callback.onError(JacksonUtil.toString(request), e);
  278 + }
261 279 }
262 280
263 281 private WriteRequest getWriteRequestSingleResource(ResourceModel.Type type, ContentFormat contentFormat, int objectId, int instanceId, int resourceId, Object value) {
... ...
... ... @@ -17,21 +17,21 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
20   -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
  20 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
21 21
22 22 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO;
23 23
24 24 @Slf4j
25 25 public class TbLwM2MCancelAllObserveCallback extends AbstractTbLwM2MRequestCallback<TbLwM2MCancelAllRequest, Integer> {
26 26
27   - public TbLwM2MCancelAllObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client) {
28   - super(handler, client);
  27 + public TbLwM2MCancelAllObserveCallback(LwM2MTelemetryLogService logService, LwM2mClient client) {
  28 + super(logService, client);
29 29 }
30 30
31 31 @Override
32 32 public void onSuccess(TbLwM2MCancelAllRequest request, Integer canceledSubscriptionsCount) {
33 33 log.trace("[{}] Cancel of all observations was successful: {}", client.getEndpoint(), canceledSubscriptionsCount);
34   - handler.logToTelemetry(client, String.format("[%s]: Cancel of all observations was successful. Result: [%s]", LOG_LWM2M_INFO, canceledSubscriptionsCount));
  34 + logService.log(client, String.format("[%s]: Cancel of all observations was successful. Result: [%s]", LOG_LWM2M_INFO, canceledSubscriptionsCount));
35 35 }
36 36
37 37 }
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.transport.lwm2m.server.downlink;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
19 20 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
20 21 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
21 22
... ... @@ -27,15 +28,15 @@ public class TbLwM2MCancelObserveCallback extends AbstractTbLwM2MRequestCallback
27 28
28 29 private final String versionedId;
29 30
30   - public TbLwM2MCancelObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String versionedId) {
31   - super(handler, client);
  31 + public TbLwM2MCancelObserveCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String versionedId) {
  32 + super(logService, client);
32 33 this.versionedId = versionedId;
33 34 }
34 35
35 36 @Override
36 37 public void onSuccess(TbLwM2MCancelObserveRequest request, Integer canceledSubscriptionsCount) {
37 38 log.trace("[{}] Cancel observation of [{}] successful: {}", client.getEndpoint(), versionedId, canceledSubscriptionsCount);
38   - handler.logToTelemetry(client, String.format("[%s]: Cancel Observe for [%s] successful. Result: [%s]", LOG_LWM2M_INFO, versionedId, canceledSubscriptionsCount));
  39 + logService.log(client, String.format("[%s]: Cancel Observe for [%s] successful. Result: [%s]", LOG_LWM2M_INFO, versionedId, canceledSubscriptionsCount));
39 40 }
40 41
41 42 }
... ...
... ... @@ -18,12 +18,13 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
18 18 import org.eclipse.leshan.core.request.DeleteRequest;
19 19 import org.eclipse.leshan.core.response.DeleteResponse;
20 20 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  21 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
21 22 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
22 23
23 24 public class TbLwM2MDeleteCallback extends TbLwM2MTargetedCallback<DeleteRequest, DeleteResponse> {
24 25
25   - public TbLwM2MDeleteCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) {
26   - super(handler, client, targetId);
  26 + public TbLwM2MDeleteCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) {
  27 + super(logService, client, targetId);
27 28 }
28 29
29 30 }
... ...
... ... @@ -17,13 +17,14 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
17 17
18 18 import org.eclipse.leshan.core.request.DiscoverRequest;
19 19 import org.eclipse.leshan.core.response.DiscoverResponse;
  20 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
20 21 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
21 22 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
22 23
23 24 public class TbLwM2MDiscoverCallback extends TbLwM2MTargetedCallback<DiscoverRequest, DiscoverResponse> {
24 25
25   - public TbLwM2MDiscoverCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) {
26   - super(handler, client, targetId);
  26 + public TbLwM2MDiscoverCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) {
  27 + super(logService, client, targetId);
27 28 }
28 29
29 30 }
... ...
... ... @@ -17,13 +17,14 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
17 17
18 18 import org.eclipse.leshan.core.request.ExecuteRequest;
19 19 import org.eclipse.leshan.core.response.ExecuteResponse;
  20 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
20 21 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
21 22 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
22 23
23 24 public class TbLwM2MExecuteCallback extends TbLwM2MTargetedCallback<ExecuteRequest, ExecuteResponse> {
24 25
25   - public TbLwM2MExecuteCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) {
26   - super(handler, client, targetId);
  26 + public TbLwM2MExecuteCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) {
  27 + super(logService, client, targetId);
27 28 }
28 29
29 30 }
... ...
... ... @@ -18,14 +18,15 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.eclipse.leshan.core.request.ObserveRequest;
20 20 import org.eclipse.leshan.core.response.ObserveResponse;
  21 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
21 22 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
22 23 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
23 24
24 25 @Slf4j
25   -public class TbLwM2MObserveCallback extends TbLwM2MTargetedCallback<ObserveRequest, ObserveResponse> {
  26 +public class TbLwM2MObserveCallback extends TbLwM2MUplinkTargetedCallback<ObserveRequest, ObserveResponse> {
26 27
27   - public TbLwM2MObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) {
28   - super(handler, client, targetId);
  28 + public TbLwM2MObserveCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) {
  29 + super(handler, logService, client, targetId);
29 30 }
30 31
31 32 @Override
... ...
... ... @@ -19,13 +19,14 @@ import lombok.extern.slf4j.Slf4j;
19 19 import org.eclipse.leshan.core.request.ReadRequest;
20 20 import org.eclipse.leshan.core.response.ReadResponse;
21 21 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  22 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
22 23 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
23 24
24 25 @Slf4j
25   -public class TbLwM2MReadCallback extends TbLwM2MTargetedCallback<ReadRequest, ReadResponse> {
  26 +public class TbLwM2MReadCallback extends TbLwM2MUplinkTargetedCallback<ReadRequest, ReadResponse> {
26 27
27   - public TbLwM2MReadCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) {
28   - super(handler, client, targetId);
  28 + public TbLwM2MReadCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) {
  29 + super(handler, logService, client, targetId);
29 30 }
30 31
31 32 @Override
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  20 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
20 21 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
21 22
22 23 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO;
... ... @@ -26,8 +27,8 @@ public abstract class TbLwM2MTargetedCallback<R, T> extends AbstractTbLwM2MReque
26 27
27 28 protected final String versionedId;
28 29
29   - public TbLwM2MTargetedCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String versionedId) {
30   - super(handler, client);
  30 + public TbLwM2MTargetedCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String versionedId) {
  31 + super(logService, client);
31 32 this.versionedId = versionedId;
32 33 }
33 34
... ... @@ -36,7 +37,7 @@ public abstract class TbLwM2MTargetedCallback<R, T> extends AbstractTbLwM2MReque
36 37 //TODO convert camelCase to "camel case" using .split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])")
37 38 String requestName = request.getClass().getSimpleName();
38 39 log.trace("[{}] {} [{}] successful: {}", client.getEndpoint(), requestName, versionedId, response);
39   - handler.logToTelemetry(client, String.format("[%s]: %s [%s] successful. Result: [%s]", LOG_LWM2M_INFO, requestName, versionedId, response));
  40 + logService.log(client, String.format("[%s]: %s [%s] successful. Result: [%s]", LOG_LWM2M_INFO, requestName, versionedId, response));
40 41 }
41 42
42 43 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.downlink;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  20 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
  21 +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
  22 +
  23 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO;
  24 +
  25 +@Slf4j
  26 +public abstract class TbLwM2MUplinkTargetedCallback<R, T> extends TbLwM2MTargetedCallback<R, T> {
  27 +
  28 + protected LwM2mUplinkMsgHandler handler;
  29 +
  30 + public TbLwM2MUplinkTargetedCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String versionedId) {
  31 + super(logService, client, versionedId);
  32 + this.handler = handler;
  33 + }
  34 +
  35 +}
... ...
... ... @@ -18,12 +18,13 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
18 18 import org.eclipse.leshan.core.request.WriteAttributesRequest;
19 19 import org.eclipse.leshan.core.response.WriteAttributesResponse;
20 20 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  21 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
21 22 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
22 23
23 24 public class TbLwM2MWriteAttributesCallback extends TbLwM2MTargetedCallback<WriteAttributesRequest, WriteAttributesResponse> {
24 25
25   - public TbLwM2MWriteAttributesCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) {
26   - super(handler, client, targetId);
  26 + public TbLwM2MWriteAttributesCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) {
  27 + super(logService, client, targetId);
27 28 }
28 29
29 30 }
... ...
... ... @@ -17,17 +17,21 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
17 17
18 18 import lombok.Builder;
19 19 import lombok.Getter;
  20 +import org.eclipse.leshan.core.request.ContentFormat;
20 21 import org.eclipse.leshan.core.response.WriteResponse;
21 22 import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType;
22 23
23 24 public class TbLwM2MWriteReplaceRequest extends AbstractTbLwM2MTargetedDownlinkRequest<WriteResponse> {
24 25
25 26 @Getter
  27 + private final ContentFormat contentFormat;
  28 + @Getter
26 29 private final Object value;
27 30
28 31 @Builder
29   - private TbLwM2MWriteReplaceRequest(String versionedId, long timeout, Object value) {
  32 + private TbLwM2MWriteReplaceRequest(String versionedId, long timeout, ContentFormat contentFormat, Object value) {
30 33 super(versionedId, timeout);
  34 + this.contentFormat = contentFormat;
31 35 this.value = value;
32 36 }
33 37
... ...
... ... @@ -18,12 +18,13 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
18 18 import org.eclipse.leshan.core.request.WriteRequest;
19 19 import org.eclipse.leshan.core.response.WriteResponse;
20 20 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  21 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
21 22 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
22 23
23   -public class TbLwM2MWriteResponseCallback extends TbLwM2MTargetedCallback<WriteRequest, WriteResponse> {
  24 +public class TbLwM2MWriteResponseCallback extends TbLwM2MUplinkTargetedCallback<WriteRequest, WriteResponse> {
24 25
25   - public TbLwM2MWriteResponseCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) {
26   - super(handler, client, targetId);
  26 + public TbLwM2MWriteResponseCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) {
  27 + super(handler, logService, client, targetId);
27 28 }
28 29
29 30 @Override
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.log;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.stereotype.Service;
  21 +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
  22 +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
  23 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  24 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
  25 +
  26 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY;
  27 +
  28 +@Slf4j
  29 +@Service
  30 +@TbLwM2mTransportComponent
  31 +@RequiredArgsConstructor
  32 +public class DefaultLwM2MTelemetryLogService implements LwM2MTelemetryLogService {
  33 +
  34 + private final LwM2mClientContext clientContext;
  35 + private final LwM2mTransportServerHelper helper;
  36 +
  37 + /**
  38 + * @param logMsg - text msg
  39 + * @param registrationId - Id of Registration LwM2M Client
  40 + */
  41 + @Override
  42 + public void log(String registrationId, String logMsg) {
  43 + log(clientContext.getClientByRegistrationId(registrationId), logMsg);
  44 + }
  45 +
  46 + @Override
  47 + public void log(LwM2mClient client, String logMsg) {
  48 + if (logMsg != null && client != null && client.getSession() != null) {
  49 + if (logMsg.length() > 1024) {
  50 + logMsg = logMsg.substring(0, 1024);
  51 + }
  52 + this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), client.getSession());
  53 + }
  54 + }
  55 +
  56 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.log;
  17 +
  18 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  19 +
  20 +public interface LwM2MTelemetryLogService {
  21 +
  22 + void log(LwM2mClient client, String msg);
  23 +
  24 + void log(String registrationId, String msg);
  25 +
  26 +}
... ...