Commit ea3d273625158946094b1267328d16d5ac1388d5
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 | } | ... | ... |
... | ... | @@ -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 | ... | ... |
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
... | ... | @@ -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 | } | ... | ... |
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java
deleted
100644 → 0
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 | +} | ... | ... |